iOS: chats and messages layout (#241)
* iOS: chats and messages layout * model update for updated API * improve chat list view * chat view layouts * delete contacts * larger headers, clean up, move message reception loop to ContentView * settings: user profile
This commit is contained in:
parent
6d5b5ab44f
commit
53040dbe1d
@ -5,73 +5,25 @@
|
|||||||
// Created by Evgeny Poberezkin on 17/01/2022.
|
// Created by Evgeny Poberezkin on 17/01/2022.
|
||||||
//
|
//
|
||||||
|
|
||||||
//import SwiftUI
|
|
||||||
|
|
||||||
//struct ContentView: View {
|
|
||||||
// @State var messages: [String] = ["Start session:"]
|
|
||||||
// @State var text: String = ""
|
|
||||||
//
|
|
||||||
// func sendMessage() {
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var body: some View {
|
|
||||||
// VStack {
|
|
||||||
// ScrollView {
|
|
||||||
// LazyVStack {
|
|
||||||
// ForEach(messages, id: \.self) { msg in
|
|
||||||
// MessageView(message: msg, sent: false)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .padding(10)
|
|
||||||
// }
|
|
||||||
// .frame(minWidth: 0,
|
|
||||||
// maxWidth: .infinity,
|
|
||||||
// minHeight: 0,
|
|
||||||
// maxHeight: .infinity,
|
|
||||||
// alignment: .topLeading)
|
|
||||||
// HStack {
|
|
||||||
// TextField("Message...", text: $text)
|
|
||||||
// .textFieldStyle(RoundedBorderTextFieldStyle())
|
|
||||||
// .frame(minHeight: CGFloat(30))
|
|
||||||
// Button(action: sendMessage) {
|
|
||||||
// Text("Send")
|
|
||||||
// }.disabled(text.isEmpty)
|
|
||||||
// }
|
|
||||||
// .frame(minHeight: CGFloat(30))
|
|
||||||
// .padding()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
|
|
||||||
// var chatStore: chat_store
|
|
||||||
// private let controller: chat_controller
|
|
||||||
|
|
||||||
// init(chatStore: chat_store) {
|
|
||||||
// self.chatStore = chatStore
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// @State private var logbuffer = [String]()
|
|
||||||
// @State private var chatcmd: String = ""
|
|
||||||
// @State private var chatlog: String = ""
|
|
||||||
// @FocusState private var focused: Bool
|
|
||||||
//
|
|
||||||
// func addLine(line: String) {
|
|
||||||
// print(line)
|
|
||||||
// logbuffer.append(line)
|
|
||||||
// if(logbuffer.count > 50) { _ = logbuffer.dropFirst() }
|
|
||||||
// chatlog = logbuffer.joined(separator: "\n")
|
|
||||||
// }
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if let user = chatModel.currentUser {
|
if let user = chatModel.currentUser {
|
||||||
ChatListView(user: user)
|
ChatListView(user: user)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
DispatchQueue.global().async {
|
||||||
|
while(true) {
|
||||||
|
do {
|
||||||
|
try processReceivedMsg(chatModel, chatRecvMsg())
|
||||||
|
} catch {
|
||||||
|
print("error receiving message: ", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let chats = try apiGetChats()
|
let chats = try apiGetChats()
|
||||||
chatModel.chatPreviews = chats
|
chatModel.chatPreviews = chats
|
||||||
@ -82,29 +34,6 @@ struct ContentView: View {
|
|||||||
} else {
|
} else {
|
||||||
WelcomeView()
|
WelcomeView()
|
||||||
}
|
}
|
||||||
|
|
||||||
// return VStack {
|
|
||||||
// ScrollView {
|
|
||||||
// VStack(alignment: .leading) {
|
|
||||||
// HStack { Spacer() }
|
|
||||||
// Text(chatlog)
|
|
||||||
// .lineLimit(nil)
|
|
||||||
// .font(.system(.body, design: .monospaced))
|
|
||||||
// }
|
|
||||||
// .frame(maxWidth: .infinity)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// TextField("Chat command", text: $chatcmd)
|
|
||||||
// .focused($focused)
|
|
||||||
// .onSubmit {
|
|
||||||
// print(chatcmd)
|
|
||||||
// var cCmd = chatcmd.cString(using: .utf8)!
|
|
||||||
// print(String.init(cString: chat_send_cmd(controller, &cCmd)))
|
|
||||||
// }
|
|
||||||
// .textInputAutocapitalization(.never)
|
|
||||||
// .disableAutocorrection(true)
|
|
||||||
// .padding()
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,21 +8,30 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
final class ChatModel: ObservableObject {
|
final class ChatModel: ObservableObject {
|
||||||
@Published var currentUser: User?
|
@Published var currentUser: User?
|
||||||
@Published var chats: Dictionary<String, Chat> = [:]
|
@Published var chats: Dictionary<String, Chat> = [:]
|
||||||
@Published var chatPreviews: [ChatPreview] = []
|
@Published var chatPreviews: [Chat] = []
|
||||||
@Published var chatItems: [ChatItem] = []
|
@Published var chatItems: [ChatItem] = []
|
||||||
@Published var terminalItems: [TerminalItem] = []
|
@Published var terminalItems: [TerminalItem] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
struct User: Codable {
|
class User: Codable {
|
||||||
var userId: Int64
|
var userId: Int64
|
||||||
var userContactId: Int64
|
var userContactId: Int64
|
||||||
var localDisplayName: ContactName
|
var localDisplayName: ContactName
|
||||||
var profile: Profile
|
var profile: Profile
|
||||||
var activeUser: Bool
|
var activeUser: Bool
|
||||||
|
|
||||||
|
internal init(userId: Int64, userContactId: Int64, localDisplayName: ContactName, profile: Profile, activeUser: Bool) {
|
||||||
|
self.userId = userId
|
||||||
|
self.userContactId = userContactId
|
||||||
|
self.localDisplayName = localDisplayName
|
||||||
|
self.profile = profile
|
||||||
|
self.activeUser = activeUser
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let sampleUser = User(
|
let sampleUser = User(
|
||||||
@ -47,15 +56,6 @@ let sampleProfile = Profile(
|
|||||||
fullName: "Alice"
|
fullName: "Alice"
|
||||||
)
|
)
|
||||||
|
|
||||||
struct ChatPreview: Identifiable, Decodable {
|
|
||||||
var chatInfo: ChatInfo
|
|
||||||
var lastChatItem: ChatItem?
|
|
||||||
|
|
||||||
var id: String {
|
|
||||||
get { chatInfo.id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ChatType: String {
|
enum ChatType: String {
|
||||||
case direct = "@"
|
case direct = "@"
|
||||||
case group = "#"
|
case group = "#"
|
||||||
@ -106,7 +106,7 @@ let sampleDirectChatInfo = ChatInfo.direct(contact: sampleContact)
|
|||||||
|
|
||||||
let sampleGroupChatInfo = ChatInfo.group(groupInfo: sampleGroupInfo)
|
let sampleGroupChatInfo = ChatInfo.group(groupInfo: sampleGroupInfo)
|
||||||
|
|
||||||
class Chat: Decodable {
|
class Chat: Decodable, Identifiable {
|
||||||
var chatInfo: ChatInfo
|
var chatInfo: ChatInfo
|
||||||
var chatItems: [ChatItem]
|
var chatItems: [ChatItem]
|
||||||
|
|
||||||
@ -114,6 +114,8 @@ class Chat: Decodable {
|
|||||||
self.chatInfo = chatInfo
|
self.chatInfo = chatInfo
|
||||||
self.chatItems = chatItems
|
self.chatItems = chatItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var id: String { get { chatInfo.id } }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Contact: Identifiable, Codable {
|
struct Contact: Identifiable, Codable {
|
||||||
@ -172,11 +174,30 @@ struct ChatItem: Identifiable, Decodable {
|
|||||||
var id: Int64 { get { meta.itemId } }
|
var id: Int64 { get { meta.itemId } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func chatItemSample(_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String) -> ChatItem {
|
||||||
|
ChatItem(
|
||||||
|
chatDir: dir,
|
||||||
|
meta: ciMetaSample(id, ts, text),
|
||||||
|
content: .sndMsgContent(msgContent: .text(text))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
enum CIDirection: Decodable {
|
enum CIDirection: Decodable {
|
||||||
case directSnd
|
case directSnd
|
||||||
case directRcv
|
case directRcv
|
||||||
case groupSnd
|
case groupSnd
|
||||||
case groupRcv(GroupMember)
|
case groupRcv(GroupMember)
|
||||||
|
|
||||||
|
var sent: Bool {
|
||||||
|
get {
|
||||||
|
switch self {
|
||||||
|
case .directSnd: return true
|
||||||
|
case .directRcv: return false
|
||||||
|
case .groupSnd: return true
|
||||||
|
case .groupRcv: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CIMeta: Decodable {
|
struct CIMeta: Decodable {
|
||||||
@ -186,6 +207,15 @@ struct CIMeta: Decodable {
|
|||||||
var createdAt: Date
|
var createdAt: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ciMetaSample(_ id: Int64, _ ts: Date, _ text: String) -> CIMeta {
|
||||||
|
CIMeta(
|
||||||
|
itemId: id,
|
||||||
|
itemTs: ts,
|
||||||
|
itemText: text,
|
||||||
|
createdAt: ts
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
enum CIContent: Decodable {
|
enum CIContent: Decodable {
|
||||||
case sndMsgContent(msgContent: MsgContent)
|
case sndMsgContent(msgContent: MsgContent)
|
||||||
case rcvMsgContent(msgContent: MsgContent)
|
case rcvMsgContent(msgContent: MsgContent)
|
||||||
|
@ -20,6 +20,8 @@ enum ChatCommand {
|
|||||||
case apiSendMessage(type: ChatType, id: Int64, msg: MsgContent)
|
case apiSendMessage(type: ChatType, id: Int64, msg: MsgContent)
|
||||||
case addContact
|
case addContact
|
||||||
case connect(connReq: String)
|
case connect(connReq: String)
|
||||||
|
case apiDeleteChat(type: ChatType, id: Int64)
|
||||||
|
case apiUpdateProfile(profile: Profile)
|
||||||
case string(String)
|
case string(String)
|
||||||
|
|
||||||
var cmdString: String {
|
var cmdString: String {
|
||||||
@ -35,6 +37,10 @@ enum ChatCommand {
|
|||||||
return "/c"
|
return "/c"
|
||||||
case let .connect(connReq):
|
case let .connect(connReq):
|
||||||
return "/c \(connReq)"
|
return "/c \(connReq)"
|
||||||
|
case let .apiDeleteChat(type, id):
|
||||||
|
return "/_del \(type.rawValue)\(id)"
|
||||||
|
case let .apiUpdateProfile(profile):
|
||||||
|
return "/p \(profile.displayName) \(profile.fullName)"
|
||||||
case let .string(str):
|
case let .string(str):
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
@ -48,11 +54,14 @@ struct APIResponse: Decodable {
|
|||||||
|
|
||||||
enum ChatResponse: Decodable, Error {
|
enum ChatResponse: Decodable, Error {
|
||||||
case response(type: String, json: String)
|
case response(type: String, json: String)
|
||||||
case apiChats(chats: [ChatPreview])
|
case apiChats(chats: [Chat])
|
||||||
case apiChat(chat: Chat)
|
case apiChat(chat: Chat)
|
||||||
case invitation(connReqInvitation: String)
|
case invitation(connReqInvitation: String)
|
||||||
case sentConfirmation
|
case sentConfirmation
|
||||||
case sentInvitation
|
case sentInvitation
|
||||||
|
case contactDeleted(contact: Contact)
|
||||||
|
case userProfileNoChange
|
||||||
|
case userProfileUpdated(fromProfile: Profile, toProfile: Profile)
|
||||||
// case newSentInvitation
|
// case newSentInvitation
|
||||||
case contactConnected(contact: Contact)
|
case contactConnected(contact: Contact)
|
||||||
case newChatItem(chatItem: AChatItem)
|
case newChatItem(chatItem: AChatItem)
|
||||||
@ -66,6 +75,9 @@ enum ChatResponse: Decodable, Error {
|
|||||||
case .invitation: return "invitation"
|
case .invitation: return "invitation"
|
||||||
case .sentConfirmation: return "sentConfirmation"
|
case .sentConfirmation: return "sentConfirmation"
|
||||||
case .sentInvitation: return "sentInvitation"
|
case .sentInvitation: return "sentInvitation"
|
||||||
|
case .contactDeleted: return "contactDeleted"
|
||||||
|
case .userProfileNoChange: return "userProfileNoChange"
|
||||||
|
case .userProfileUpdated: return "userProfileNoChange"
|
||||||
case .contactConnected: return "contactConnected"
|
case .contactConnected: return "contactConnected"
|
||||||
case .newChatItem: return "newChatItem"
|
case .newChatItem: return "newChatItem"
|
||||||
}
|
}
|
||||||
@ -81,6 +93,9 @@ enum ChatResponse: Decodable, Error {
|
|||||||
case let .invitation(connReqInvitation): return connReqInvitation
|
case let .invitation(connReqInvitation): return connReqInvitation
|
||||||
case .sentConfirmation: return "sentConfirmation: no details"
|
case .sentConfirmation: return "sentConfirmation: no details"
|
||||||
case .sentInvitation: return "sentInvitation: no details"
|
case .sentInvitation: return "sentInvitation: no details"
|
||||||
|
case let .contactDeleted(contact): return String(describing: contact)
|
||||||
|
case .userProfileNoChange: return "userProfileNoChange: no details"
|
||||||
|
case let .userProfileUpdated(_, toProfile): return String(describing: toProfile)
|
||||||
case let .contactConnected(contact): return String(describing: contact)
|
case let .contactConnected(contact): return String(describing: contact)
|
||||||
case let .newChatItem(chatItem): return String(describing: chatItem)
|
case let .newChatItem(chatItem): return String(describing: chatItem)
|
||||||
}
|
}
|
||||||
@ -156,7 +171,7 @@ func chatRecvMsg() throws -> ChatResponse {
|
|||||||
chatResponse(chat_recv_msg(getChatCtrl())!)
|
chatResponse(chat_recv_msg(getChatCtrl())!)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiGetChats() throws -> [ChatPreview] {
|
func apiGetChats() throws -> [Chat] {
|
||||||
let r = try chatSendCmd(.apiGetChats)
|
let r = try chatSendCmd(.apiGetChats)
|
||||||
if case let .apiChats(chats) = r { return chats }
|
if case let .apiChats(chats) = r { return chats }
|
||||||
throw r
|
throw r
|
||||||
@ -189,13 +204,28 @@ func apiConnect(connReq: String) throws {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiDeleteChat(type: ChatType, id: Int64) throws {
|
||||||
|
let r = try chatSendCmd(.apiDeleteChat(type: type, id: id))
|
||||||
|
if case .contactDeleted = r { return }
|
||||||
|
throw r
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiUpdateProfile(profile: Profile) throws -> Profile? {
|
||||||
|
let r = try chatSendCmd(.apiUpdateProfile(profile: profile))
|
||||||
|
switch r {
|
||||||
|
case .userProfileNoChange: return nil
|
||||||
|
case let .userProfileUpdated(_, toProfile): return toProfile
|
||||||
|
default: throw r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func processReceivedMsg(_ chatModel: ChatModel, _ res: ChatResponse) {
|
func processReceivedMsg(_ chatModel: ChatModel, _ res: ChatResponse) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
chatModel.terminalItems.append(.resp(Date.now, res))
|
chatModel.terminalItems.append(.resp(Date.now, res))
|
||||||
switch res {
|
switch res {
|
||||||
case let .contactConnected(contact):
|
case let .contactConnected(contact):
|
||||||
chatModel.chatPreviews.insert(
|
chatModel.chatPreviews.insert(
|
||||||
ChatPreview(chatInfo: .direct(contact: contact)),
|
Chat(chatInfo: .direct(contact: contact), chatItems: []),
|
||||||
at: 0
|
at: 0
|
||||||
)
|
)
|
||||||
case let .newChatItem(aChatItem):
|
case let .newChatItem(aChatItem):
|
||||||
@ -204,7 +234,7 @@ func processReceivedMsg(_ chatModel: ChatModel, _ res: ChatResponse) {
|
|||||||
chatModel.chats[ci.id] = chat
|
chatModel.chats[ci.id] = chat
|
||||||
chat.chatItems.append(aChatItem.chatItem)
|
chat.chatItems.append(aChatItem.chatItem)
|
||||||
default:
|
default:
|
||||||
print("unsupported response: ", res)
|
print("unsupported response: ", res.responseType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,7 +246,6 @@ private struct UserResponse: Decodable {
|
|||||||
|
|
||||||
private func chatResponse(_ cjson: UnsafePointer<CChar>) -> ChatResponse {
|
private func chatResponse(_ cjson: UnsafePointer<CChar>) -> ChatResponse {
|
||||||
let s = String.init(cString: cjson)
|
let s = String.init(cString: cjson)
|
||||||
print("chatResponse", s)
|
|
||||||
let d = s.data(using: .utf8)!
|
let d = s.data(using: .utf8)!
|
||||||
// TODO is there a way to do it without copying the data? e.g:
|
// TODO is there a way to do it without copying the data? e.g:
|
||||||
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
||||||
@ -270,7 +299,6 @@ private func getChatCtrl() -> chat_ctrl {
|
|||||||
|
|
||||||
private func decodeCJSON<T: Decodable>(_ cjson: UnsafePointer<CChar>) -> T? {
|
private func decodeCJSON<T: Decodable>(_ cjson: UnsafePointer<CChar>) -> T? {
|
||||||
let s = String.init(cString: cjson)
|
let s = String.init(cString: cjson)
|
||||||
print("decodeCJSON", s)
|
|
||||||
let d = s.data(using: .utf8)!
|
let d = s.data(using: .utf8)!
|
||||||
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
||||||
// let d = Data.init(bytesNoCopy: p, count: strlen(cjson), deallocator: .free)
|
// let d = Data.init(bytesNoCopy: p, count: strlen(cjson), deallocator: .free)
|
||||||
@ -286,6 +314,5 @@ private func getJSONObject(_ cjson: UnsafePointer<CChar>) -> NSDictionary? {
|
|||||||
private func encodeCJSON<T: Encodable>(_ value: T) -> [CChar] {
|
private func encodeCJSON<T: Encodable>(_ value: T) -> [CChar] {
|
||||||
let data = try! jsonEncoder.encode(value)
|
let data = try! jsonEncoder.encode(value)
|
||||||
let str = String(decoding: data, as: UTF8.self)
|
let str = String(decoding: data, as: UTF8.self)
|
||||||
print("encodeCJSON", str)
|
|
||||||
return str.cString(using: .utf8)!
|
return str.cString(using: .utf8)!
|
||||||
}
|
}
|
||||||
|
@ -10,19 +10,13 @@ import SwiftUI
|
|||||||
|
|
||||||
struct ChatListView: View {
|
struct ChatListView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
|
@State private var chatId: String?
|
||||||
|
@State private var chatsToBeDeleted: IndexSet?
|
||||||
|
@State private var showDeleteAlert = false
|
||||||
|
|
||||||
var user: User
|
var user: User
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
DispatchQueue.global().async {
|
|
||||||
while(true) {
|
|
||||||
do {
|
|
||||||
try processReceivedMsg(chatModel, chatRecvMsg())
|
|
||||||
} catch {
|
|
||||||
print("error receiving message: ", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return VStack {
|
return VStack {
|
||||||
// if chatModel.chats.isEmpty {
|
// if chatModel.chats.isEmpty {
|
||||||
// VStack {
|
// VStack {
|
||||||
@ -31,8 +25,8 @@ struct ChatListView: View {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
ChatHeaderView()
|
ChatHeaderView(chatId: $chatId)
|
||||||
|
|
||||||
NavigationView {
|
NavigationView {
|
||||||
List {
|
List {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
@ -40,35 +34,86 @@ struct ChatListView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Text("Terminal")
|
Text("Terminal")
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach(chatModel.chatPreviews) { chatPreview in
|
ForEach(chatModel.chatPreviews) { chatPreview in
|
||||||
NavigationLink {
|
NavigationLink(
|
||||||
ChatView(chatInfo: chatPreview.chatInfo)
|
tag: chatPreview.chatInfo.id,
|
||||||
.onAppear {
|
selection: $chatId,
|
||||||
do {
|
destination: {
|
||||||
let ci = chatPreview.chatInfo
|
ChatView(chatInfo: chatPreview.chatInfo)
|
||||||
let chat = try apiGetChat(type: ci.chatType, id: ci.apiId)
|
.onAppear {
|
||||||
chatModel.chats[ci.id] = chat
|
do {
|
||||||
} catch {
|
let ci = chatPreview.chatInfo
|
||||||
print("apiGetChatItems", error)
|
let chat = try apiGetChat(type: ci.chatType, id: ci.apiId)
|
||||||
|
chatModel.chats[ci.id] = chat
|
||||||
|
} catch {
|
||||||
|
print("apiGetChatItems", error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}, label: {
|
||||||
} label: {
|
ChatPreviewView(chatPreview: chatPreview)
|
||||||
ChatPreviewView(chatPreview: chatPreview)
|
.alert(isPresented: $showDeleteAlert) {
|
||||||
}
|
deleteChatAlert((chatsToBeDeleted?.first)!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.frame(height: 80)
|
||||||
|
}
|
||||||
|
.onDelete { idx in
|
||||||
|
chatsToBeDeleted = idx
|
||||||
|
showDeleteAlert = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding(0)
|
||||||
|
.offset(x: -8)
|
||||||
|
.listStyle(.plain)
|
||||||
|
.edgesIgnoringSafeArea(.top)
|
||||||
}
|
}
|
||||||
.navigationViewStyle(.stack)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteChatAlert(_ ix: IndexSet.Element) -> Alert {
|
||||||
|
let ci = chatModel.chatPreviews[ix].chatInfo
|
||||||
|
switch ci {
|
||||||
|
case .direct:
|
||||||
|
return Alert(
|
||||||
|
title: Text("Delete contact?"),
|
||||||
|
message: Text("Contact and all messages will be deleted"),
|
||||||
|
primaryButton: .destructive(Text("Delete")) {
|
||||||
|
do {
|
||||||
|
try apiDeleteChat(type: ci.chatType, id: ci.apiId)
|
||||||
|
chatModel.chatPreviews.remove(at: ix)
|
||||||
|
} catch let error {
|
||||||
|
print("Error: \(error)")
|
||||||
|
}
|
||||||
|
chatsToBeDeleted = nil
|
||||||
|
}, secondaryButton: .cancel() {
|
||||||
|
chatsToBeDeleted = nil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case .group:
|
||||||
|
return Alert(
|
||||||
|
title: Text("Delete group"),
|
||||||
|
message: Text("Group deletion is not supported")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//struct ChatListView_Previews: PreviewProvider {
|
struct ChatListView_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
static var previews: some View {
|
||||||
// let chatModel = ChatModel()
|
let chatModel = ChatModel()
|
||||||
// chatModel.chatPreviews = []
|
chatModel.chatPreviews = [
|
||||||
// return ChatListView(user: sampleUser)
|
Chat(
|
||||||
// .environmentObject(chatModel)
|
chatInfo: sampleDirectChatInfo,
|
||||||
// }
|
chatItems: [chatItemSample(1, .directSnd, Date.now, "hello")]
|
||||||
//}
|
),
|
||||||
|
Chat(
|
||||||
|
chatInfo: sampleGroupChatInfo,
|
||||||
|
chatItems: [chatItemSample(1, .directSnd, Date.now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
return ChatListView(user: sampleUser)
|
||||||
|
.environmentObject(chatModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,19 +9,55 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ChatPreviewView: View {
|
struct ChatPreviewView: View {
|
||||||
var chatPreview: ChatPreview
|
var chatPreview: Chat
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Text(chatPreview.chatInfo.localDisplayName)
|
let ci = chatPreview.chatItems.last
|
||||||
|
return VStack(spacing: 4) {
|
||||||
|
HStack(alignment: .top) {
|
||||||
|
Text(chatPreview.chatInfo.localDisplayName)
|
||||||
|
.font(.title3)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.padding(.leading, 8)
|
||||||
|
.padding(.top, 4)
|
||||||
|
.frame(maxHeight: .infinity, alignment: .topLeading)
|
||||||
|
Spacer()
|
||||||
|
if let ci = ci {
|
||||||
|
Text(getDateFormatter().string(from: ci.meta.itemTs))
|
||||||
|
.font(.subheadline)
|
||||||
|
.padding(.trailing, 8)
|
||||||
|
.padding(.top, 4)
|
||||||
|
.frame(minWidth: 60, alignment: .trailing)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let ci = ci {
|
||||||
|
Text(ci.content.text)
|
||||||
|
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 44, maxHeight: 44, alignment: .topLeading)
|
||||||
|
.padding([.leading, .trailing], 8)
|
||||||
|
.padding(.bottom, 4)
|
||||||
|
.padding(.top, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChatPreviewView_Previews: PreviewProvider {
|
struct ChatPreviewView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
Group{
|
Group{
|
||||||
ChatPreviewView(chatPreview: ChatPreview(chatInfo: sampleDirectChatInfo))
|
ChatPreviewView(chatPreview: Chat(
|
||||||
ChatPreviewView(chatPreview: ChatPreview(chatInfo: sampleGroupChatInfo))
|
chatInfo: sampleDirectChatInfo,
|
||||||
|
chatItems: []
|
||||||
|
))
|
||||||
|
ChatPreviewView(chatPreview: Chat(
|
||||||
|
chatInfo: sampleDirectChatInfo,
|
||||||
|
chatItems: [chatItemSample(1, .directSnd, Date.now, "hello")]
|
||||||
|
))
|
||||||
|
ChatPreviewView(chatPreview: Chat(
|
||||||
|
chatInfo: sampleGroupChatInfo,
|
||||||
|
chatItems: [chatItemSample(1, .directSnd, Date.now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")]
|
||||||
|
))
|
||||||
}
|
}
|
||||||
.previewLayout(.fixed(width: 300, height: 70))
|
.previewLayout(.fixed(width: 360, height: 80))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,9 @@ struct ChatView: View {
|
|||||||
if let chat: Chat = chatModel.chats[chatInfo.id] {
|
if let chat: Chat = chatModel.chats[chatInfo.id] {
|
||||||
VStack {
|
VStack {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
LazyVStack {
|
LazyVStack(spacing: 5) {
|
||||||
ForEach(chat.chatItems) { chatItem in
|
ForEach(chat.chatItems) {
|
||||||
Text(chatItem.content.text)
|
ChatItemView(chatItem: $0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,11 +28,13 @@ struct ChatView: View {
|
|||||||
} else {
|
} else {
|
||||||
Text("unexpected: chat not found...")
|
Text("unexpected: chat not found...")
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer(minLength: 0)
|
||||||
|
|
||||||
SendMessageView(sendMessage: sendMessage, inProgress: inProgress)
|
SendMessageView(sendMessage: sendMessage, inProgress: inProgress)
|
||||||
}
|
}
|
||||||
|
.edgesIgnoringSafeArea(.all)
|
||||||
|
.navigationBarHidden(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendMessage(_ msg: String) {
|
func sendMessage(_ msg: String) {
|
||||||
@ -53,7 +55,15 @@ struct ChatView_Previews: PreviewProvider {
|
|||||||
chatModel.chats = [
|
chatModel.chats = [
|
||||||
"@1": Chat(
|
"@1": Chat(
|
||||||
chatInfo: sampleDirectChatInfo,
|
chatInfo: sampleDirectChatInfo,
|
||||||
chatItems: []
|
chatItems: [
|
||||||
|
chatItemSample(1, .directSnd, Date.now, "hello"),
|
||||||
|
chatItemSample(2, .directRcv, Date.now, "hi"),
|
||||||
|
chatItemSample(3, .directRcv, Date.now, "hi there"),
|
||||||
|
chatItemSample(4, .directRcv, Date.now, "hello again"),
|
||||||
|
chatItemSample(5, .directSnd, Date.now, "hi there!!!"),
|
||||||
|
chatItemSample(6, .directSnd, Date.now, "how are you?"),
|
||||||
|
chatItemSample(7, .directSnd, Date.now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
|
||||||
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
return ChatView(chatInfo: sampleDirectChatInfo)
|
return ChatView(chatInfo: sampleDirectChatInfo)
|
||||||
|
@ -9,79 +9,47 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ChatHeaderView: View {
|
struct ChatHeaderView: View {
|
||||||
@State private var showAddChat = false
|
@Binding var chatId: String?
|
||||||
@State private var addContact = false
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
@State private var addContactAlert = false
|
|
||||||
@State private var addContactError: Error?
|
|
||||||
@State private var connReqInvitation: String = ""
|
|
||||||
@State private var connectContact = false
|
|
||||||
@State private var connectAlert = false
|
|
||||||
@State private var connectError: Error?
|
|
||||||
@State private var createGroup = false
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
Button("Edit", action: {})
|
if let cId = chatId {
|
||||||
Spacer()
|
Button { chatId = nil } label: { Image(systemName: "chevron.backward") }
|
||||||
Text("Your chats")
|
Spacer()
|
||||||
Spacer()
|
Text(chatModel.chats[cId]?.chatInfo.localDisplayName ?? "")
|
||||||
Button { showAddChat = true } label: {
|
.font(.title3)
|
||||||
Image(systemName: "square.and.pencil")
|
Spacer()
|
||||||
|
EmptyView()
|
||||||
|
} else {
|
||||||
|
SettingsButton()
|
||||||
|
Spacer()
|
||||||
|
Text("Your chats")
|
||||||
|
.font(.title3)
|
||||||
|
Spacer()
|
||||||
|
NewChatButton()
|
||||||
}
|
}
|
||||||
.confirmationDialog("Start new chat", isPresented: $showAddChat, titleVisibility: .visible) {
|
|
||||||
Button("Add contact") { addContactAction() }
|
|
||||||
Button("Scan QR code") { connectContact = true }
|
|
||||||
Button("Create group") { createGroup = true }
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $addContact, content: {
|
|
||||||
AddContactView(connReqInvitation: connReqInvitation)
|
|
||||||
})
|
|
||||||
.alert(isPresented: $addContactAlert) {
|
|
||||||
connectionError(addContactError)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $connectContact, content: {
|
|
||||||
connectContactSheet()
|
|
||||||
})
|
|
||||||
.alert(isPresented: $connectAlert) {
|
|
||||||
connectionError(connectError)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $createGroup, content: { CreateGroupView() })
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding([.horizontal, .top])
|
||||||
.padding(.top)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addContactAction() {
|
|
||||||
do {
|
|
||||||
connReqInvitation = try apiAddContact()
|
|
||||||
addContact = true
|
|
||||||
} catch {
|
|
||||||
addContactAlert = true
|
|
||||||
addContactError = error
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectContactSheet() -> some View {
|
|
||||||
ConnectContactView(completed: { err in
|
|
||||||
connectContact = false
|
|
||||||
if err != nil {
|
|
||||||
connectAlert = true
|
|
||||||
connectError = err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func connectionError(_ error: Error?) -> Alert {
|
|
||||||
Alert(
|
|
||||||
title: Text("Connection error"),
|
|
||||||
message: Text(error?.localizedDescription ?? "")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ChatHeaderView_Previews: PreviewProvider {
|
struct ChatHeaderView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ChatHeaderView()
|
@State var chatId1: String? = "@1"
|
||||||
|
@State var chatId2: String?
|
||||||
|
let chatModel = ChatModel()
|
||||||
|
chatModel.chats = [
|
||||||
|
"@1": Chat(
|
||||||
|
chatInfo: sampleDirectChatInfo,
|
||||||
|
chatItems: [chatItemSample(1, .directSnd, Date.now, "hello")]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
return Group {
|
||||||
|
ChatHeaderView(chatId: $chatId1)
|
||||||
|
ChatHeaderView(chatId: $chatId2)
|
||||||
|
}
|
||||||
|
.previewLayout(.fixed(width: 300, height: 70))
|
||||||
|
.environmentObject(chatModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
63
apps/ios/Shared/Views/Helpers/ChatItemView.swift
Normal file
63
apps/ios/Shared/Views/Helpers/ChatItemView.swift
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// ChatItemView.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by Evgeny Poberezkin on 30/01/2022.
|
||||||
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
private var dateFormatter: DateFormatter?
|
||||||
|
|
||||||
|
struct ChatItemView: View {
|
||||||
|
var chatItem: ChatItem
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let sent = chatItem.chatDir.sent
|
||||||
|
|
||||||
|
return VStack {
|
||||||
|
Group {
|
||||||
|
Text(chatItem.content.text)
|
||||||
|
.padding(.top, 8)
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
.frame(minWidth: 200, maxWidth: 300, alignment: .leading)
|
||||||
|
.foregroundColor(sent ? .white : .primary)
|
||||||
|
Text(getDateFormatter().string(from: chatItem.meta.itemTs))
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(sent ? .white : .secondary)
|
||||||
|
.padding(.bottom, 8)
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
.frame(minWidth: 200, maxWidth: 300, alignment: .trailing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(sent ? .blue : Color(uiColor: .tertiarySystemGroupedBackground))
|
||||||
|
.cornerRadius(10)
|
||||||
|
.padding(.horizontal)
|
||||||
|
.frame(
|
||||||
|
minWidth: 200,
|
||||||
|
maxWidth: .infinity,
|
||||||
|
minHeight: 0,
|
||||||
|
maxHeight: .infinity,
|
||||||
|
alignment: sent ? .trailing : .leading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDateFormatter() -> DateFormatter {
|
||||||
|
if let df = dateFormatter { return df }
|
||||||
|
let df = DateFormatter()
|
||||||
|
df.dateFormat = "HH:mm"
|
||||||
|
dateFormatter = df
|
||||||
|
return df
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChatItemView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
Group{
|
||||||
|
ChatItemView(chatItem: chatItemSample(1, .directSnd, Date.now, "hello"))
|
||||||
|
ChatItemView(chatItem: chatItemSample(2, .directRcv, Date.now, "hello there too"))
|
||||||
|
}
|
||||||
|
.previewLayout(.fixed(width: 300, height: 70))
|
||||||
|
}
|
||||||
|
}
|
80
apps/ios/Shared/Views/Helpers/NewChat/NewChatButton.swift
Normal file
80
apps/ios/Shared/Views/Helpers/NewChat/NewChatButton.swift
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
//
|
||||||
|
// NewChatButton.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||||
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct NewChatButton: View {
|
||||||
|
@State private var showAddChat = false
|
||||||
|
@State private var addContact = false
|
||||||
|
@State private var addContactAlert = false
|
||||||
|
@State private var addContactError: Error?
|
||||||
|
@State private var connReqInvitation: String = ""
|
||||||
|
@State private var connectContact = false
|
||||||
|
@State private var connectAlert = false
|
||||||
|
@State private var connectError: Error?
|
||||||
|
@State private var createGroup = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button { showAddChat = true } label: {
|
||||||
|
Image(systemName: "square.and.pencil")
|
||||||
|
}
|
||||||
|
.confirmationDialog("Start new chat", isPresented: $showAddChat, titleVisibility: .visible) {
|
||||||
|
Button("Add contact") { addContactAction() }
|
||||||
|
Button("Scan QR code") { connectContact = true }
|
||||||
|
Button("Create group") { createGroup = true }
|
||||||
|
.disabled(true)
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $addContact, content: {
|
||||||
|
AddContactView(connReqInvitation: connReqInvitation)
|
||||||
|
})
|
||||||
|
.alert(isPresented: $addContactAlert) {
|
||||||
|
connectionError(addContactError)
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $connectContact, content: {
|
||||||
|
connectContactSheet()
|
||||||
|
})
|
||||||
|
.alert(isPresented: $connectAlert) {
|
||||||
|
connectionError(connectError)
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $createGroup, content: { CreateGroupView() })
|
||||||
|
}
|
||||||
|
|
||||||
|
func addContactAction() {
|
||||||
|
do {
|
||||||
|
connReqInvitation = try apiAddContact()
|
||||||
|
addContact = true
|
||||||
|
} catch {
|
||||||
|
addContactAlert = true
|
||||||
|
addContactError = error
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectContactSheet() -> some View {
|
||||||
|
ConnectContactView(completed: { err in
|
||||||
|
connectContact = false
|
||||||
|
if err != nil {
|
||||||
|
connectAlert = true
|
||||||
|
connectError = err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectionError(_ error: Error?) -> Alert {
|
||||||
|
Alert(
|
||||||
|
title: Text("Connection error"),
|
||||||
|
message: Text(error?.localizedDescription ?? "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NewChatButton_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
NewChatButton()
|
||||||
|
}
|
||||||
|
}
|
@ -16,11 +16,10 @@ struct SendMessageView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
TextField("Message...", text: $command)
|
TextField("Message...", text: $command)
|
||||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
.textFieldStyle(.roundedBorder)
|
||||||
.textInputAutocapitalization(.never)
|
.textInputAutocapitalization(.never)
|
||||||
.disableAutocorrection(true)
|
.disableAutocorrection(true)
|
||||||
.frame(minHeight: 30)
|
.onSubmit(submit)
|
||||||
.onSubmit(submit)
|
|
||||||
|
|
||||||
if (inProgress) {
|
if (inProgress) {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
@ -31,7 +30,7 @@ struct SendMessageView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(minHeight: 30)
|
.frame(minHeight: 30)
|
||||||
.padding()
|
.padding(12)
|
||||||
}
|
}
|
||||||
|
|
||||||
func submit() {
|
func submit() {
|
||||||
|
21
apps/ios/Shared/Views/Helpers/Settings/ProfileHeader.swift
Normal file
21
apps/ios/Shared/Views/Helpers/Settings/ProfileHeader.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// ProfileHeader.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||||
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ProfileHeader: View {
|
||||||
|
var body: some View {
|
||||||
|
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProfileHeader_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ProfileHeader()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// SettingsButton.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||||
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SettingsButton: View {
|
||||||
|
@State private var showSettings = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button { showSettings = true } label: {
|
||||||
|
Image(systemName: "gearshape")
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showSettings, content: {
|
||||||
|
SettingsView()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SettingsButton_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
SettingsButton()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// SettingsProfile.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||||
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SettingsProfile: View {
|
||||||
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
|
@State private var profile = Profile(displayName: "", fullName: "")
|
||||||
|
@State private var editProfile: Bool = false
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let user: User = chatModel.currentUser!
|
||||||
|
|
||||||
|
return VStack(alignment: .leading) {
|
||||||
|
Text("Your chat profile")
|
||||||
|
.font(.title)
|
||||||
|
.padding(.bottom)
|
||||||
|
Text("Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile.")
|
||||||
|
.padding(.bottom)
|
||||||
|
if editProfile {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
TextField("Display name", text: $profile.displayName)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.padding(.bottom)
|
||||||
|
TextField("Full name (optional)", text: $profile.fullName)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.disableAutocorrection(true)
|
||||||
|
.padding(.bottom)
|
||||||
|
HStack(spacing: 20) {
|
||||||
|
Button("Cancel") { editProfile = false }
|
||||||
|
Button("Save (and notify contacts)") { saveProfile(user) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
|
||||||
|
} else {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
HStack {
|
||||||
|
Text("Display name:")
|
||||||
|
Text(user.profile.displayName)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
HStack {
|
||||||
|
Text("Full name:")
|
||||||
|
Text(user.profile.fullName)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
}
|
||||||
|
.padding(.bottom)
|
||||||
|
Button("Edit") {
|
||||||
|
profile = user.profile
|
||||||
|
editProfile = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveProfile(_ user: User) {
|
||||||
|
do {
|
||||||
|
if let newProfile = try apiUpdateProfile(profile: profile) {
|
||||||
|
user.profile = newProfile
|
||||||
|
profile = newProfile
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
editProfile = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SettingsProfile_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let chatModel = ChatModel()
|
||||||
|
chatModel.currentUser = sampleUser
|
||||||
|
return SettingsProfile()
|
||||||
|
.environmentObject(chatModel)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// SettingsView.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||||
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SettingsView: View {
|
||||||
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
SettingsProfile()
|
||||||
|
UserAddress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SettingsView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let chatModel = ChatModel()
|
||||||
|
chatModel.currentUser = sampleUser
|
||||||
|
return SettingsView()
|
||||||
|
.environmentObject(chatModel)
|
||||||
|
}
|
||||||
|
}
|
21
apps/ios/Shared/Views/Helpers/UserSettings/UserAddress.swift
Normal file
21
apps/ios/Shared/Views/Helpers/UserSettings/UserAddress.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// UserAddress.swift
|
||||||
|
// SimpleX
|
||||||
|
//
|
||||||
|
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||||
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct UserAddress: View {
|
||||||
|
var body: some View {
|
||||||
|
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UserAddress_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
UserAddress()
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
||||||
5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
||||||
|
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||||
|
5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||||
5C1AEB86279F4A6400247F08 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C1AEB7F279F4A6400247F08 /* libffi.a */; };
|
5C1AEB86279F4A6400247F08 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C1AEB7F279F4A6400247F08 /* libffi.a */; };
|
||||||
5C1AEB87279F4A6400247F08 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C1AEB7F279F4A6400247F08 /* libffi.a */; };
|
5C1AEB87279F4A6400247F08 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C1AEB7F279F4A6400247F08 /* libffi.a */; };
|
||||||
5C1AEB88279F4A6400247F08 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C1AEB80279F4A6400247F08 /* libgmp.a */; };
|
5C1AEB88279F4A6400247F08 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C1AEB80279F4A6400247F08 /* libgmp.a */; };
|
||||||
@ -27,6 +29,8 @@
|
|||||||
5C44B6A127A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C44B69E27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a */; };
|
5C44B6A127A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C44B69E27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a */; };
|
||||||
5C44B6A227A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C44B69F27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a */; };
|
5C44B6A227A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C44B69F27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a */; };
|
||||||
5C44B6A327A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C44B69F27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a */; };
|
5C44B6A327A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C44B69F27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a */; };
|
||||||
|
5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; };
|
||||||
|
5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; };
|
||||||
5C764E80279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; };
|
5C764E80279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; };
|
||||||
5C764E81279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; };
|
5C764E81279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; };
|
||||||
5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7B279C71D4000C6508 /* libiconv.tbd */; };
|
5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7B279C71D4000C6508 /* libiconv.tbd */; };
|
||||||
@ -54,6 +58,14 @@
|
|||||||
5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */; };
|
5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */; };
|
||||||
5CA05A4F279752D00002BEB4 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4E279752D00002BEB4 /* MessageView.swift */; };
|
5CA05A4F279752D00002BEB4 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4E279752D00002BEB4 /* MessageView.swift */; };
|
||||||
5CA05A50279752D00002BEB4 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4E279752D00002BEB4 /* MessageView.swift */; };
|
5CA05A50279752D00002BEB4 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4E279752D00002BEB4 /* MessageView.swift */; };
|
||||||
|
5CB924D427A853F100ACCCDD /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D327A853F100ACCCDD /* SettingsButton.swift */; };
|
||||||
|
5CB924D527A853F100ACCCDD /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D327A853F100ACCCDD /* SettingsButton.swift */; };
|
||||||
|
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
|
||||||
|
5CB924D827A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
|
||||||
|
5CB924E127A867BA00ACCCDD /* SettingsProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* SettingsProfile.swift */; };
|
||||||
|
5CB924E227A867BA00ACCCDD /* SettingsProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* SettingsProfile.swift */; };
|
||||||
|
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; };
|
||||||
|
5CB924E527A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; };
|
||||||
5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; };
|
5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; };
|
||||||
5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; };
|
5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; };
|
||||||
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; };
|
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; };
|
||||||
@ -87,6 +99,7 @@
|
|||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
|
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
|
||||||
|
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||||
5C1AEB7F279F4A6400247F08 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
5C1AEB7F279F4A6400247F08 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||||
5C1AEB80279F4A6400247F08 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
5C1AEB80279F4A6400247F08 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||||
5C1AEB81279F4A6400247F08 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
5C1AEB81279F4A6400247F08 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||||
@ -97,6 +110,7 @@
|
|||||||
5C2E261127A30FEA00F70299 /* TerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalView.swift; sourceTree = "<group>"; };
|
5C2E261127A30FEA00F70299 /* TerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalView.swift; sourceTree = "<group>"; };
|
||||||
5C44B69E27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a"; sourceTree = "<group>"; };
|
5C44B69E27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w.a"; sourceTree = "<group>"; };
|
||||||
5C44B69F27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a"; sourceTree = "<group>"; };
|
5C44B69F27A5FF22001C3154 /* libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.0.2-5okwuQXOXC78H7u8DMgS6w-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||||
|
5C6AD81227A834E300348BD7 /* NewChatButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewChatButton.swift; sourceTree = "<group>"; };
|
||||||
5C764E7B279C71D4000C6508 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libiconv.tbd; sourceTree = DEVELOPER_DIR; };
|
5C764E7B279C71D4000C6508 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libiconv.tbd; sourceTree = DEVELOPER_DIR; };
|
||||||
5C764E7C279C71DB000C6508 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
|
5C764E7C279C71DB000C6508 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
|
||||||
5C764E7D279C7275000C6508 /* SimpleX (iOS)-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SimpleX (iOS)-Bridging-Header.h"; sourceTree = "<group>"; };
|
5C764E7D279C7275000C6508 /* SimpleX (iOS)-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SimpleX (iOS)-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
@ -118,6 +132,10 @@
|
|||||||
5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = "<group>"; };
|
5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = "<group>"; };
|
||||||
5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
||||||
5CA05A4E279752D00002BEB4 /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = "<group>"; };
|
5CA05A4E279752D00002BEB4 /* MessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = "<group>"; };
|
||||||
|
5CB924D327A853F100ACCCDD /* SettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsButton.swift; sourceTree = "<group>"; };
|
||||||
|
5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
|
5CB924E027A867BA00ACCCDD /* SettingsProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsProfile.swift; sourceTree = "<group>"; };
|
||||||
|
5CB924E327A8683A00ACCCDD /* UserAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddress.swift; sourceTree = "<group>"; };
|
||||||
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = "<group>"; };
|
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = "<group>"; };
|
||||||
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
||||||
5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHeaderView.swift; sourceTree = "<group>"; };
|
5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHeaderView.swift; sourceTree = "<group>"; };
|
||||||
@ -191,12 +209,10 @@
|
|||||||
children = (
|
children = (
|
||||||
5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */,
|
5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */,
|
||||||
5CA05A4E279752D00002BEB4 /* MessageView.swift */,
|
5CA05A4E279752D00002BEB4 /* MessageView.swift */,
|
||||||
|
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */,
|
||||||
5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */,
|
5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */,
|
||||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */,
|
5CB924DF27A8678B00ACCCDD /* UserSettings */,
|
||||||
5CCD403627A5F9A200368C90 /* ConnectContactView.swift */,
|
5CB924DD27A8622200ACCCDD /* NewChat */,
|
||||||
5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */,
|
|
||||||
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */,
|
|
||||||
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */,
|
|
||||||
);
|
);
|
||||||
path = Helpers;
|
path = Helpers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -297,6 +313,30 @@
|
|||||||
path = "Tests macOS";
|
path = "Tests macOS";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
5CB924DD27A8622200ACCCDD /* NewChat */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5C6AD81227A834E300348BD7 /* NewChatButton.swift */,
|
||||||
|
5CCD403327A5F6DF00368C90 /* AddContactView.swift */,
|
||||||
|
5CCD403627A5F9A200368C90 /* ConnectContactView.swift */,
|
||||||
|
5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */,
|
||||||
|
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */,
|
||||||
|
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */,
|
||||||
|
);
|
||||||
|
path = NewChat;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
5CB924DF27A8678B00ACCCDD /* UserSettings */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5CB924D327A853F100ACCCDD /* SettingsButton.swift */,
|
||||||
|
5CB924D627A8563F00ACCCDD /* SettingsView.swift */,
|
||||||
|
5CB924E327A8683A00ACCCDD /* UserAddress.swift */,
|
||||||
|
5CB924E027A867BA00ACCCDD /* SettingsProfile.swift */,
|
||||||
|
);
|
||||||
|
path = UserSettings;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@ -464,7 +504,11 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */,
|
||||||
|
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */,
|
||||||
|
5CB924E127A867BA00ACCCDD /* SettingsProfile.swift in Sources */,
|
||||||
5C764E80279C7276000C6508 /* dummy.m in Sources */,
|
5C764E80279C7276000C6508 /* dummy.m in Sources */,
|
||||||
|
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */,
|
||||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||||
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */,
|
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */,
|
||||||
5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */,
|
5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */,
|
||||||
@ -483,6 +527,8 @@
|
|||||||
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */,
|
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */,
|
||||||
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */,
|
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */,
|
||||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */,
|
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */,
|
||||||
|
5CB924D427A853F100ACCCDD /* SettingsButton.swift in Sources */,
|
||||||
|
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -490,7 +536,11 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */,
|
||||||
|
5CB924D827A8563F00ACCCDD /* SettingsView.swift in Sources */,
|
||||||
|
5CB924E227A867BA00ACCCDD /* SettingsProfile.swift in Sources */,
|
||||||
5C764E81279C7276000C6508 /* dummy.m in Sources */,
|
5C764E81279C7276000C6508 /* dummy.m in Sources */,
|
||||||
|
5CB924E527A8683A00ACCCDD /* UserAddress.swift in Sources */,
|
||||||
5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||||
5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */,
|
5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */,
|
||||||
5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */,
|
5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */,
|
||||||
@ -509,6 +559,8 @@
|
|||||||
5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */,
|
5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */,
|
||||||
5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */,
|
5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */,
|
||||||
5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */,
|
5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */,
|
||||||
|
5CB924D527A853F100ACCCDD /* SettingsButton.swift in Sources */,
|
||||||
|
5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user