Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions Orbit/Services/MeetupApprovalService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,19 @@ class MeetupApprovalService: MeetupApprovalServiceProtocol {
}

// Delete an approval
func deleteApproval(approvalId: String) async throws {
do {
try await appwriteService.databases.deleteDocument(
databaseId: appwriteService.databaseId,
collectionId: collectionId,
documentId: approvalId
)
} catch {
throw NSError(
domain: "Failed to delete approval", code: 500, userInfo: nil)
func deleteApproval(approvalId: String) async throws {
do {
let result = try await appwriteService.databases.deleteDocument(
databaseId: appwriteService.databaseId,
collectionId: collectionId,
documentId: approvalId
)
} catch {
throw NSError(
domain: "Failed to delete approval", code: 500, userInfo: nil)
}
}
}


// List all approvals
func listApprovals(queries: [String]? = nil) async throws
-> [MeetupApprovalDocument]
Expand Down
3 changes: 2 additions & 1 deletion Orbit/UI/OrbitApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct OrbitApp: App {
.font: UIFont.systemFont(ofSize: 24, weight: .bold),
]
UINavigationBar.appearance().scrollEdgeAppearance = scrollEdgeAppearance

}

var body: some Scene {
Expand All @@ -58,7 +59,7 @@ struct OrbitApp: App {
.environmentObject(authVM)
.environmentObject(userVM)
.environmentObject(msgVM)
.environmentObject(chatRequestVM)
.environmentObject(chatRequestVM)
.environmentObject(meetupRequestVM)
.environmentObject(meetupApprovalVM)
.environmentObject(appDelegate.appState)
Expand Down
2 changes: 1 addition & 1 deletion Orbit/UI/Screens/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import AppwriteModels
import SwiftUI

struct HomeView: View {
@EnvironmentObject private var userVM: UserViewModel
@EnvironmentObject private var authVM: AuthViewModel
@EnvironmentObject private var chatRequestVM: ChatRequestViewModel
@EnvironmentObject private var meetupRequestVM: MeetupRequestViewModel
@EnvironmentObject private var appState: AppState
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject private var userVM: UserViewModel

// @State private var selectedMeetupRequest: MeetupRequestDocument? = nil
@State private var isShowingChatRequests = false
Expand Down
44 changes: 24 additions & 20 deletions Orbit/UI/Screens/Meetup/MeetuprequestDetailedView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ struct MeetupRequestDetailedView: View {
}

HStack(spacing: 16) {
Button(action: declineMeetupRequest) {
Button(action: {
Task {
await meetupApprovalVM.declineMeetup(meetupRequest: meetupRequest)
dismiss()
}
}) {
HStack {
Image(systemName: "xmark.circle.fill")
Text("Decline")
Expand All @@ -154,6 +159,7 @@ struct MeetupRequestDetailedView: View {
.background(Color.red)
.cornerRadius(16)
}

Button(action: approveMeetupRequest) {
HStack {
Image(systemName: "checkmark.circle.fill")
Expand All @@ -173,18 +179,23 @@ struct MeetupRequestDetailedView: View {
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
dismiss()
} label: {
Image(systemName: "person.crop.circle.badge.xmark") //Other potential icons: "nosign", "shield.lefthalf.filled"
.foregroundColor(
ColorPalette.secondaryText(for: colorScheme))
#warning(
"TODO: Add block functionality"
)
Menu {
Button(role: .destructive) {
Task {
if let userId = meetupRequest.createdByUser?.id {
await userVM.blockUser(userId: userId)
dismiss() // Dismiss after blocking
}
}
} label: {
Label("Block User", systemImage: "nosign")
}
} label: {
Image(systemName: "ellipsis.circle") // Three-dot menu
.foregroundColor(ColorPalette.secondaryText(for: colorScheme))
}
}
}
}
}

}
.onAppear {
Expand Down Expand Up @@ -236,14 +247,7 @@ struct MeetupRequestDetailedView: View {
}
}

private func declineMeetupRequest() {
Task {
#warning(
"TODO: Implement decline functionality"
)
dismiss()
}
}

}

#if DEBUG
Expand Down
43 changes: 25 additions & 18 deletions Orbit/UI/ViewModels/MeetupApprovalViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,31 @@ class MeetupApprovalViewModel: ObservableObject {
)
}
}
/// Declines (removes) a meetup approval request
func declineMeetup(meetupRequest: MeetupRequestModel) async {
isLoading = true
defer { isLoading = false }

do {
// Find the approval linked to this meetup request
if let approval = approvals.first(where: { $0.data.meetupRequest?.id == meetupRequest.id }) {
// Call the service to delete it
try await meetupApprovalService.deleteApproval(approvalId: approval.id)

/// Reject (or remove) a meetup approval
// func removeApproval(_ approval: MeetupApprovalModel) async {
// isLoading = true
// defer { isLoading = false }
//
// do {
// try await meetupApprovalService.deleteApproval(
// approvalId: approval.meetupRequest.title)
// self.approvals.removeAll {
// $0.meetupRequest.title == approval.meetupRequest.title
// }
// } catch {
// self.error = error.localizedDescription
// print(
// "MeetupApprovalViewModel - removeApproval: Error: \(error.localizedDescription)"
// )
// }
// }
// Update UI by removing the declined approval
await MainActor.run {
self.approvals.removeAll { $0.id == approval.id }
}

print("Successfully declined meetup request.")
} else {
print("Error: Approval not found for meetup request \(meetupRequest.id)")
}
} catch {
self.error = error.localizedDescription
print("MeetupApprovalViewModel - declineMeetup: Error: \(error.localizedDescription)")
}
}

