Skip to content

Commit

Permalink
Add app improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-amisha-i authored Dec 31, 2024
1 parent 5c55c41 commit b4dd71f
Show file tree
Hide file tree
Showing 51 changed files with 792 additions and 399 deletions.
2 changes: 1 addition & 1 deletion .firebaserc
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
}
},
"dataconnectEmulatorConfig": {}
}
}
1 change: 0 additions & 1 deletion Data/Data/Extension/Notification+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public extension Notification.Name {
static let addTransaction = Notification.Name("addTransaction")
static let updateTransaction = Notification.Name("updateTransaction")
static let deleteTransaction = Notification.Name("deleteTransaction")
static let restoreTransaction = Notification.Name("restoreTransaction")

static let joinGroup = Notification.Name("joinGroup")

Expand Down
2 changes: 1 addition & 1 deletion Data/Data/Model/AppUser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public struct AppUser: Identifiable, Codable, Hashable, Sendable {
}

enum CodingKeys: String, CodingKey {
case id = "id"
case id
case firstName = "first_name"
case lastName = "last_name"
case emailId = "email_id"
Expand Down
4 changes: 2 additions & 2 deletions Data/Data/Repository/ActivityLogRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public class ActivityLogRepository: ObservableObject {

@Inject private var store: ActivityLogStore

public func fetchLatestActivityLogs(userId: String) -> AsyncStream<[ActivityLog]?> {
store.fetchLatestActivityLogs(userId: userId)
public func streamLatestActivityLogs(userId: String) -> AsyncStream<[ActivityLog]?> {
store.streamLatestActivityLogs(userId: userId)
}

public func addActivityLog(userId: String, activity: ActivityLog) async throws {
Expand Down
4 changes: 4 additions & 0 deletions Data/Data/Repository/GroupRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ public class GroupRepository: ObservableObject {
try await store.fetchGroupBy(id: id)
}

public func streamLatestGroupDataBy(id: String) -> AsyncStream<Groups?> {
store.streamLatestGroupBy(id: id)
}

public func fetchGroupsBy(userId: String, limit: Int = 10, lastDocument: DocumentSnapshot? = nil) async throws -> (data: [Groups], lastDocument: DocumentSnapshot?) {
try await store.fetchGroupsBy(userId: userId, limit: limit, lastDocument: lastDocument)
}
Expand Down
8 changes: 6 additions & 2 deletions Data/Data/Repository/TransactionRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,14 @@ public class TransactionRepository: ObservableObject {
}

public func fetchTransactionsBy(groupId: String, limit: Int = 10, lastDocument: DocumentSnapshot? = nil) async throws -> (transactions: [Transactions], lastDocument: DocumentSnapshot?) {
return try await store.fetchTransactionsBy(groupId: groupId, limit: limit, lastDocument: lastDocument)
try await store.fetchTransactionsBy(groupId: groupId, limit: limit, lastDocument: lastDocument)
}

public func fetchTransactionBy(groupId: String, transactionId: String) async throws -> Transactions {
return try await store.fetchTransactionsBy(groupId: groupId, transactionId: transactionId)
try await store.fetchTransactionsBy(groupId: groupId, transactionId: transactionId)
}

public func getTransactionsCount(groupId: String) async throws -> Int {
try await store.getTransactionsCount(groupId: groupId)
}
}
26 changes: 12 additions & 14 deletions Data/Data/Repository/UserRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public class UserRepository: ObservableObject {
try await store.fetchUserBy(email: email)
}

public func fetchLatestUserBy(userID: String) -> AsyncStream<AppUser?> {
store.fetchLatestUserBy(id: userID)
public func streamLatestUserBy(userID: String) -> AsyncStream<AppUser?> {
store.streamLatestUserBy(id: userID)
}

private func uploadImage(imageData: Data, user: AppUser) async throws -> AppUser {
Expand Down Expand Up @@ -94,18 +94,16 @@ public class UserRepository: ObservableObject {
try await user.delete()
}

public func updateDeviceFcmToken(retryCount: Int = 3) {
Task {
guard let userId = preference.user?.id, let fcmToken = preference.fcmToken else { return }

do {
try await store.updateUserDeviceFcmToken(userId: userId, fcmToken: fcmToken)
LogI("AppDelegate: \(#function) Device fcm token updated successfully.")
} catch {
LogE("AppDelegate: \(#function) Failed to update device fcm token: \(error).")
if retryCount > 0 {
updateDeviceFcmToken(retryCount: retryCount - 1)
}
public func updateDeviceFcmToken(retryCount: Int = 3) async {
guard let userId = preference.user?.id, let fcmToken = preference.fcmToken else { return }

do {
try await store.updateUserDeviceFcmToken(userId: userId, fcmToken: fcmToken)
LogI("UserRepository: \(#function) Device fcm token updated successfully.")
} catch {
LogE("UserRepository: \(#function) Failed to update device fcm token: \(error).")
if retryCount > 0 {
await updateDeviceFcmToken(retryCount: retryCount - 1)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Data/Data/Store/ActivityLogStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ActivityLogStore: ObservableObject {
.collection(SUB_COLLECTION_NAME)
}

func fetchLatestActivityLogs(userId: String) -> AsyncStream<[ActivityLog]?> {
func streamLatestActivityLogs(userId: String) -> AsyncStream<[ActivityLog]?> {
AsyncStream { continuation in
let query = activityReference(userId: userId)
.order(by: "recorded_on", descending: true)
Expand Down
31 changes: 31 additions & 0 deletions Data/Data/Store/GroupStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,37 @@ class GroupStore: ObservableObject {
}
}

func streamLatestGroupBy(id: String) -> AsyncStream<Groups?> {
AsyncStream { continuation in
let listener = groupReference.document(id).addSnapshotListener { snapshot, error in
if let error {
LogE("GroupStore: \(#function) Error fetching document: \(error).")
continuation.finish()
return
}

guard let snapshot else {
LogE("GroupStore: \(#function) Snapshot is nil for requested Group.")
continuation.yield(nil)
return
}

do {
let group = try snapshot.data(as: Groups.self)
continuation.yield(group)
} catch {
LogE("GroupStore: \(#function) Error decoding Group data: \(error).")
continuation.finish()
}
}

// Clean up: Remove listener when the stream is cancelled
continuation.onTermination = { _ in
listener.remove()
}
}
}

func fetchGroupsBy(userId: String, limit: Int, lastDocument: DocumentSnapshot?) async throws -> (data: [Groups], lastDocument: DocumentSnapshot?) {
var query = groupReference
.whereField("is_active", isEqualTo: true)
Expand Down
11 changes: 10 additions & 1 deletion Data/Data/Store/TransactionStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ public class TransactionStore: ObservableObject {
}

func fetchTransactionsBy(groupId: String, transactionId: String) async throws -> Transactions {
return try await transactionReference(groupId: groupId).document(transactionId).getDocument(as: Transactions.self, source: .server)
try await transactionReference(groupId: groupId)
.document(transactionId)
.getDocument(as: Transactions.self, source: .server)
}

func getTransactionsCount(groupId: String) async throws -> Int {
let count = try await transactionReference(groupId: groupId)
.whereField("is_active", isEqualTo: true)
.count.getAggregation(source: .server).count
return Int(truncating: count)
}
}
4 changes: 2 additions & 2 deletions Data/Data/Store/UserStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class UserStore: ObservableObject {
}
}

func fetchLatestUserBy(id: String) -> AsyncStream<AppUser?> {
func streamLatestUserBy(id: String) -> AsyncStream<AppUser?> {
AsyncStream { continuation in
let listener = usersCollection.document(id).addSnapshotListener { snapshot, error in
if let error {
Expand All @@ -63,7 +63,7 @@ class UserStore: ObservableObject {

guard let snapshot else {
LogE("UserStore: \(#function) Snapshot is nil for requested user.")
continuation.finish()
continuation.yield(nil)
return
}

Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,48 +60,48 @@ Splito is currently in active development, with exciting new features planned:
<tr>
<th width="32%"> Group List </th>
<th width="32%"> Group Expense List </th>
<th width="32%"> Invite Friends/Group Members </th>
<th width="32%"> Expense Add/Edit </th>
</tr>
<tr>
<td> <img src="./Screenshots/GroupList.png" /> </td>
<td> <img src="./Screenshots/GroupHome.png"/> </td>
<td> <img src="./Screenshots/InviteMember.png"/> </td>
<td> <img src="./Screenshots/AddExpense.png"/> </td>
</tr>
</table>
<table>
<tr>
<th width="32%"> Expense Add/Edit </th>
<th width="32%"> Expense Split Option </th>
<th width="32%"> Expense Detail </th>
<th width="32%"> Group Balance </th>
</tr>
<tr>
<td> <img src="./Screenshots/AddExpense.png"/> </td>
<td> <img src="./Screenshots/SplitOptions.png"/> </td>
<td> <img src="./Screenshots/ExpenseDetail.png"/> </td>
<td> <img src="./Screenshots/GroupBalance.png"/> </td>
</tr>
</table>
<table>
<tr>
<th width="32%"> Group Balance </th>
<th width="32%"> Group Summary </th>
<th width="32%"> Activity Log History </th>
<th width="32%"> Payment List </th>
</tr>
<tr>
<td> <img src="./Screenshots/GroupBalance.png"/> </td>
<td> <img src="./Screenshots/GroupSummary.png"/> </td>
<td> <img src="./Screenshots/ActivityLogs.png"/> </td>
<td> <img src="./Screenshots/Transactions.png"/> </td>
</tr>
</table>
<table>
<tr>
<th width="32%"> Payment List </th>
<th width="32%"> Payment Add/Edit </th>
<th width="32%"> Payment Detail </th>
<th> </th>
</tr>
<tr>
<td> <img src="./Screenshots/Transactions.png"/> </td>
<td> <img src="./Screenshots/GroupPayment.png"/> </td>
<td> <img src="./Screenshots/TransactionDetail.png"/> </td>
<td> </td>
</tr>
</table>

Expand Down
Binary file modified Screenshots/ActivityLogs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/AddExpense.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/ExpenseDetail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/GroupBalance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/GroupHome.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/GroupList.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/GroupPayment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/GroupSummary.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed Screenshots/InviteMember.png
Binary file not shown.
Binary file modified Screenshots/SplitOptions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/TransactionDetail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Screenshots/Transactions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Splito.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,8 @@
D85E86E62BB2E189002EDF76 /* ExpenseRouteView.swift */,
D85E86DF2BAB06A3002EDF76 /* AddExpenseView.swift */,
D85E86E22BAB06D9002EDF76 /* AddExpenseViewModel.swift */,
D85E86F02BB41CBA002EDF76 /* Detail Selection */,
D856C7302BCFD2080008A341 /* Expense Detail */,
D85E86F02BB41CBA002EDF76 /* Detail Selection */,
210CC0392CF6DA7F0035682E /* Note */,
);
path = Expense;
Expand Down
5 changes: 3 additions & 2 deletions Splito/UI/Home/Account/AccountHomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ class AccountHomeViewModel: BaseViewModel, ObservableObject {
func handleLogoutBtnTap() {
alert = .init(title: "See you soon!", message: "Are you sure you want to sign out?",
positiveBtnTitle: "Sign out",
positiveBtnAction: { self.performLogoutAction() },
positiveBtnAction: { [weak self] in self?.performLogoutAction() },
negativeBtnTitle: "Cancel",
negativeBtnAction: { self.showAlert = false }, isPositiveBtnDestructive: true)
negativeBtnAction: { [weak self] in self?.showAlert = false },
isPositiveBtnDestructive: true)
showAlert = true
}

Expand Down
13 changes: 5 additions & 8 deletions Splito/UI/Home/Account/User Profile/UserProfileViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ public class UserProfileViewModel: BaseViewModel, ObservableObject {
}
},
negativeBtnTitle: "Cancel",
negativeBtnAction: { self.showAlert = false }, isPositiveBtnDestructive: true)
negativeBtnAction: { [weak self] in self?.showAlert = false },
isPositiveBtnDestructive: true)
showAlert = true
}

Expand All @@ -210,13 +211,9 @@ public class UserProfileViewModel: BaseViewModel, ObservableObject {
alert = .init(
title: "", message: error.localizedDescription,
positiveBtnTitle: "Reauthenticate",
positiveBtnAction: { [weak self] in
self?.reAuthenticateUser()
}, negativeBtnTitle: "Cancel",
negativeBtnAction: { [weak self] in
self?.showAlert = false
}
)
positiveBtnAction: { [weak self] in self?.reAuthenticateUser() },
negativeBtnTitle: "Cancel",
negativeBtnAction: { [weak self] in self?.showAlert = false })
showAlert = true
} else {
showToastForError()
Expand Down
7 changes: 2 additions & 5 deletions Splito/UI/Home/ActivityLog/ActivityLogViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,11 @@ class ActivityLogViewModel: BaseViewModel, ObservableObject {
// Listens real-time updates and returns the latest activity logs for the current user
private func fetchLatestActivityLogs() {
guard let userId = preference.user?.id else { return }
let activityLogStream = activityLogRepository.streamLatestActivityLogs(userId: userId)

task?.cancel() // Cancel the existing task if it's running
task = Task { [weak self] in
guard let self else { return }
let activityLogStream = self.activityLogRepository.fetchLatestActivityLogs(userId: userId)
for await activityLogs in activityLogStream {
guard !Task.isCancelled else { return } // Exit early if the task is cancelled

guard let self else { return }
if let activityLogs {
for activityLog in activityLogs where !(self.activityLogs.contains(where: { $0.id == activityLog.id })) {
self.activityLogs.append(activityLog)
Expand Down
25 changes: 11 additions & 14 deletions Splito/UI/Home/Expense/AddExpenseView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,14 @@ struct AddExpenseView: View {
handleActionSelection: viewModel.handleActionSelection(_:))
}
}
.task { focusedField = .expenseName }
.onDisappear { focusedField = nil }
.background(surfaceColor)
.scrollDismissesKeyboard(.immediately)
.navigationTitle(viewModel.expenseId == nil ? "Add expense" : "Edit expense")
.navigationBarTitleDisplayMode(.inline)
.toastView(toast: $viewModel.toast)
.alertView.alert(isPresented: $viewModel.showAlert, alertStruct: viewModel.alert)
.onAppear {
focusedField = .expenseName
}
.sheet(isPresented: $viewModel.showGroupSelection) {
NavigationStack {
SelectGroupView(viewModel: SelectGroupViewModel(selectedGroup: viewModel.selectedGroup,
Expand Down Expand Up @@ -167,17 +166,15 @@ private struct ExpenseDetailRow: View {

VStack(alignment: .leading, spacing: 0) {
if field == .expenseName {
TextField("Enter a description", text: $name)
.font(.subTitle2())
.foregroundStyle(primaryText)
.keyboardType(.default)
.tint(primaryColor)
.focused(focusedField, equals: field)
.textInputAutocapitalization(.sentences)
.submitLabel(.next)
.onSubmit {
focusedField.wrappedValue = .amount
}
TextField("Enter a description", text: $name, onCommit: {
focusedField.wrappedValue = .amount
})
.font(.subTitle2())
.tint(primaryColor)
.foregroundStyle(primaryText)
.focused(focusedField, equals: field)
.textInputAutocapitalization(.sentences)
.submitLabel(.next)
} else {
HStack(spacing: 16) {
Text(inputValue.localized)
Expand Down
Loading

0 comments on commit b4dd71f

Please sign in to comment.