From 2d7eb2a2841c732a622ca279cc0693d08e1ebad4 Mon Sep 17 00:00:00 2001 From: JH713 Date: Tue, 26 Mar 2024 20:58:27 +0900 Subject: [PATCH 1/3] feat: set favorite bookmark --- iBox/Sources/AppDelegate.swift | 10 ++++- iBox/Sources/BoxList/BoxListView.swift | 10 +++-- .../BoxList/BoxListViewController.swift | 1 + iBox/Sources/BoxList/BoxListViewModel.swift | 38 +++++++++++++++++-- iBox/Sources/Shared/CoreDataManager.swift | 9 +++++ iBox/Sources/Shared/UserDefaultsManager.swift | 5 +-- iBox/Sources/Shared/WebViewPreloader.swift | 20 ++++++++-- 7 files changed, 79 insertions(+), 14 deletions(-) diff --git a/iBox/Sources/AppDelegate.swift b/iBox/Sources/AppDelegate.swift index 151f061..bf06e1b 100644 --- a/iBox/Sources/AppDelegate.swift +++ b/iBox/Sources/AppDelegate.swift @@ -26,8 +26,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } private func preloadFavoriteWeb() { - let favorite = UserDefaultsManager.favorite - let favoriteUrl = favorite.url + let favoriteId = UserDefaultsManager.favoriteId + var favoriteUrl: URL? = nil + if let favoriteId { + favoriteUrl = CoreDataManager.shared.getBookmarkUrl(favoriteId) + if favoriteUrl == nil { + UserDefaultsManager.favoriteId = nil + } + } WebViewPreloader.shared.preloadFavoriteView(url: favoriteUrl) } diff --git a/iBox/Sources/BoxList/BoxListView.swift b/iBox/Sources/BoxList/BoxListView.swift index 967479b..baeff51 100644 --- a/iBox/Sources/BoxList/BoxListView.swift +++ b/iBox/Sources/BoxList/BoxListView.swift @@ -190,12 +190,16 @@ extension BoxListView: UITableViewDelegate { func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { // 액션 정의 - let favoriteAction = UIContextualAction(style: .normal, title: "favorite", handler: {(action, view, completionHandler) in - self.viewModel?.input.send(.setFavorite(indexPath: indexPath)) + let favoriteAction = UIContextualAction(style: .normal, title: "favorite", handler: { [weak self] (action, view, completionHandler) in + self?.viewModel?.input.send(.toggleFavorite(indexPath: indexPath)) completionHandler(true) }) favoriteAction.backgroundColor = .box2 - favoriteAction.image = UIImage(systemName: "heart") + if viewModel?.isFavoriteBookmark(at: indexPath) == true { + favoriteAction.image = UIImage(systemName: "heart.fill") + } else { + favoriteAction.image = UIImage(systemName: "heart") + } let shareAction = UIContextualAction(style: .normal, title: "share", handler: {(action, view, completionHandler) in let cellViewModel = self.viewModel?.viewModel(at: indexPath) diff --git a/iBox/Sources/BoxList/BoxListViewController.swift b/iBox/Sources/BoxList/BoxListViewController.swift index f0823d3..46c5376 100644 --- a/iBox/Sources/BoxList/BoxListViewController.swift +++ b/iBox/Sources/BoxList/BoxListViewController.swift @@ -84,6 +84,7 @@ extension BoxListViewController: BoxListViewDelegate { guard let newName = controller.textFields?.first?.text else { return } guard let newUrlString = controller.textFields?.last?.text, let newUrl = URL(string: newUrlString) else { return } + guard let contentView = self?.contentView as? BoxListView else { return } contentView.viewModel?.editBookmark(at: indexPath, name: newName, url: newUrl) } diff --git a/iBox/Sources/BoxList/BoxListViewModel.swift b/iBox/Sources/BoxList/BoxListViewModel.swift index dfd5c83..fd7a820 100644 --- a/iBox/Sources/BoxList/BoxListViewModel.swift +++ b/iBox/Sources/BoxList/BoxListViewModel.swift @@ -15,8 +15,11 @@ class BoxListViewModel { var folders: [Folder] { boxList.map{ $0.folder } } + var sectionsToReload = Set() var isEditing = false + var favoriteId: UUID? = nil + enum Input { case toggleEditStatus @@ -24,7 +27,7 @@ class BoxListViewModel { case viewWillAppear case folderTapped(section: Int) case deleteBookmark(indexPath: IndexPath) - case setFavorite(indexPath: IndexPath) + case toggleFavorite(indexPath: IndexPath) case moveBookmark(from: IndexPath, to: IndexPath) case openFolderIfNeeded(folderIndex: Int) } @@ -50,6 +53,7 @@ class BoxListViewModel { case .viewDidLoad: let folders = CoreDataManager.shared.getFolders() boxList = folders.map{ BoxListSectionViewModel(folder: $0) } + favoriteId = UserDefaultsManager.favoriteId case .viewWillAppear: output.send(.sendBoxList(boxList: boxList)) if !sectionsToReload.isEmpty { @@ -61,8 +65,8 @@ class BoxListViewModel { output.send(.sendBoxList(boxList: boxList)) case let .deleteBookmark(indexPath): deleteBookmark(at: indexPath) - case let .setFavorite(indexPath): - print("\(viewModel(at: indexPath).name) favorite 할게용") + case let .toggleFavorite(indexPath): + toggleFavorite(at: indexPath) case .moveBookmark(from: let from, to: let to): reorderBookmark(srcIndexPath: from, destIndexPath: to) case .openFolderIfNeeded(folderIndex: let folderIndex): @@ -76,6 +80,34 @@ class BoxListViewModel { return boxList[indexPath.section].boxListCellViewModels[indexPath.row] } + func isFavoriteBookmark(at indexPath: IndexPath) -> Bool { + if let favoriteId { + if favoriteId == bookmark(at: indexPath).id { + return true + } else { return false } + } else { + return false + } + } + + private func toggleFavorite(at indexPath: IndexPath) { + let bookmark = boxList[indexPath.section].viewModel(at: indexPath.row) + if let prevId = favoriteId { + if prevId == bookmark.id { // 지금 들어온게 즐겨찾기면 지워야 + WebViewPreloader.shared.setFavoriteUrl(url: nil) + favoriteId = nil + UserDefaultsManager.favoriteId = nil + } else { + WebViewPreloader.shared.setFavoriteUrl(url: bookmark.url) + favoriteId = bookmark.id + } + } else { + WebViewPreloader.shared.setFavoriteUrl(url: bookmark.url) + favoriteId = bookmark.id + } + UserDefaultsManager.favoriteId = favoriteId + } + func bookmark(at indexPath: IndexPath) -> Bookmark { return boxList[indexPath.section].viewModel(at: indexPath.row).bookmark } diff --git a/iBox/Sources/Shared/CoreDataManager.swift b/iBox/Sources/Shared/CoreDataManager.swift index 411e516..28ef1c2 100644 --- a/iBox/Sources/Shared/CoreDataManager.swift +++ b/iBox/Sources/Shared/CoreDataManager.swift @@ -199,6 +199,15 @@ extension CoreDataManager { // 북마크 관련 extension CoreDataManager { + func getBookmarkUrl(_ bookmarkId: UUID) -> URL? { + let entity = getBookmarkEntity(id: bookmarkId) + if let entity { + return entity.url + } else { + return nil + } + } + func addBookmark(_ bookmark: Bookmark, folderId: UUID) { let context = persistentContainer.viewContext diff --git a/iBox/Sources/Shared/UserDefaultsManager.swift b/iBox/Sources/Shared/UserDefaultsManager.swift index 48e785d..0ef8775 100644 --- a/iBox/Sources/Shared/UserDefaultsManager.swift +++ b/iBox/Sources/Shared/UserDefaultsManager.swift @@ -12,8 +12,8 @@ final class UserDefaultsManager { @UserDefaultsData(key: "theme", defaultValue: Theme.system) static var theme: Theme - @UserDefaultsData(key: "favorite", defaultValue: Bookmark(id: UUID(), name: "42 Intra", url: URL(string: "https://profile.intra.42.fr/")!)) - static var favorite: Bookmark + @UserDefaultsData(key: "favoriteId", defaultValue: nil) + static var favoriteId: UUID? @UserDefaultsData(key: "homeTabIndex", defaultValue: 0) static var homeTabIndex: Int @@ -23,7 +23,6 @@ final class UserDefaultsManager { @UserDefaultsData(key: "isPreload", defaultValue: false) static var isPreload: Bool - } @propertyWrapper diff --git a/iBox/Sources/Shared/WebViewPreloader.swift b/iBox/Sources/Shared/WebViewPreloader.swift index 79082d6..554f159 100644 --- a/iBox/Sources/Shared/WebViewPreloader.swift +++ b/iBox/Sources/Shared/WebViewPreloader.swift @@ -12,6 +12,7 @@ class WebViewPreloader { static let shared = WebViewPreloader() private var webView: WKWebView? private var favoriteView: (url: URL, webView: WKWebView)? + private var defaultUrl = URL(string: "https://profile.intra.42.fr/")! private init() {} @@ -22,11 +23,11 @@ class WebViewPreloader { self.webView = webView } - func preloadFavoriteView(url: URL) { + func preloadFavoriteView(url: URL?) { let webView = WKWebView() webView.isOpaque = false - webView.load(URLRequest(url: url)) - favoriteView = (url, webView) + webView.load(URLRequest(url: url ?? defaultUrl)) + favoriteView = (url ?? defaultUrl, webView) } func getWebView() -> WKWebView? { @@ -45,5 +46,18 @@ class WebViewPreloader { guard let favoriteView else { return } favoriteView.webView.load(URLRequest(url: favoriteView.url)) } + + func setFavoriteUrl(url: URL?) { + if let favoriteView { + if url == favoriteView.url { + return + } else { + self.favoriteView?.url = url ?? defaultUrl + resetFavoriteView() + } + } else { + preloadFavoriteView(url: url) + } + } } From d9dc0c848ce3f856ff85fd9b9c01a0afdfb97838 Mon Sep 17 00:00:00 2001 From: JH713 Date: Tue, 26 Mar 2024 21:06:31 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20default=20=EC=8B=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iBox/Sources/Shared/WebViewPreloader.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iBox/Sources/Shared/WebViewPreloader.swift b/iBox/Sources/Shared/WebViewPreloader.swift index 554f159..4f88167 100644 --- a/iBox/Sources/Shared/WebViewPreloader.swift +++ b/iBox/Sources/Shared/WebViewPreloader.swift @@ -49,7 +49,8 @@ class WebViewPreloader { func setFavoriteUrl(url: URL?) { if let favoriteView { - if url == favoriteView.url { + if url == favoriteView.url || + (url == nil && favoriteView.url == defaultUrl ) { return } else { self.favoriteView?.url = url ?? defaultUrl From 394a35cd66148c2048a5c6f06559421c989ba768 Mon Sep 17 00:00:00 2001 From: JH713 Date: Thu, 28 Mar 2024 14:41:07 +0900 Subject: [PATCH 3/3] feat: reset data --- iBox/Sources/BoxList/BoxListView.swift | 13 ++++++ .../Settings/Reset/ResetViewController.swift | 44 ++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/iBox/Sources/BoxList/BoxListView.swift b/iBox/Sources/BoxList/BoxListView.swift index baeff51..7b3c2cf 100644 --- a/iBox/Sources/BoxList/BoxListView.swift +++ b/iBox/Sources/BoxList/BoxListView.swift @@ -52,12 +52,17 @@ class BoxListView: UIView { setupLayout() configureDataSource() bindViewModel() + subscribeToNotifications() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + deinit { + NotificationCenter.default.removeObserver(self) + } + // MARK: - Setup Methods private func setupProperty() { @@ -141,6 +146,14 @@ class BoxListView: UIView { }.store(in: &cancellables) } + private func subscribeToNotifications() { + NotificationCenter.default.addObserver(self, selector: #selector(dataDidReset), name: .didResetData, object: nil) + } + + @objc func dataDidReset(notification: NSNotification) { + viewModel?.input.send(.viewDidLoad) + } + } extension BoxListView: UITableViewDelegate { diff --git a/iBox/Sources/Settings/Reset/ResetViewController.swift b/iBox/Sources/Settings/Reset/ResetViewController.swift index d593220..dfa30af 100644 --- a/iBox/Sources/Settings/Reset/ResetViewController.swift +++ b/iBox/Sources/Settings/Reset/ResetViewController.swift @@ -37,22 +37,56 @@ extension ResetViewController: ResetViewDelegate { func showAlert() { let alertController = UIAlertController(title: "경고", message: "이 작업은 되돌릴 수 없습니다. 계속하려면 \"iBox\"라고 입력해 주세요.", preferredStyle: .alert) - alertController.addTextField() let cancelAction = UIAlertAction(title: "취소", style: .cancel, handler: nil) alertController.addAction(cancelAction) - let confirmAction = UIAlertAction(title: "확인", style: .default) { _ in + let confirmAction = UIAlertAction(title: "확인", style: .default) { [weak self] _ in if let textField = alertController.textFields?.first, let text = textField.text, text == "iBox" { - print("정말로 초기화를 해버렷당") - self.navigationController?.popViewController(animated: true) + self?.resetData() + self?.navigationController?.popViewController(animated: true) } else { - self.showAlert() + self?.showAlert() } } + confirmAction.isEnabled = false alertController.addAction(confirmAction) + alertController.addTextField() { textField in + NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: textField, queue: OperationQueue.main, using: + {_ in + let isTextMatch = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "iBox" + + confirmAction.isEnabled = isTextMatch + }) + + } + self.present(alertController, animated: true, completion: nil) } + private func resetData() { + let defaultData = [ + Folder(id: UUID(), name: "42 폴더", bookmarks: [ + Bookmark(id: UUID(), name: "42 Intra", url: URL(string: "https://profile.intra.42.fr/")!), + Bookmark(id: UUID(), name: "42Where", url: URL(string: "https://www.where42.kr/")! ), + Bookmark(id: UUID(), name: "42Stat", url: URL(string: "https://stat.42seoul.kr/")!), + Bookmark(id: UUID(), name: "집현전", url: URL(string: "https://42library.kr/")!), + Bookmark(id: UUID(), name: "Cabi", url: URL(string: "https://cabi.42seoul.io/")!), + Bookmark(id: UUID(), name: "24HANE", url: URL(string: "https://24hoursarenotenough.42seoul.kr/")!) + ]) + ] + CoreDataManager.shared.deleteAllFolders() + CoreDataManager.shared.addInitialFolders(defaultData) + UserDefaultsManager.isDefaultDataInserted = true + + UserDefaultsManager.favoriteId = nil + WebViewPreloader.shared.setFavoriteUrl(url: nil) + NotificationCenter.default.post(name: .didResetData, object: nil) + } + +} + +extension Notification.Name { + static let didResetData = Notification.Name("didResetData") }