Skip to content

Commit

Permalink
Merge pull request #911 from TortugaPower/detailsview-sections
Browse files Browse the repository at this point in the history
Add separate author section in details screen
  • Loading branch information
GianniCarlo committed Jun 12, 2023
2 parents b5f53de + 6823925 commit 399dcd9
Show file tree
Hide file tree
Showing 39 changed files with 160 additions and 189 deletions.
4 changes: 0 additions & 4 deletions BookPlayer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,6 @@
9F5F13682978D9E100F061A0 /* ProfileSyncTasksStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5F13672978D9E100F061A0 /* ProfileSyncTasksStatusView.swift */; };
9F5FBB08293EDCD8009F4B0E /* ItemDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5FBB07293EDCD8009F4B0E /* ItemDetailsViewController.swift */; };
9F5FBB0A293EE0C2009F4B0E /* ItemDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5FBB09293EE0C2009F4B0E /* ItemDetailsViewModel.swift */; };
9F5FBB0E293EE116009F4B0E /* ItemDetailsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F5FBB0D293EE116009F4B0E /* ItemDetailsCoordinator.swift */; };
9F646006283AD6B700710D3C /* PlayerSettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F646005283AD6B700710D3C /* PlayerSettingsViewModelTests.swift */; };
9F64C6212793C31600B2493C /* PlayerControlsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F64C6202793C31600B2493C /* PlayerControlsCoordinator.swift */; };
9F64C6242793C3DA00B2493C /* PlayerControlsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F64C6232793C3DA00B2493C /* PlayerControlsViewModel.swift */; };
Expand Down Expand Up @@ -1037,7 +1036,6 @@
9F5F13672978D9E100F061A0 /* ProfileSyncTasksStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSyncTasksStatusView.swift; sourceTree = "<group>"; };
9F5FBB07293EDCD8009F4B0E /* ItemDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsViewController.swift; sourceTree = "<group>"; };
9F5FBB09293EE0C2009F4B0E /* ItemDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsViewModel.swift; sourceTree = "<group>"; };
9F5FBB0D293EE116009F4B0E /* ItemDetailsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailsCoordinator.swift; sourceTree = "<group>"; };
9F646005283AD6B700710D3C /* PlayerSettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerSettingsViewModelTests.swift; sourceTree = "<group>"; };
9F64C6202793C31600B2493C /* PlayerControlsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsCoordinator.swift; sourceTree = "<group>"; };
9F64C6232793C3DA00B2493C /* PlayerControlsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1376,7 +1374,6 @@
4158386826EAF4A300F4A12B /* ItemListCoordinator.swift */,
4158388226EBD76A00F4A12B /* LibraryListCoordinator.swift */,
4158386D26EAF76700F4A12B /* FolderListCoordinator.swift */,
9F5FBB0D293EE116009F4B0E /* ItemDetailsCoordinator.swift */,
4158388426EBEE9800F4A12B /* PlayerCoordinator.swift */,
4158388A26EC2CC500F4A12B /* ImportCoordinator.swift */,
41188D1F26ECDDD50017124E /* BookmarkCoordinator.swift */,
Expand Down Expand Up @@ -2985,7 +2982,6 @@
9FBDDBA227DD13FA005FB447 /* ProfileCoordinator.swift in Sources */,
9F22DE39288CBE6A00056FCD /* FormButton.swift in Sources */,
4151A6A826E48C3400E49DBE /* Storyboarded.swift in Sources */,
9F5FBB0E293EE116009F4B0E /* ItemDetailsCoordinator.swift in Sources */,
9F00A5FC294F9B9A005EA316 /* ItemDetailsForm.swift in Sources */,
9F7B647C2804798800895ECC /* IconsViewModel.swift in Sources */,
9F3D0CE728C2BF7700E9E8A3 /* ButtonFreeViewModel.swift in Sources */,
Expand Down
4 changes: 2 additions & 2 deletions BookPlayer/Base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@
"search_title" = "Search";
"books_title" = "Books";
"folders_title" = "Folders";
"item_title_placeholder" = "Title";
"item_author_placeholder" = "Author";
"section_item_title" = "Title";
"section_item_author" = "Author";
"artwork_options_title" = "Artwork options";
"artwork_photolibrary_title" = "Choose from Photo Library";
"artwork_clipboard_title" = "Paste from clipboard";
Expand Down
69 changes: 0 additions & 69 deletions BookPlayer/Coordinators/ItemDetailsCoordinator.swift

This file was deleted.

22 changes: 14 additions & 8 deletions BookPlayer/Coordinators/ItemListCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,21 +172,27 @@ extension ItemListCoordinator {
}