#if DEBUG
static func mock() -> MeetupApprovalViewModel {
Expand All @@ -113,3 +119,4 @@ class MeetupApprovalViewModel: ObservableObject {
}
#endif
}

23 changes: 14 additions & 9 deletions Orbit/UI/ViewModels/MeetupRequestViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ class MeetupRequestViewModel: ObservableObject {

private var meetupService: MeetupRequestServiceProtocol =
MeetupRequestService()

init() {
private var userVM: UserViewModel

init(userVM: UserViewModel) {
if !isPreviewMode {
Task {
await fetchAllMeetups()
Expand All @@ -35,13 +36,17 @@ class MeetupRequestViewModel: ObservableObject {
do {
let meetups = try await meetupService.listMeetups(queries: nil)
self.meetupRequests = meetups
} catch {
self.error = error.localizedDescription
print(
"MeetupRequestViewModel - fetchAllMeetups: Error: \(error.localizedDescription)"
)
}
}
await MainActor.run {
self.meetupRequests = meetups.filter { request in
guard let userId = request.data.createdByUser?.id else { return true }
return !userVM.isUserBlocked(userId: userId)
}
}
} catch {
self.error = error.localizedDescription
print("MeetupRequestViewModel - fetchAllMeetups: Error: \(error.localizedDescription)")
}
}

/// Fetch a specific meetup by ID
func fetchMeetup(by id: String) async {
Expand Down
54 changes: 54 additions & 0 deletions Orbit/UI/ViewModels/UserViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class UserViewModel: NSObject, ObservableObject, PreciseLocationManagerDelegate,
@Published var selectedRadius: Double = 10.0
@Published var isOnCampus = false // Track if the user is inside campus
@Published var allUsers: [UserModel] = []
@Published var blockedUsers: Set<String> = []

private var userManagementService: UserManagementServiceProtocol =
UserManagementService()
Expand All @@ -51,6 +52,7 @@ class UserViewModel: NSObject, ObservableObject, PreciseLocationManagerDelegate,
var lastFetchedAreaId: String?
var lastFetchedTimestamp: Date?
private var subscribeToLocationUpdates: RealtimeSubscription?
private let appwriteService = AppwriteService.shared

init(
campusLocationManager: CampusLocationManager = CampusLocationManager()
Expand All @@ -73,6 +75,57 @@ class UserViewModel: NSObject, ObservableObject, PreciseLocationManagerDelegate,
// await subscribeToRealtimeUpdates()
// self.allUsers = await getAllUsers()
// await fetchAllUsernames()
await fetchBlockedUsers()
}
@MainActor
func fetchBlockedUsers() async {
do {
let blockedUsersList = try await appwriteService.databases.listDocuments(
databaseId: "orbit",
collectionId: "blockedUsers"
)
let blockedIds = blockedUsersList.documents.compactMap { document in
document.data["blockedUserId"].map {String(describing: $0)}
}

// Update local blockedUsers list
self.blockedUsers = Set(blockedIds)

print("Fetched blocked users: \(self.blockedUsers)")
} catch {
print("Error fetching blocked users: \(error.localizedDescription)")
}
}

func isUserBlocked(userId: String) -> Bool {
return blockedUsers.contains(userId)
}

@MainActor
func blockUser(userId: String) async {
guard !blockedUsers.contains(userId) else {
print("User is already blocked.")
return
}

do {
// Store the blocked user in the Appwrite database
let createdDocument = try await appwriteService.databases.createDocument(
databaseId: "orbit",
collectionId: "blockedUsers",
documentId: ID.unique(),
data: ["blockedUserId": userId, "blockingUserId": currentUser?.id ?? ""]
)

print("Block entry created with ID:\(createdDocument)")

// Update local state
self.blockedUsers.insert(userId)

print("User \(userId) has been blocked.")
} catch {
print("Error blocking user: \(error)")
}
}

@MainActor
Expand Down Expand Up @@ -637,6 +690,7 @@ class UserViewModel: NSObject, ObservableObject, PreciseLocationManagerDelegate,
self.error = error.localizedDescription
}
}


@MainActor
func handleRealtimeUserUpdate(_ updatedUser: UserModel) {
Expand Down