Skip to content

Commit

Permalink
Add swipe to delete message
Browse files Browse the repository at this point in the history
  • Loading branch information
marcprux committed Jan 9, 2024
1 parent 51bd397 commit 6ebc629
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 76 deletions.
82 changes: 71 additions & 11 deletions Sources/FireSide/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct ContentView: View {
TabView(selection: $selectedTab) {
JoinChatView()
.tag(0)
.tabItem { Label("Welcome", systemImage: "star") }
.tabItem { Label("Join", systemImage: "star") }
.task {
//do {
// try await fireSide.runTask()
Expand All @@ -27,7 +27,7 @@ public struct ContentView: View {
MessagesListView()
}
.tag(1)
.tabItem { Label("Home", systemImage: "house.fill") }
.tabItem { Label("Messages", systemImage: "list.bullet") }

Form {
Text("Settings")
Expand All @@ -40,43 +40,86 @@ public struct ContentView: View {
}
}

let fmt: DateFormatter = {
let f = DateFormatter()
f.dateStyle = .short
f.timeStyle = .short
return f
}()

let fmt2: DateFormatter = {
let f = DateFormatter()
f.dateStyle = .long
f.timeStyle = .long
return f
}()

struct MessagesListView : View {
@State var messageList: MessageList? = nil

var body: some View {
VStack {
VStack(spacing: 0.0) {
List {
if let messageList = messageList {
ForEach(messageList.messages) { m in
NavigationLink(value: m) {
HStack {
Text(m.message)
.font(.title)
Text(m.time.description)
.font(.title2)

Text(fmt.string(from: m.time))
.font(Font.callout)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.lineLimit(1)
}
}
.onDelete { indices in
Task.detached {
await deleteMessages(indices)
}
}
}
}
.navigationTitle("Navigation")
#if !SKIP
// the Observable is in FireSideModel, which doesn't know about SwiftUI and so cannot perform the update in a `withAnimation`
.animation(.default, value: messageList?.messages)
#endif
.navigationTitle("Messages: \(messageList?.messages.count ?? 0)")
.navigationDestination(for: Message.self) { msg in
VStack {
Text(msg.message)
.font(.title)
.navigationTitle("Message")
Form {
HStack {
Text("ID")
Text(msg.id ?? "NO ID")
}

HStack {
Text("Message")
Text(msg.message)
}

HStack {
Text("Date")
Text(fmt2.string(from: msg.time))
.font(Font.subheadline)
}

}
.navigationTitle("Message")
}

Divider()

HStack {
ForEach(["♥️", "💙", "💜", "💛", "💚"], id: \.self) { emoji in
ForEach(["♥️", "💙", "💛", "💚"], id: \.self) { emoji in
Button(emoji) {
Task.detached {
let isJava = ProcessInfo.processInfo.environment["java.io.tmpdir"] != nil
let msg = emoji + " from " + (isJava ? "Android" : "iOS")
await sendMessage(msg)
}
}
.font(.largeTitle)
.buttonStyle(.bordered)
}
}
Expand All @@ -102,6 +145,23 @@ struct MessagesListView : View {
}

}

func deleteMessages(_ indices: IndexSet) async {
logger.log("deleteMessages: \(indices)")
do {
if let messages = self.messageList?.messages {
let ids = indices.compactMap({ messages[$0].id })
self.messageList?.messages.removeAll(where: {
ids.contains($0.id ?? "")
})
try await firestore.deleteMessages(ids)
logger.error("deleted ids: \(ids)")
}
} catch {
logger.error("error deleteMessages: \(error)")
}

}
}

let chatKeyCount = 8
Expand Down
16 changes: 11 additions & 5 deletions Sources/FireSide/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
"Chat Key" : {

},
"Home" : {
"Date" : {

},
"ID" : {

},
"Join" : {

},
"Join Chat" : {
Expand All @@ -13,7 +19,10 @@
"Message" : {

},
"Navigation" : {
"Messages" : {

},
"Messages: %lld" : {

},
"New Chat" : {
Expand All @@ -24,9 +33,6 @@
},
"Settings" : {

},
"Welcome" : {

}
},
"version" : "1.0"
Expand Down
124 changes: 64 additions & 60 deletions Sources/FireSideModel/FireSideModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,68 +58,80 @@ public actor FireSideStore {
/// "Sends" a message by adding it to the document
@MainActor public func sendMessage(_ message: String) async throws -> Message {
logger.info("sendMessage: \(message)")
let msg = Message(id: UUID(), message: message, time: Date.now)
let dref = try await firestore.collection("messages").addDocument(data: msg.data)
let msg = Message(id: nil, message: message, time: Date.now)
let _ = try await firestore.collection("messages").addDocument(data: msg.data)
return msg
}

@MainActor public func deleteMessages(_ ids: [String]) async throws {
for doc in try await firestore.collection("messages")
.whereField(FieldPath.documentID(), in: ids.map({ $0 as Any }))
.getDocuments()
.documents {
try await doc.reference.delete()
}

}

@MainActor public func startNewChat() async throws -> String {
logger.info("startNewChat")

let cref = firestore.collection("messages")
//let q = cref.whereField("t", isGreaterThan: 100.0).limit(to: 4)

let snapshot = try await cref.getDocuments()
logger.log("cref document: \(snapshot)")
for document in snapshot.documents {
logger.log("read cref: \(document.documentID) => \(document.data())")
}

var changeCount = 0
let lreg = cref.addSnapshotListener { q, e in
if let q = q {
logger.log(" addSnapshotListener: \(q) count: \(q.documentChanges.count)")
for change in q.documentChanges {
changeCount += 1
let t = change.type
logger.log(" - change: \(String(describing: t)) \(change.document) \(change)")
let data = change.document.data()
logger.log(" - change data: \(data)")
}
} else {
logger.log(" addSnapshotListener: NO QUERY error=\(e)")
}
}

logger.log("added snapshot listener")

let dref = try await cref.addDocument(data: [
"m": "some message",
"t": Date.now.timeIntervalSince1970,
])

logger.log("created document: \(dref.documentID)")

print("changeCount: \(changeCount) listener: \(lreg)")
lreg.remove()

return dref.documentID
//let snapshot = try await cref.getDocuments()
//logger.log("cref document: \(snapshot)")
//for document in snapshot.documents {
// logger.log("read cref: \(document.documentID) => \(document.data())")
//}

//var changeCount = 0
//let lreg = cref.addSnapshotListener { q, e in
// if let q = q {
// logger.log(" addSnapshotListener: \(q) count: \(q.documentChanges.count)")
// for change in q.documentChanges {
// changeCount += 1
// let t = change.type
// logger.log(" - change: \(String(describing: t)) \(change.document) \(change)")
// let data = change.document.data()
// logger.log(" - change data: \(data)")
// }
// } else {
// logger.log(" addSnapshotListener: NO QUERY error=\(e)")
// }
//}

//logger.log("added snapshot listener")

//let dref = try await cref.addDocument(data: [
// "m": "some message",
// "t": Date.now.timeIntervalSince1970,
//])

//logger.log("created document: \(dref.documentID)")

//print("changeCount: \(changeCount) listener: \(lreg)")
//lreg.remove()

//return dref.documentID

return "ABCDEF"
}

@MainActor public func runTask() async throws {
//let dbname = "(default)"
//@MainActor public func runTask() async throws {
// //let dbname = "(default)"

let cref = firestore.collection("messages")
let snapshot = try await cref.getDocuments()
for document in snapshot.documents {
logger.log("read cref: \(document.documentID) => \(document.data())")
}
// let cref = firestore.collection("messages")
// let snapshot = try await cref.getDocuments()
// for document in snapshot.documents {
// logger.log("read cref: \(document.documentID) => \(document.data())")
// }

let id = UUID()
let bos = cref.document("msg-\(id.uuidString)")
// let id = UUID()
// let bos = cref.document("msg-\(id.uuidString)")

try await bos.setData(Message(id: UUID(), message: "message", time: Date.now).data)
}
// try await bos.setData(Message(id: UUID(), message: "message", time: Date.now).data)
//}

public struct InvalidConfigurationError : LocalizedError {
public var errorDescription: String?
Expand All @@ -137,7 +149,7 @@ public actor FireSideStore {
var msgs: [Message] = []
if let snap = snap {
for doc in snap.documents {
if let msg = Message.from(data: doc.data()) {
if let msg = Message.from(id: doc.documentID, data: doc.data()) {
msgs.append(msg)
} else {
logger.warning("could not create message from data: \(doc.data())")
Expand All @@ -157,34 +169,26 @@ public actor FireSideStore {

/// An individual message
public struct Message: Hashable, Identifiable, Codable, CustomStringConvertible {
public let id: UUID
public let id: String?
public var message: String
public var time: Date

public var description: String {
return "Message: id=\(id.uuidString) message=\(message) time=\(time.timeIntervalSince1970)"
return "Message: id=\(id ?? "NONE") message=\(message) time=\(time.timeIntervalSince1970)"
}

static func from(data: [String: Any]) -> Message? {
static func from(id: String, data: [String: Any]) -> Message? {
guard let message = data["m"] as? String else {
return nil
}

guard let time = data["t"] as? TimeInterval else {
return nil
}

guard let uuid = data["id"] as? String,
let id = UUID(uuidString: uuid) else {
return nil
}

return Message(id: id, message: message, time: Date(timeIntervalSince1970: time))
}

var data: [String: Any] {
[
"id": id.uuidString,
"m": message,
"t": time.timeIntervalSince1970,
]
Expand Down

0 comments on commit 6ebc629

Please sign in to comment.