func showItemDetails(_ item: SimpleLibraryItem) {
let coordinator = ItemDetailsCoordinator(
let viewModel = ItemDetailsViewModel(
item: item,
libraryService: libraryService,
syncService: syncService,
navigationController: navigationController
syncService: syncService
)

coordinator.onFinish = { route in
viewModel.onTransition = { [weak self] route in
switch route {
case .infoUpdated:
self.reloadItemsWithPadding()
case .done:
self?.reloadItemsWithPadding()
case .cancel:
/// do nothing on cancel
break
}
self?.navigationController.dismiss(animated: true)
}

coordinator.start()
let vc = ItemDetailsViewController(viewModel: viewModel)
let nav = AppNavigationController.instantiate(from: .Main)
nav.viewControllers = [vc]

navigationController.present(nav, animated: true, completion: nil)
}

func showItemSelectionScreen(
Expand Down
10 changes: 3 additions & 7 deletions BookPlayer/Generated/AutoMockable.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1469,22 +1469,18 @@ class SyncServiceProtocolMock: SyncServiceProtocol {
}
//MARK: - scheduleUpload

var scheduleUploadItemsThrowableError: Error?
var scheduleUploadItemsCallsCount = 0
var scheduleUploadItemsCalled: Bool {
return scheduleUploadItemsCallsCount > 0
}
var scheduleUploadItemsReceivedItems: [SimpleLibraryItem]?
var scheduleUploadItemsReceivedInvocations: [[SimpleLibraryItem]] = []
var scheduleUploadItemsClosure: (([SimpleLibraryItem]) throws -> Void)?
func scheduleUpload(items: [SimpleLibraryItem]) throws {
if let error = scheduleUploadItemsThrowableError {
throw error
}
var scheduleUploadItemsClosure: (([SimpleLibraryItem]) -> Void)?
func scheduleUpload(items: [SimpleLibraryItem]) {
scheduleUploadItemsCallsCount += 1
scheduleUploadItemsReceivedItems = items
scheduleUploadItemsReceivedInvocations.append(items)
try scheduleUploadItemsClosure?(items)
scheduleUploadItemsClosure?(items)
}
//MARK: - scheduleDelete

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct ClearableTextField: View {
/// Input's text
@Binding var text: String
/// Current theme
@StateObject var themeViewModel = ThemeViewModel()
@EnvironmentObject var themeViewModel: ThemeViewModel

init(_ placeholder: String, text: Binding<String>) {
self.placeholder = placeholder
Expand All @@ -38,5 +38,6 @@ struct ClearableTextField: View {
struct ClearableTextField_Previews: PreviewProvider {
static var previews: some View {
ClearableTextField("Title", text: .constant(""))
.environmentObject(ThemeViewModel())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class ItemDetailsFormViewModel: ObservableObject {
@Published var author: String
/// Artwork image
@Published var selectedImage: UIImage?
/// Original item title
var titlePlaceholder: String
/// Original item author
var authorPlaceholder: String
/// Determines if there's an update for the artwork
var artworkIsUpdated: Bool = false
/// Flag to show the author field
Expand All @@ -28,7 +32,9 @@ class ItemDetailsFormViewModel: ObservableObject {
/// Initializer
init(item: SimpleLibraryItem) {
self.title = item.title
self.titlePlaceholder = item.title
self.author = item.details
self.authorPlaceholder = item.details
self.showAuthor = item.type == .book
self.originalImageDataProvider = ArtworkService.getArtworkProvider(for: item.relativePath)
let cachedImageURL = ArtworkService.getCachedImageURL(for: item.relativePath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SwiftUI
import Themeable
import UIKit

class ItemDetailsViewController: BaseViewController<ItemDetailsCoordinator, ItemDetailsViewModel> {
class ItemDetailsViewController: BaseViewController<Coordinator, ItemDetailsViewModel> {
// MARK: - UI components

private lazy var detailsView: UIView = {
Expand Down
86 changes: 56 additions & 30 deletions BookPlayer/Library/ItemDetails Screen/ItemDetailsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import BookPlayerKit
import Combine

class ItemDetailsViewModel: BaseViewModel<ItemDetailsCoordinator> {
class ItemDetailsViewModel: BaseViewModel<Coordinator> {
/// Possible routes for the screen
enum Routes {
case cancel
Expand Down Expand Up @@ -57,39 +57,18 @@ class ItemDetailsViewModel: BaseViewModel<ItemDetailsCoordinator> {
onTransition?(.cancel)
}

// swiftlint:disable:next function_body_length
func handleSaveAction() {
var cacheKey = item.relativePath
let cacheKey: String

let storedTitle = libraryService.getItemProperty(
#keyPath(LibraryItem.title),
relativePath: item.relativePath
) as? String

if storedTitle != formViewModel.title {
switch item.type {
case .book:
libraryService.renameBook(at: item.relativePath, with: formViewModel.title)
case .bound, .folder:
do {
let newRelativePath = try libraryService.renameFolder(at: item.relativePath, with: formViewModel.title)
cacheKey = newRelativePath
syncService.scheduleRenameFolder(at: item.relativePath, name: formViewModel.title)
} catch {
sendEvent(.showAlert(content: BPAlertContent.errorAlert(message: error.localizedDescription)))
return
}
}
do {
cacheKey = try updateTitle(formViewModel.title, relativePath: item.relativePath)
} catch {
sendEvent(.showAlert(content: BPAlertContent.errorAlert(message: error.localizedDescription)))
return
}

let storedDetails = libraryService.getItemProperty(
#keyPath(LibraryItem.details),
relativePath: item.relativePath
) as? String

if formViewModel.showAuthor,
storedDetails != formViewModel.author {
libraryService.updateDetails(at: item.relativePath, details: formViewModel.author)
if formViewModel.showAuthor {
updateAuthor(formViewModel.author, relativePath: item.relativePath)
}

guard formViewModel.artworkIsUpdated else {
Expand Down Expand Up @@ -120,6 +99,53 @@ class ItemDetailsViewModel: BaseViewModel<ItemDetailsCoordinator> {
}
}

/// Update the item title if necessary
/// - Returns: The new relative path to be used as the cache key
func updateTitle(_ newTitle: String, relativePath: String) throws -> String {
var cacheKey = relativePath
let cleanedTitle = newTitle.trimmingCharacters(in: .whitespacesAndNewlines)

guard !cleanedTitle.isEmpty else {
return cacheKey
}

let storedTitle = libraryService.getItemProperty(
#keyPath(LibraryItem.title),
relativePath: relativePath
) as? String

guard storedTitle != cleanedTitle else {
return cacheKey
}

switch item.type {
case .book:
libraryService.renameBook(at: relativePath, with: cleanedTitle)
case .bound, .folder:
let newRelativePath = try libraryService.renameFolder(at: relativePath, with: cleanedTitle)
cacheKey = newRelativePath
syncService.scheduleRenameFolder(at: relativePath, name: cleanedTitle)
}

return cacheKey
}

/// Update the item's author if necessary
func updateAuthor(_ newAuthor: String, relativePath: String) {
let cleanedAuthor = newAuthor.trimmingCharacters(in: .whitespacesAndNewlines)

guard !cleanedAuthor.isEmpty else { return }

let storedDetails = libraryService.getItemProperty(
#keyPath(LibraryItem.details),
relativePath: relativePath
) as? String

guard storedDetails != cleanedAuthor else { return }

libraryService.updateDetails(at: relativePath, details: cleanedAuthor)
}

private func sendEvent(_ event: ItemDetailsViewModel.Events) {
eventsPublisher.send(event)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct ItemDetailsArtworkSectionView: View {
/// Callback for action handler
var actionHandler: () -> Void
/// Theme view model to update colors
@StateObject var themeViewModel = ThemeViewModel()
@EnvironmentObject var themeViewModel: ThemeViewModel

var body: some View {
Section(
Expand Down Expand Up @@ -49,5 +49,6 @@ struct ItemDetailsArtworkSectionView_Previews: PreviewProvider {
image: .constant(nil),
actionHandler: {}
)
.environmentObject(ThemeViewModel())
}
}
17 changes: 12 additions & 5 deletions BookPlayer/Library/ItemDetails Screen/Views/ItemDetailsForm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,22 @@ struct ItemDetailsForm: View {

var body: some View {
Form {
Section(header: Text("details_title".localized)
Section(header: Text("section_item_title".localized)
.foregroundColor(themeViewModel.secondaryColor)
) {
ClearableTextField("item_title_placeholder".localized, text: $viewModel.title)
if viewModel.showAuthor {
ClearableTextField("item_author_placeholder".localized, text: $viewModel.author)
}
ClearableTextField(viewModel.titlePlaceholder, text: $viewModel.title)
}
.listRowBackground(themeViewModel.secondarySystemBackgroundColor)

if viewModel.showAuthor {
Section(header: Text("section_item_author".localized)
.foregroundColor(themeViewModel.secondaryColor)
) {
ClearableTextField(viewModel.authorPlaceholder, text: $viewModel.author)
}
.listRowBackground(themeViewModel.secondarySystemBackgroundColor)
}

ItemDetailsArtworkSectionView(image: $viewModel.selectedImage) {
showingArtworkOptions = true
}
Expand Down Expand Up @@ -69,6 +75,7 @@ struct ItemDetailsForm: View {
dismissButton: .default(Text("ok_button".localized))
)
}
.environmentObject(themeViewModel)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct ItemDetailsView: View {
var body: some View {
if #available(iOS 16.0, *) {
ItemDetailsForm(viewModel: viewModel)
.scrollContentBackground(.hidden)
.scrollContentBackground(.hidden)
} else {
ItemDetailsForm(viewModel: viewModel)
}
Expand Down
Loading

0 comments on commit 399dcd9

Please sign in to comment.