adding toasts like crazy
This commit is contained in:
@@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user