Skip to content
Merged
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
8 changes: 8 additions & 0 deletions DevLog/App/Assembler/DomainAssembler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ private extension DomainAssembler {
DeletePushNotificationUseCaseImpl(container.resolve(PushNotificationRepository.self))
}

container.register(UndoDeletePushNotificationUseCase.self) {
UndoDeletePushNotificationUseCaseImpl(container.resolve(PushNotificationRepository.self))
}

container.register(FetchPushNotificationsUseCase.self) {
FetchPushNotificationsUseCaseImpl(container.resolve(PushNotificationRepository.self))
}
Expand All @@ -116,6 +120,10 @@ private extension DomainAssembler {
container.register(DeleteWebPageUseCase.self) {
DeleteWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
}

container.register(UndoDeleteWebPageUseCase.self) {
UndoDeleteWebPageUseCaseImpl(container.resolve(WebPageRepository.self))
}
}

func registerUserPreferencesUseCases(_ container: DIContainer) {
Expand Down
4 changes: 4 additions & 0 deletions DevLog/Data/Repository/PushNotificationRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ final class PushNotificationRepositoryImpl: PushNotificationRepository {
try await service.deleteNotification(notificationID)
}

func undoDeleteNotification(_ notificationID: String) async throws {
try await service.undoDeleteNotification(notificationID)
}

// 푸시 알림 읽음/안읽음 토글
func toggleNotificationRead(_ todoId: String) async throws {
try await service.toggleNotificationRead(todoId)
Expand Down
4 changes: 4 additions & 0 deletions DevLog/Data/Repository/WebPageRepositoryImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ final class WebPageRepositoryImpl: WebPageRepository {
try await webPageService.deleteWebPage(urlString)
await metadataService.removeCachedImage(for: urlString)
}

func undoDelete(_ urlString: String) async throws {
try await webPageService.undoDeleteWebPage(urlString)
}
}

private extension WebPageRepositoryImpl {
Expand Down
1 change: 1 addition & 0 deletions DevLog/Domain/Protocol/PushNotificationRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ protocol PushNotificationRepository {
limit: Int
) throws -> AnyPublisher<PushNotificationPage, Error>
func deleteNotification(_ notificationID: String) async throws
func undoDeleteNotification(_ notificationID: String) async throws
func toggleNotificationRead(_ todoId: String) async throws
}
1 change: 1 addition & 0 deletions DevLog/Domain/Protocol/WebPageRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ protocol WebPageRepository {
func fetch(_ query: String) async throws -> [WebPage]
func upsert(_ urlString: String) async throws
func delete(_ urlString: String) async throws
func undoDelete(_ urlString: String) async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// UndoDeletePushNotificationUseCase.swift
// DevLog
//
// Created by opfic on 3/16/26.
//

protocol UndoDeletePushNotificationUseCase {
func execute(_ notificationID: String) async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// UndoDeletePushNotificationUseCaseImpl.swift
// DevLog
//
// Created by opfic on 3/16/26.
//

final class UndoDeletePushNotificationUseCaseImpl: UndoDeletePushNotificationUseCase {
private let repository: PushNotificationRepository

init(_ repository: PushNotificationRepository) {
self.repository = repository
}

func execute(_ notificationID: String) async throws {
try await repository.undoDeleteNotification(notificationID)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// UndoDeleteWebPageUseCase.swift
// DevLog
//
// Created by opfic on 3/16/26.
//

protocol UndoDeleteWebPageUseCase {
func execute(_ urlString: String) async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// UndoDeleteWebPageUseCaseImpl.swift
// DevLog
//
// Created by opfic on 3/16/26.
//

final class UndoDeleteWebPageUseCaseImpl: UndoDeleteWebPageUseCase {
private let repository: WebPageRepository

init(_ repository: WebPageRepository) {
self.repository = repository
}

func execute(_ urlString: String) async throws {
try await repository.undoDelete(urlString)
}
}
26 changes: 22 additions & 4 deletions DevLog/Infra/Service/PushNotificationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@
import FirebaseAuth
import Combine
import FirebaseFirestore
import FirebaseFunctions

final class PushNotificationService {
private enum FunctionName: String {
case requestPushNotificationDeletion
case undoPushNotificationDeletion
}

private let store = Firestore.firestore()
private let functions = Functions.functions(region: "asia-northeast3")
private let logger = Logger(category: "PushNotificationService")

/// 푸시 알림 On/Off 설정
Expand Down Expand Up @@ -174,13 +181,24 @@ final class PushNotificationService {
/// 푸시 알림 기록 삭제
func deleteNotification(_ notificationID: String) async throws {
do {
guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated }
guard Auth.auth().currentUser?.uid != nil else { throw AuthError.notAuthenticated }

let docRef = store.collection("users/\(uid)/notifications").document(notificationID)
let function = functions.httpsCallable(FunctionName.requestPushNotificationDeletion)
_ = try await function.call(["notificationId": notificationID])
} catch {
logger.error("Failed to request notification deletion", error: error)
throw error
}
}

func undoDeleteNotification(_ notificationID: String) async throws {
do {
guard Auth.auth().currentUser?.uid != nil else { throw AuthError.notAuthenticated }

try await docRef.delete()
let function = functions.httpsCallable(FunctionName.undoPushNotificationDeletion)
_ = try await function.call(["notificationId": notificationID])
} catch {
logger.error("Failed to delete notification", error: error)
logger.error("Failed to undo notification deletion", error: error)
throw error
}
}
Expand Down
43 changes: 35 additions & 8 deletions DevLog/Infra/Service/WebPageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@

import FirebaseAuth
import FirebaseFirestore
import FirebaseFunctions

final class WebPageService {
private enum FunctionName: String {
case requestWebPageDeletion
case undoWebPageDeletion
}

private let store = Firestore.firestore()
private let functions = Functions.functions(region: "asia-northeast3")
private let encoder = Firestore.Encoder()
private let logger = Logger(category: "WebPageService")

Expand Down Expand Up @@ -67,21 +74,37 @@ final class WebPageService {
}

func deleteWebPage(_ urlString: String) async throws {
logger.info("Deleting web page: \(urlString)")
logger.info("Requesting web page deletion: \(urlString)")

guard let uid = Auth.auth().currentUser?.uid else {
guard Auth.auth().currentUser?.uid != nil else {
logger.error("User not authenticated")
throw AuthError.notAuthenticated
}

do {
let function = functions.httpsCallable(FunctionName.requestWebPageDeletion)
_ = try await function.call(["urlString": urlString])
logger.info("Successfully requested web page deletion")
} catch {
logger.error("Failed to request web page deletion", error: error)
throw error
}
}

func undoDeleteWebPage(_ urlString: String) async throws {
logger.info("Undoing web page deletion: \(urlString)")

guard Auth.auth().currentUser?.uid != nil else {
logger.error("User not authenticated")
throw AuthError.notAuthenticated
}

do {
let documentID = documentID(for: urlString)
let docRef = store
.document("users/\(uid)/webPages/\(documentID)")
try await docRef.delete()
logger.info("Successfully deleted web page")
let function = functions.httpsCallable(FunctionName.undoWebPageDeletion)
_ = try await function.call(["urlString": urlString])
logger.info("Successfully undone web page deletion")
} catch {
logger.error("Failed to delete web page", error: error)
logger.error("Failed to undo web page deletion", error: error)
throw error
}
}
Expand All @@ -101,6 +124,9 @@ final class WebPageService {
private extension WebPageService {
func makeResponse(from snapshot: QueryDocumentSnapshot) -> WebPageResponse? {
let data = snapshot.data()
if data[WebPageFieldKey.deletingAt.rawValue] is Timestamp {
return nil
}
guard
let title = data[WebPageFieldKey.title.rawValue] as? String,
let url = data[WebPageFieldKey.url.rawValue] as? String,
Expand All @@ -123,5 +149,6 @@ private extension WebPageService {
case url
case displayURL
case imageURL
case deletingAt // 삭제 요청은 되었지만, 5초 유예 후 최종 삭제되기 전 상태
}
}
Loading