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.
|
||||
//
|
||||
|
||||
//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
|
||||
|
||||
struct ContentView: View {
|
||||
@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 {
|
||||
if let user = chatModel.currentUser {
|
||||
ChatListView(user: user)
|
||||
.onAppear {
|
||||
DispatchQueue.global().async {
|
||||
while(true) {
|
||||
do {
|
||||
try processReceivedMsg(chatModel, chatRecvMsg())
|
||||
} catch {
|
||||
print("error receiving message: ", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let chats = try apiGetChats()
|
||||
chatModel.chatPreviews = chats
|
||||
@ -82,29 +34,6 @@ struct ContentView: View {
|
||||
} else {
|
||||
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 Combine
|
||||
import SwiftUI
|
||||
|
||||
final class ChatModel: ObservableObject {
|
||||
@Published var currentUser: User?
|
||||
@Published var chats: Dictionary<String, Chat> = [:]
|
||||
@Published var chatPreviews: [ChatPreview] = []
|
||||
@Published var chatPreviews: [Chat] = []
|
||||
@Published var chatItems: [ChatItem] = []
|
||||
@Published var terminalItems: [TerminalItem] = []
|
||||
}
|
||||
|
||||
struct User: Codable {
|
||||
class User: Codable {
|
||||
var userId: Int64
|
||||
var userContactId: Int64
|
||||
var localDisplayName: ContactName
|
||||
var profile: Profile
|
||||
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(
|
||||
@ -47,15 +56,6 @@ let sampleProfile = Profile(
|
||||
fullName: "Alice"
|
||||
)
|
||||
|
||||
struct ChatPreview: Identifiable, Decodable {
|
||||
var chatInfo: ChatInfo
|
||||
var lastChatItem: ChatItem?
|
||||
|
||||
var id: String {
|
||||
get { chatInfo.id }
|
||||
}
|
||||
}
|
||||
|
||||
enum ChatType: String {
|
||||
case direct = "@"
|
||||
case group = "#"
|
||||
@ -106,7 +106,7 @@ let sampleDirectChatInfo = ChatInfo.direct(contact: sampleContact)
|
||||
|
||||
let sampleGroupChatInfo = ChatInfo.group(groupInfo: sampleGroupInfo)
|
||||
|
||||
class Chat: Decodable {
|
||||
class Chat: Decodable, Identifiable {
|
||||
var chatInfo: ChatInfo
|
||||
var chatItems: [ChatItem]
|
||||
|
||||
@ -114,6 +114,8 @@ class Chat: Decodable {
|
||||
self.chatInfo = chatInfo
|
||||
self.chatItems = chatItems
|
||||
}
|
||||
|
||||
var id: String { get { chatInfo.id } }
|
||||
}
|
||||
|
||||
struct Contact: Identifiable, Codable {
|
||||
@ -172,11 +174,30 @@ struct ChatItem: Identifiable, Decodable {
|
||||
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 {
|
||||
case directSnd
|
||||
case directRcv
|
||||
case groupSnd
|
||||
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 {
|
||||
@ -186,6 +207,15 @@ struct CIMeta: Decodable {
|
||||
var createdAt: Date
|
||||
}
|
||||
|
||||
func ciMetaSample(_ id: Int64, _ ts: Date, _ text: String) -> CIMeta {
|
||||
CIMeta(
|
||||
itemId: id,
|
||||
itemTs: ts,
|
||||
itemText: text,
|
||||
createdAt: ts
|
||||
)
|
||||
}
|
||||
|
||||
enum CIContent: Decodable {
|
||||
case sndMsgContent(msgContent: MsgContent)
|
||||
case rcvMsgContent(msgContent: MsgContent)
|
||||
|
@ -20,6 +20,8 @@ enum ChatCommand {
|
||||
case apiSendMessage(type: ChatType, id: Int64, msg: MsgContent)
|
||||
case addContact
|
||||
case connect(connReq: String)
|
||||
case apiDeleteChat(type: ChatType, id: Int64)
|
||||
case apiUpdateProfile(profile: Profile)
|
||||
case string(String)
|
||||
|
||||
var cmdString: String {
|
||||
@ -35,6 +37,10 @@ enum ChatCommand {
|
||||
return "/c"
|
||||
case let .connect(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):
|
||||
return str
|
||||
}
|
||||
@ -48,11 +54,14 @@ struct APIResponse: Decodable {
|
||||
|
||||
enum ChatResponse: Decodable, Error {
|
||||
case response(type: String, json: String)
|
||||
case apiChats(chats: [ChatPreview])
|
||||
case apiChats(chats: [Chat])
|
||||
case apiChat(chat: Chat)
|
||||
case invitation(connReqInvitation: String)
|
||||
case sentConfirmation
|
||||
case sentInvitation
|
||||
case contactDeleted(contact: Contact)
|
||||
case userProfileNoChange
|
||||
case userProfileUpdated(fromProfile: Profile, toProfile: Profile)
|
||||
// case newSentInvitation
|
||||
case contactConnected(contact: Contact)
|
||||
case newChatItem(chatItem: AChatItem)
|
||||
@ -66,6 +75,9 @@ enum ChatResponse: Decodable, Error {
|
||||
case .invitation: return "invitation"
|
||||
case .sentConfirmation: return "sentConfirmation"
|
||||
case .sentInvitation: return "sentInvitation"
|
||||
case .contactDeleted: return "contactDeleted"
|
||||
case .userProfileNoChange: return "userProfileNoChange"
|
||||
case .userProfileUpdated: return "userProfileNoChange"
|
||||
case .contactConnected: return "contactConnected"
|
||||
case .newChatItem: return "newChatItem"
|
||||
}
|
||||
@ -81,6 +93,9 @@ enum ChatResponse: Decodable, Error {
|
||||
case let .invitation(connReqInvitation): return connReqInvitation
|
||||
case .sentConfirmation: return "sentConfirmation: 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 .newChatItem(chatItem): return String(describing: chatItem)
|
||||
}
|
||||
@ -156,7 +171,7 @@ func chatRecvMsg() throws -> ChatResponse {
|
||||
chatResponse(chat_recv_msg(getChatCtrl())!)
|
||||
}
|
||||
|
||||
func apiGetChats() throws -> [ChatPreview] {
|
||||
func apiGetChats() throws -> [Chat] {
|
||||
let r = try chatSendCmd(.apiGetChats)
|
||||
if case let .apiChats(chats) = r { return chats }
|
||||
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) {
|
||||
DispatchQueue.main.async {
|
||||
chatModel.terminalItems.append(.resp(Date.now, res))
|
||||
switch res {
|
||||
case let .contactConnected(contact):
|
||||
chatModel.chatPreviews.insert(
|
||||
ChatPreview(chatInfo: .direct(contact: contact)),
|
||||
Chat(chatInfo: .direct(contact: contact), chatItems: []),
|
||||
at: 0
|
||||
)
|
||||
case let .newChatItem(aChatItem):
|
||||
@ -204,7 +234,7 @@ func processReceivedMsg(_ chatModel: ChatModel, _ res: ChatResponse) {
|
||||
chatModel.chats[ci.id] = chat
|
||||
chat.chatItems.append(aChatItem.chatItem)
|
||||
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 {
|
||||
let s = String.init(cString: cjson)
|
||||
print("chatResponse", s)
|
||||
let d = s.data(using: .utf8)!
|
||||
// TODO is there a way to do it without copying the data? e.g:
|
||||
// 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? {
|
||||
let s = String.init(cString: cjson)
|
||||
print("decodeCJSON", s)
|
||||
let d = s.data(using: .utf8)!
|
||||
// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson))
|
||||
// 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] {
|
||||
let data = try! jsonEncoder.encode(value)
|
||||
let str = String(decoding: data, as: UTF8.self)
|
||||
print("encodeCJSON", str)
|
||||
return str.cString(using: .utf8)!
|
||||
}
|
||||
|
@ -10,19 +10,13 @@ import SwiftUI
|
||||
|
||||
struct ChatListView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@State private var chatId: String?
|
||||
@State private var chatsToBeDeleted: IndexSet?
|
||||
@State private var showDeleteAlert = false
|
||||
|
||||
var user: User
|
||||
|
||||
var body: some View {
|
||||
DispatchQueue.global().async {
|
||||
while(true) {
|
||||
do {
|
||||
try processReceivedMsg(chatModel, chatRecvMsg())
|
||||
} catch {
|
||||
print("error receiving message: ", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VStack {
|
||||
// if chatModel.chats.isEmpty {
|
||||
// VStack {
|
||||
@ -31,8 +25,8 @@ struct ChatListView: View {
|
||||
// }
|
||||
// }
|
||||
|
||||
ChatHeaderView()
|
||||
|
||||
ChatHeaderView(chatId: $chatId)
|
||||
|
||||
NavigationView {
|
||||
List {
|
||||
NavigationLink {
|
||||
@ -40,35 +34,86 @@ struct ChatListView: View {
|
||||
} label: {
|
||||
Text("Terminal")
|
||||
}
|
||||
|
||||
|
||||
ForEach(chatModel.chatPreviews) { chatPreview in
|
||||
NavigationLink {
|
||||
ChatView(chatInfo: chatPreview.chatInfo)
|
||||
.onAppear {
|
||||
do {
|
||||
let ci = chatPreview.chatInfo
|
||||
let chat = try apiGetChat(type: ci.chatType, id: ci.apiId)
|
||||
chatModel.chats[ci.id] = chat
|
||||
} catch {
|
||||
print("apiGetChatItems", error)
|
||||
NavigationLink(
|
||||
tag: chatPreview.chatInfo.id,
|
||||
selection: $chatId,
|
||||
destination: {
|
||||
ChatView(chatInfo: chatPreview.chatInfo)
|
||||
.onAppear {
|
||||
do {
|
||||
let ci = chatPreview.chatInfo
|
||||
let chat = try apiGetChat(type: ci.chatType, id: ci.apiId)
|
||||
chatModel.chats[ci.id] = chat
|
||||
} catch {
|
||||
print("apiGetChatItems", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
ChatPreviewView(chatPreview: chatPreview)
|
||||
}
|
||||
}, label: {
|
||||
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 {
|
||||
// static var previews: some View {
|
||||
// let chatModel = ChatModel()
|
||||
// chatModel.chatPreviews = []
|
||||
// return ChatListView(user: sampleUser)
|
||||
// .environmentObject(chatModel)
|
||||
// }
|
||||
//}
|
||||
struct ChatListView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let chatModel = ChatModel()
|
||||
chatModel.chatPreviews = [
|
||||
Chat(
|
||||
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
|
||||
|
||||
struct ChatPreviewView: View {
|
||||
var chatPreview: ChatPreview
|
||||
var chatPreview: Chat
|
||||
|
||||
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 {
|
||||
static var previews: some View {
|
||||
Group{
|
||||
ChatPreviewView(chatPreview: ChatPreview(chatInfo: sampleDirectChatInfo))
|
||||
ChatPreviewView(chatPreview: ChatPreview(chatInfo: sampleGroupChatInfo))
|
||||
ChatPreviewView(chatPreview: Chat(
|
||||
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] {
|
||||
VStack {
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
ForEach(chat.chatItems) { chatItem in
|
||||
Text(chatItem.content.text)
|
||||
LazyVStack(spacing: 5) {
|
||||
ForEach(chat.chatItems) {
|
||||
ChatItemView(chatItem: $0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -28,11 +28,13 @@ struct ChatView: View {
|
||||
} else {
|
||||
Text("unexpected: chat not found...")
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Spacer(minLength: 0)
|
||||
|
||||
SendMessageView(sendMessage: sendMessage, inProgress: inProgress)
|
||||
}
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.navigationBarHidden(true)
|
||||
}
|
||||
|
||||
func sendMessage(_ msg: String) {
|
||||
@ -53,7 +55,15 @@ struct ChatView_Previews: PreviewProvider {
|
||||
chatModel.chats = [
|
||||
"@1": Chat(
|
||||
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)
|
||||
|
@ -9,79 +9,47 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ChatHeaderView: 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
|
||||
@Binding var chatId: String?
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Button("Edit", action: {})
|
||||
Spacer()
|
||||
Text("Your chats")
|
||||
Spacer()
|
||||
Button { showAddChat = true } label: {
|
||||
Image(systemName: "square.and.pencil")
|
||||
if let cId = chatId {
|
||||
Button { chatId = nil } label: { Image(systemName: "chevron.backward") }
|
||||
Spacer()
|
||||
Text(chatModel.chats[cId]?.chatInfo.localDisplayName ?? "")
|
||||
.font(.title3)
|
||||
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(.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 ?? "")
|
||||
)
|
||||
.padding([.horizontal, .top])
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatHeaderView_Previews: PreviewProvider {
|
||||
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 {
|
||||
HStack {
|
||||
TextField("Message...", text: $command)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
.textInputAutocapitalization(.never)
|
||||
.disableAutocorrection(true)
|
||||
.frame(minHeight: 30)
|
||||
.onSubmit(submit)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.textInputAutocapitalization(.never)
|
||||
.disableAutocorrection(true)
|
||||
.onSubmit(submit)
|
||||
|
||||
if (inProgress) {
|
||||
ProgressView()
|
||||
@ -31,7 +30,7 @@ struct SendMessageView: View {
|
||||
}
|
||||
}
|
||||
.frame(minHeight: 30)
|
||||
.padding()
|
||||
.padding(12)
|
||||
}
|
||||
|
||||
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 */
|
||||
5C063D2727A4564100AEC577 /* 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 */; };
|
||||
5C1AEB87279F4A6400247F08 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C1AEB7F279F4A6400247F08 /* libffi.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 */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
5C764E81279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; };
|
||||
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 */; };
|
||||
5CA05A4F279752D00002BEB4 /* 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 */; };
|
||||
5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; };
|
||||
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; };
|
||||
@ -87,6 +99,7 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -97,6 +110,7 @@
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
@ -118,6 +132,10 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -191,12 +209,10 @@
|
||||
children = (
|
||||
5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */,
|
||||
5CA05A4E279752D00002BEB4 /* MessageView.swift */,
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */,
|
||||
5CCD403027A5F1C600368C90 /* ChatHeaderView.swift */,
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */,
|
||||
5CCD403627A5F9A200368C90 /* ConnectContactView.swift */,
|
||||
5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */,
|
||||
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */,
|
||||
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */,
|
||||
5CB924DF27A8678B00ACCCDD /* UserSettings */,
|
||||
5CB924DD27A8622200ACCCDD /* NewChat */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
@ -297,6 +313,30 @@
|
||||
path = "Tests macOS";
|
||||
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 */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -464,7 +504,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */,
|
||||
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */,
|
||||
5CB924E127A867BA00ACCCDD /* SettingsProfile.swift in Sources */,
|
||||
5C764E80279C7276000C6508 /* dummy.m in Sources */,
|
||||
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */,
|
||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */,
|
||||
5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */,
|
||||
@ -483,6 +527,8 @@
|
||||
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */,
|
||||
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */,
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */,
|
||||
5CB924D427A853F100ACCCDD /* SettingsButton.swift in Sources */,
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -490,7 +536,11 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */,
|
||||
5CB924D827A8563F00ACCCDD /* SettingsView.swift in Sources */,
|
||||
5CB924E227A867BA00ACCCDD /* SettingsProfile.swift in Sources */,
|
||||
5C764E81279C7276000C6508 /* dummy.m in Sources */,
|
||||
5CB924E527A8683A00ACCCDD /* UserAddress.swift in Sources */,
|
||||
5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||
5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */,
|
||||
5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */,
|
||||
@ -509,6 +559,8 @@
|
||||
5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */,
|
||||
5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */,
|
||||
5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */,
|
||||
5CB924D527A853F100ACCCDD /* SettingsButton.swift in Sources */,
|
||||
5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user