adding toasts like crazy

This commit is contained in:
talksik
2022-01-02 17:43:06 -08:00
parent ddb7380511
commit 57f8dd4dc9
6 changed files with 169 additions and 52 deletions
@@ -8,8 +8,45 @@
import Foundation import Foundation
import Contacts import Contacts
import SwiftUI import SwiftUI
import AlertToast
class ContactsViewModel : ObservableObject { class ContactsViewModel : ObservableObject {
@Published var toast: Toast? {
didSet {
self.showToast = true
}
}
@Published var showToast: Bool = false
enum Toast: Identifiable {
var id: Self { self }
case maxFriendsInCircle
case cannotFriendYourself
case addedFriend
case removedFriend
case fetchingContacts
case generalError
var view: AlertToast {
switch self {
case .fetchingContacts:
return AlertToast(displayMode: .hud, type: .systemImage("person.3.fill", NirvanaColor.dimTeal), title: "looking for friends")
case .removedFriend:
return AlertToast(displayMode: .hud, type: .complete(Color.green), title: "removed friend")
case .addedFriend:
return AlertToast(displayMode: .hud, type: .complete(Color.green), title: "added friend")
case .cannotFriendYourself:
return AlertToast(displayMode: .hud, type: .error(Color.orange), title: "silly! 🙉", subTitle: "you cannot friend yourself")
case .maxFriendsInCircle:
return AlertToast(displayMode: .hud, type: .systemImage("person.crop.circle.badge.exclamationmark.fill", Color.orange), title: "circle full!", subTitle: "tap on an existing friend and hold down on their icon in the bottom left to remove them to make space")
default:
return AlertToast(displayMode: .hud, type: .error(Color.orange), title: "Something went wrong ‼️")
}
}
}
@Published var showPermissionAlert = false @Published var showPermissionAlert = false
@Published var contacts: [String: ContactsViewModelContact] = [:] @Published var contacts: [String: ContactsViewModelContact] = [:]
@@ -48,6 +85,8 @@ class ContactsViewModel : ObservableObject {
fetchRequest.sortOrder = .userDefault fetchRequest.sortOrder = .userDefault
do { do {
self.toast = .fetchingContacts
try store.enumerateContacts(with: fetchRequest) {[weak self](contact, stop) in try store.enumerateContacts(with: fetchRequest) {[weak self](contact, stop) in
// only checking if american number or not for now // only checking if american number or not for now
var cnPhoneNumber = contact.phoneNumbers.first?.value.stringValue var cnPhoneNumber = contact.phoneNumbers.first?.value.stringValue
@@ -88,7 +127,6 @@ class ContactsViewModel : ObservableObject {
} }
} catch { } catch {
print("Unable to fetch contacts. \(error)") print("Unable to fetch contacts. \(error)")
} }
} }
@@ -97,6 +135,7 @@ class ContactsViewModel : ObservableObject {
// make sure userId is not the same as friendId...don't want people friending themselves // make sure userId is not the same as friendId...don't want people friending themselves
if userId == friendId { if userId == friendId {
completion(ServiceState.error(ServiceError(description: "You cannot friend yourself, silly!"))) completion(ServiceState.error(ServiceError(description: "You cannot friend yourself, silly!")))
self.toast = .cannotFriendYourself
return return
} }
@@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
import Contacts import Contacts
import NavigationStack import NavigationStack
import AlertToast
struct FindFriendsView: View { struct FindFriendsView: View {
@EnvironmentObject var navigationStack : NavigationStack @EnvironmentObject var navigationStack : NavigationStack
@@ -52,6 +53,9 @@ struct FindFriendsView: View {
.onAppear { .onAppear {
self.contactsVM.fetchContacts() self.contactsVM.fetchContacts()
} }
.toast(isPresenting: self.$contactsVM.showToast) {
self.contactsVM.toast?.view ?? AlertToast(displayMode: .hud, type: .error(Color.red), title: "Something went wrong")
}
} }
var searchItems: [String] { var searchItems: [String] {
@@ -134,24 +138,28 @@ struct ListContactRow: View {
primaryButton: .default(Text("Cancel")), primaryButton: .default(Text("Cancel")),
secondaryButton: .default(Text("Confirm")) { secondaryButton: .default(Text("Confirm")) {
print("adding contact to circle") print("adding contact to circle")
// call method in vm to get it done, then navigate to the circle if self.authSessionStore.friendsArr.count >= 10 {
if self.authSessionStore.friendsArr.count < 10 || self.authSessionStore.user?.phoneNumber == "+19499230445" { self.contactsVM.toast = .maxFriendsInCircle
self.contactsVM.addOrActivateFriendToCircle(userId: self.authSessionStore.user!.id!, friendId: (contact.user!.id)!) {res in return
print(res) }
switch res { self.contactsVM.addOrActivateFriendToCircle(userId: self.authSessionStore.user!.id!, friendId: (contact.user!.id)!) {res in
case .error(let err): print(res)
print(err)
case .success(let str):
print(str)
}
}
self.navigationStack.push(InnerCircleView()) switch res {
case .error(let err):
print(err)
self.contactsVM.toast = .generalError
case .success(let str):
print(str)
self.contactsVM.toast = .addedFriend
self.navigationStack.push(InnerCircleView())
}
} }
} }
) )
} }
} }
else { // invite button to text the person else { // invite button to text the person
HStack(alignment: .center, spacing: 0) { HStack(alignment: .center, spacing: 0) {
@@ -43,11 +43,9 @@ struct CircleFooterView: View {
Button(role: .destructive) { Button(role: .destructive) {
print("deactivating friend...removing from circle") print("deactivating friend...removing from circle")
if self.authSessionStore.user?.id != nil && self.selectedFriendIndex != nil { if self.authSessionStore.user?.id != nil && self.selectedFriendIndex != nil {
self.innerCircleVM.activateOrDeactiveInboxUser(activate: false, userId: self.authSessionStore.user!.id!, friendId: self.selectedFriendIndex!) {res in self.innerCircleVM.activateOrDeactiveInboxUser(activate: false, userId: self.authSessionStore.user!.id!, friendId: self.selectedFriendIndex!)
print(res) self.selectedFriendIndex = nil
self.selectedFriendIndex = nil // making this footer disappear although a view change should do this
}
} }
} label: { } label: {
Label("Remove from Circle", systemImage: "person.crop.circle.fill.badge.minus") Label("Remove from Circle", systemImage: "person.crop.circle.fill.badge.minus")
@@ -131,7 +131,6 @@ struct CircleGridView: View {
else { else {
print("no convo id to join") print("no convo id to join")
} }
} }
else { else {
self.convoVM.toast = .alreadyInCall self.convoVM.toast = .alreadyInCall
@@ -308,17 +307,17 @@ struct CircleGridView: View {
message: Text(self.alertSubtext), message: Text(self.alertSubtext),
primaryButton: .destructive(Text("Reject"), action: { primaryButton: .destructive(Text("Reject"), action: {
// create user friend but a rejected one // create user friend but a rejected one
if self.authSessionStore.friendsArr.count < 10 || self.authSessionStore.user?.phoneNumber == "+19499230445" { self.innerCircleVM.activateOrDeactiveInboxUser(activate: false, userId: self.authSessionStore.user!.id!, friendId: inboxUserId)
self.innerCircleVM.activateOrDeactiveInboxUser(activate: false, userId: self.authSessionStore.user!.id!, friendId: inboxUserId) { res in
print(res)
}
}
}), }),
secondaryButton: .default(Text("Add"), action: { secondaryButton: .default(Text("Add"), action: {
// create user friend // create user friend if have space in circle
self.innerCircleVM.activateOrDeactiveInboxUser(activate: true, userId: self.authSessionStore.user!.id!, friendId: inboxUserId) { res in // TODO: view models should be able to do this validation, need some way of view models to speak to each other
print(res) if self.authSessionStore.friendsArr.count >= 10 {
self.innerCircleVM.toast = .maxFriendsInCircle
return
} }
self.innerCircleVM.activateOrDeactiveInboxUser(activate: true, userId: self.authSessionStore.user!.id!, friendId: inboxUserId)
}) })
) )
} }
@@ -153,6 +153,9 @@ struct InnerCircleView: View {
.toast(isPresenting: self.$convoViewModel.showToast) { .toast(isPresenting: self.$convoViewModel.showToast) {
self.convoViewModel.toast?.view ?? AlertToast(displayMode: .hud, type: .error(Color.red), title: "Something went wrong") self.convoViewModel.toast?.view ?? AlertToast(displayMode: .hud, type: .error(Color.red), title: "Something went wrong")
} }
.toast(isPresenting: self.$innerCircleVM.showToast) {
self.innerCircleVM.toast?.view ?? AlertToast(displayMode: .hud, type: .error(Color.red), title: "Something went wrong")
}
// .onDisappear { // .onDisappear {
// print("deiniting data listeners, but current data should still be cached!") // print("deiniting data listeners, but current data should still be cached!")
// //
@@ -7,8 +7,58 @@
import Foundation import Foundation
import AVFoundation import AVFoundation
import AlertToast
import SwiftUI
class InnerCircleViewModel: ObservableObject { class InnerCircleViewModel: ObservableObject {
@Published var toast: Toast? {
didSet {
self.showToast = true
}
}
@Published var showToast: Bool = false
enum Toast: Identifiable {
var id: Self { self }
case startedClip
case problemSendingClip
case nothingRecorded
case clipSent
case maxFriendsInCircle
case cannotFriendYourself
case addedFriend
case removedFriend
case generalError
var view: AlertToast {
switch self {
case .removedFriend:
return AlertToast(displayMode: .hud, type: .complete(Color.green), title: "removed friend")
case .addedFriend:
return AlertToast(displayMode: .hud, type: .complete(Color.green), title: "added friend")
case .cannotFriendYourself:
return AlertToast(displayMode: .hud, type: .error(Color.orange), title: "silly! 🙉", subTitle: "you cannot friend yourself")
case .maxFriendsInCircle:
return AlertToast(displayMode: .hud, type: .systemImage("person.crop.circle.badge.exclamationmark.fill", Color.orange), title: "circle full!", subTitle: "tap on an existing friend and hold down on their icon in the bottom left to remove them to make space")
case .nothingRecorded:
return AlertToast(displayMode: .hud, type: .systemImage("exclamationmark.triangle.fill", NirvanaColor.teal), title: "nothing recorded", subTitle: "please try again")
case .clipSent:
return AlertToast(displayMode: .hud, type: .systemImage("paperplane.circle.fill", NirvanaColor.teal), title: "clip sent")
case .problemSendingClip:
return AlertToast(displayMode: .hud, type: .error(Color.orange), title: "problem sending clip", subTitle: "please try again")
case .startedClip:
return AlertToast(displayMode: .hud, type: .systemImage("waveform.circle.fill", NirvanaColor.teal), title: "clip started")
default:
return AlertToast(displayMode: .hud, type: .error(Color.orange), title: "Something went wrong ‼️")
}
}
}
var audioRecorder : AVAudioRecorder! var audioRecorder : AVAudioRecorder!
var audioPlayer : AVAudioPlayer! var audioPlayer : AVAudioPlayer!
@@ -21,7 +71,7 @@ class InnerCircleViewModel: ObservableObject {
private let cloudStorageService = CloudStorageService() private let cloudStorageService = CloudStorageService()
private let firestoreService = FirestoreService() private let firestoreService = FirestoreService()
private let pushNotificationService = PushNotificationService() private let pushNotificationService = PushNotificationService()
private let agoraService = AgoraService()
init() { init() {
// separate set up for listening vs recording // separate set up for listening vs recording
@@ -57,6 +107,8 @@ extension InnerCircleViewModel {
audioRecorder.record() audioRecorder.record()
isRecording = true isRecording = true
self.toast = .startedClip
self.audioLocalUrl = filePath // setting this for later use when recording is stopped self.audioLocalUrl = filePath // setting this for later use when recording is stopped
} catch { } catch {
@@ -81,6 +133,7 @@ extension InnerCircleViewModel {
self.cloudStorageService.uploadLocalUrl(localFileUrl: self.audioLocalUrl!) {[weak self] audioDataUrl in self.cloudStorageService.uploadLocalUrl(localFileUrl: self.audioLocalUrl!) {[weak self] audioDataUrl in
if audioDataUrl == nil { if audioDataUrl == nil {
print("there was an error in uploading file") print("there was an error in uploading file")
self?.toast = .problemSendingClip
return return
} }
@@ -90,18 +143,33 @@ extension InnerCircleViewModel {
self?.firestoreService.createMessage(message: newMessage) {[weak self] res in self?.firestoreService.createMessage(message: newMessage) {[weak self] res in
print(res) print(res)
// sending push notification if there was a device token switch res {
if receiver.deviceToken != nil && receiver.nickname != nil { case .success:
self?.pushNotificationService.sendPushNotification(to: receiver.deviceToken!, title: "🌱Nirvana", body: "continue your conversation with \(sender.nickname ?? "your friend")")
self?.toast = .clipSent
// sending push notification if there was a device token for this friend
if receiver.deviceToken != nil && receiver.nickname != nil {
self?.pushNotificationService.sendPushNotification(to: receiver.deviceToken!, title: "🌱Nirvana", body: "continue your conversation with \(sender.nickname ?? "your friend")")
}
// delete local audio file from user's phone so that it doesn't take crazy space
print("stopped recording: file about to get deleted from \(self?.getTemporaryDirectory()) with filename: \(self?.audioLocalUrl)")
try? FileManager.default.removeItem(at: (self?.audioLocalUrl)!)
case .error(let err):
print(err)
self?.toast = .problemSendingClip
default:
self?.toast = .problemSendingClip
} }
// delete local audio file from user's phone so that it doesn't get crazy
print("stopped recording: file about to get deleted from \(self?.getTemporaryDirectory()) with filename: \(self?.audioLocalUrl)")
try? FileManager.default.removeItem(at: (self?.audioLocalUrl)!)
} }
} }
} }
else {
print("nothing recorded, can't send")
self.toast = .problemSendingClip
}
} }
func getTemporaryDirectory() -> URL { func getTemporaryDirectory() -> URL {
@@ -123,11 +191,12 @@ extension InnerCircleViewModel {
// handle activating or deactivating friends // handle activating or deactivating friends
extension InnerCircleViewModel { extension InnerCircleViewModel {
func activateOrDeactiveInboxUser(activate: Bool, userId: String, friendId: String, completion: @escaping((_ state: ServiceState) -> ())) { func activateOrDeactiveInboxUser(activate: Bool, userId: String, friendId: String) {
// validation // validation
// make sure userId is not the same as friendId...don't want people friending themselves // make sure userId is not the same as friendId...don't want people friending themselves
if userId == friendId { if userId == friendId {
completion(ServiceState.error(ServiceError(description: "You cannot friend yourself, silly!"))) print("you cannot friend yourself")
self.toast = .cannotFriendYourself
return return
} }
@@ -135,7 +204,20 @@ extension InnerCircleViewModel {
var userFriend = UserFriends(userId: userId, friendId: friendId, isActive: activate, lastUpdatedTimestamp: nil) var userFriend = UserFriends(userId: userId, friendId: friendId, isActive: activate, lastUpdatedTimestamp: nil)
self.firestoreService.createOrUpdateUserFriends(userFriend: userFriend, activateOrDeactivate: activate) {[weak self] res in self.firestoreService.createOrUpdateUserFriends(userFriend: userFriend, activateOrDeactivate: activate) {[weak self] res in
completion(res) switch res {
case .success:
if activate {
self?.toast = .addedFriend
}
else {
self?.toast = .removedFriend
}
case .error(let err):
print(err)
self?.toast = .generalError
default:
self?.toast = .generalError
}
} }
} }
} }
@@ -146,15 +228,3 @@ extension InnerCircleViewModel {
} }
} }
// everything related to calls and such
extension InnerCircleViewModel {
func getAgoraToken() {
if let uid = AuthSessionStore.getCurrentUserId() {
self.agoraService.getAgoraUserTokenServer(channelName: uid)
}
else {
print("no user authenticated to get an agora token")
}
}
}