Commit 8e2a1982 authored by Edgar Fouillet's avatar Edgar Fouillet

EditController: Refactor edit functions into EditActions

parent 44c257aa
/*****************************************************************************
* EditActions.swift
*
* Copyright © 2019 VLC authors and VideoLAN
*
* Authors: Edgar Fouillet <vlc # edgar.fouillet.eu>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
enum completionState {
case inProgress
case success
case fail
}
class EditActions {
private let rootViewController: UIViewController
private let model: MediaLibraryBaseModel
private let mediaLibraryService: MediaLibraryService
private var completion: ((completionState) -> Void)?
var objects = [VLCMLObject]()
private lazy var addToPlaylistViewController: AddToPlaylistViewController = {
var addToPlaylistViewController = AddToPlaylistViewController(playlists: mediaLibraryService.playlists())
addToPlaylistViewController.delegate = self
return addToPlaylistViewController
}()
init(model: MediaLibraryBaseModel, mediaLibraryService: MediaLibraryService) {
self.rootViewController = UIApplication.shared.keyWindow!.rootViewController!
self.model = model
self.mediaLibraryService = mediaLibraryService
}
private func addToNewPlaylist() {
let alertInfo = TextFieldAlertInfo(alertTitle: NSLocalizedString("PLAYLISTS", comment: ""),
placeHolder: NSLocalizedString("PLAYLIST_PLACEHOLDER",
comment: ""))
presentTextFieldAlert(with: alertInfo) {
[unowned self] text -> Void in
guard text != "" else {
DispatchQueue.main.async {
VLCAlertViewController.alertViewManager(title: NSLocalizedString("ERROR_EMPTY_NAME",
comment: ""),
viewController: self.rootViewController)
}
return
}
self.createPlaylist(text)
}
}
func addToPlaylist(_ completion: ((completionState) -> Void)? = nil) {
self.completion = completion
if !mediaLibraryService.playlists().isEmpty {
addToPlaylistViewController.playlists = mediaLibraryService.playlists()
let navigationController = UINavigationController(rootViewController: addToPlaylistViewController)
rootViewController.present(navigationController, animated: true, completion: nil)
} else {
addToNewPlaylist()
}
}
func rename(_ completion: ((completionState) -> Void)? = nil) {
self.completion = completion
if !objects.isEmpty {
let mlObject = objects.first
var mlObjectName = ""
if let media = mlObject as? VLCMLMedia {
mlObjectName = media.title
} else if let playlist = mlObject as? VLCMLPlaylist {
mlObjectName = playlist.name
} else {
assertionFailure("EditActions: Rename called with wrong model.")
}
// Not using VLCAlertViewController to have more customization in text fields
let alertInfo = TextFieldAlertInfo(alertTitle: String(format: NSLocalizedString("RENAME_MEDIA_TO", comment: ""), mlObjectName),
textfieldText: mlObjectName,
confirmActionTitle: NSLocalizedString("BUTTON_RENAME", comment: ""))
presentTextFieldAlert(with: alertInfo, completionHandler: {
[unowned self] text -> Void in
guard text != "" else {
VLCAlertViewController.alertViewManager(title: NSLocalizedString("ERROR_RENAME_FAILED", comment: ""),
errorMessage: NSLocalizedString("ERROR_EMPTY_NAME", comment: ""),
viewController: self.rootViewController)
self.completion?(.fail)
return
}
if let media = mlObject as? VLCMLMedia {
media.updateTitle(text)
} else if let playlist = mlObject as? VLCMLPlaylist {
playlist.updateName(text)
}
self.objects.removeFirst()
self.completion?(.inProgress)
self.rename(completion)
})
} else {
self.completion?(.success)
}
}
private func URLs() -> [URL] {
var fileURLs = [URL]()
for object in objects {
if let media = object as? VLCMLMedia {
if let file = media.mainFile() {
fileURLs.append(file.mrl)
}
} else if let mediaCollection = object as? MediaCollectionModel {
if let files = mediaCollection.files() {
for media in files {
if let file = media.mainFile() {
fileURLs.append(file.mrl)
}
}
}
}
}
return fileURLs
}
func delete(_ completion: ((completionState) -> Void)? = nil) {
self.completion = completion
var message = NSLocalizedString("DELETE_MESSAGE", comment: "")
if model is PlaylistModel {
message = NSLocalizedString("DELETE_MESSAGE_PLAYLIST", comment: "")
} else if (model as? CollectionModel)?.mediaCollection is VLCMLPlaylist {
message = NSLocalizedString("DELETE_MESSAGE_PLAYLIST_CONTENT", comment: "")
}
let cancelButton = VLCAlertButton(title: NSLocalizedString("BUTTON_CANCEL", comment: ""),
style: .cancel)
let deleteButton = VLCAlertButton(title: NSLocalizedString("BUTTON_DELETE", comment: ""),
style: .destructive,
action: {
[unowned self] action in
self.model.delete(self.objects)
self.objects.removeAll()
self.completion?(.success)
})
VLCAlertViewController.alertViewManager(title: NSLocalizedString("DELETE_TITLE", comment: ""),
errorMessage: message,
viewController: rootViewController,
buttonsAction: [cancelButton,
deleteButton])
}
func share(_ completion: ((completionState) -> Void)? = nil) {
self.completion = completion
UIApplication.shared.beginIgnoringInteractionEvents()
guard let controller = VLCActivityViewControllerVendor.activityViewController(forFiles: URLs(),
presenting: nil,
presenting: rootViewController,
completionHandler: {
[unowned self] _ in
self.completion?(.success)
}
) else {
UIApplication.shared.endIgnoringInteractionEvents()
self.completion?(.fail)
return
}
controller.popoverPresentationController?.sourceView = rootViewController.view
rootViewController.present(controller, animated: true) {
UIApplication.shared.endIgnoringInteractionEvents()
}
}
}
private extension EditActions {
private struct TextFieldAlertInfo {
var alertTitle: String
var alertDescription: String
var placeHolder: String
var textfieldText: String
var confirmActionTitle: String
init(alertTitle: String = "",
alertDescription: String = "",
placeHolder: String = "",
textfieldText: String = "",
confirmActionTitle: String = NSLocalizedString("BUTTON_DONE", comment: "")) {
self.alertTitle = alertTitle
self.alertDescription = alertDescription
self.placeHolder = placeHolder
self.textfieldText = textfieldText
self.confirmActionTitle = confirmActionTitle
}
}
private func presentTextFieldAlert(with info: TextFieldAlertInfo,
completionHandler: @escaping (String) -> Void) {
let alertController = UIAlertController(title: info.alertTitle,
message: info.alertDescription,
preferredStyle: .alert)
alertController.addTextField(configurationHandler: {
textField in
textField.text = info.textfieldText
textField.placeholder = info.placeHolder
})
let cancelButton = UIAlertAction(title: NSLocalizedString("BUTTON_CANCEL", comment: ""),
style: .cancel)
let confirmAction = UIAlertAction(title: info.confirmActionTitle, style: .default) {
[weak alertController] _ in
guard let alertController = alertController,
let textField = alertController.textFields?.first else { return }
completionHandler(textField.text ?? "")
}
alertController.addAction(cancelButton)
alertController.addAction(confirmAction)
rootViewController.present(alertController, animated: true, completion: nil)
}
private func createPlaylist(_ name: String) {
guard let playlist = mediaLibraryService.createPlaylist(with: name) else {
assertionFailure("EditActions: createPlaylist: Failed to create a playlist.")
DispatchQueue.main.async {
VLCAlertViewController.alertViewManager(title: NSLocalizedString("ERROR_PLAYLIST_CREATION",
comment: ""),
viewController: self.rootViewController)
}
completion?(.fail)
return
}
for media in objects {
if !playlist.appendMedia(withIdentifier: media.identifier()) {
assertionFailure("EditActions: createPlaylist: Failed to add item.")
}
}
completion?(.success)
}
}
// MARK: - AddToPlaylistViewControllerDelegate
extension EditActions: AddToPlaylistViewControllerDelegate {
func addToPlaylistViewController(_ addToPlaylistViewController: AddToPlaylistViewController,
didSelectPlaylist playlist: VLCMLPlaylist) {
for media in objects {
if !playlist.appendMedia(withIdentifier: media.identifier()) {
assertionFailure("EditActions: AddToPlaylistViewControllerDelegate: Failed to add item.")
completion?(.fail)
}
}
addToPlaylistViewController.dismiss(animated: true, completion: nil)
completion?(.success)
}
func addToPlaylistViewController(_ addToPlaylistViewController: AddToPlaylistViewController,
newPlaylistWithName name: String) {
createPlaylist(name)
addToPlaylistViewController.dismiss(animated: true, completion: nil)
}
}
......@@ -22,12 +22,7 @@ class EditController: UIViewController {
private let model: MediaLibraryBaseModel
private let mediaLibraryService: MediaLibraryService
private let presentingView: UICollectionView
private lazy var addToPlaylistViewController: AddToPlaylistViewController = {
var addToPlaylistViewController = AddToPlaylistViewController(playlists: mediaLibraryService.playlists())
addToPlaylistViewController.delegate = self
return addToPlaylistViewController
}()
private(set) var editActions: EditActions
weak var delegate: EditControllerDelegate?
......@@ -43,6 +38,7 @@ class EditController: UIViewController {
self.mediaLibraryService = mediaLibraryService
self.model = model
self.presentingView = presentingView
self.editActions = EditActions(model: model, mediaLibraryService: mediaLibraryService)
super.init(nibName: nil, bundle: nil)
}
......@@ -112,73 +108,46 @@ private extension EditController {
present(alertController, animated: true, completion: nil)
}
}
private func createPlaylist(_ name: String) {
guard let playlist = mediaLibraryService.createPlaylist(with: name) else {
assertionFailure("MediaModel: createPlaylist: Failed to create a playlist.")
DispatchQueue.main.async {
VLCAlertViewController.alertViewManager(title: NSLocalizedString("ERROR_PLAYLIST_CREATION",
comment: ""),
viewController: self)
}
return
}
// MARK: - VLCEditToolbarDelegate
// In the case of Video, Tracks
if let files = model.anyfiles as? [VLCMLMedia] {
for index in selectedCellIndexPaths where index.row < files.count {
playlist.appendMedia(withIdentifier: files[index.row].identifier())
}
} else if let mediaCollectionArray = model.anyfiles as? [MediaCollectionModel] {
for index in selectedCellIndexPaths where index.row < mediaCollectionArray.count {
guard let tracks = mediaCollectionArray[index.row].files() else {
extension EditController: EditToolbarDelegate {
private func getSelectedObjects() {
let files = model.anyfiles
for index in selectedCellIndexPaths where index.row < files.count {
if let mediaCollection = files[index.row] as? MediaCollectionModel {
guard let files = mediaCollection.files() else {
assertionFailure("EditController: Fail to retrieve tracks.")
DispatchQueue.main.async {
VLCAlertViewController.alertViewManager(title: NSLocalizedString("ERROR_PLAYLIST_TRACKS",
comment: ""),
comment: ""),
viewController: self)
}
return
}
tracks.forEach() {
playlist.appendMedia(withIdentifier: $0.identifier())
}
}
}
resetSelections(resetUI: true)
delegate?.editControllerDidFinishEditing(editController: self)
}
}
// MARK: - VLCEditToolbarDelegate
extension EditController: EditToolbarDelegate {
func addToNewPlaylist() {
let alertInfo = TextFieldAlertInfo(alertTitle: NSLocalizedString("PLAYLISTS", comment: ""),
placeHolder: NSLocalizedString("PLAYLIST_PLACEHOLDER",
comment: ""))
presentTextFieldAlert(with: alertInfo) {
[unowned self] text -> Void in
guard text != "" else {
DispatchQueue.main.async {
VLCAlertViewController.alertViewManager(title: NSLocalizedString("ERROR_EMPTY_NAME",
comment: ""),
viewController: self)
}
return
editActions.objects += files
} else {
editActions.objects.append(files[index.row])
}
self.createPlaylist(text)
}
}
func editToolbarDidAddToPlaylist(_ editToolbar: EditToolbar) {
if !mediaLibraryService.playlists().isEmpty && !selectedCellIndexPaths.isEmpty {
addToPlaylistViewController.playlists = mediaLibraryService.playlists()
delegate?.editController(editController: self,
present: addToPlaylistViewController)
} else {
addToNewPlaylist()
guard !selectedCellIndexPaths.isEmpty else {
assertionFailure("EditController: Add to playlist called without selection")
return
}
editActions.objects.removeAll()
getSelectedObjects()
editActions.addToPlaylist({
[weak self] state in
if state == .success || state == .fail {
self?.resetSelections(resetUI: false)
self?.delegate?.editControllerDidFinishEditing(editController: self)
}
})
}
func editToolbarDidDelete(_ editToolbar: EditToolbar) {
......@@ -186,131 +155,50 @@ extension EditController: EditToolbarDelegate {
assertionFailure("EditController: Delete called without selection")
return
}
var objectsToDelete = [VLCMLObject]()
editActions.objects.removeAll()
for indexPath in selectedCellIndexPaths.sorted(by: { $0 > $1 }) {
objectsToDelete.append(model.anyfiles[indexPath.row])
editActions.objects.append(model.anyfiles[indexPath.row])
}
var message = NSLocalizedString("DELETE_MESSAGE", comment: "")
if model is PlaylistModel {
message = NSLocalizedString("DELETE_MESSAGE_PLAYLIST", comment: "")
} else if (model as? CollectionModel)?.mediaCollection is VLCMLPlaylist {
message = NSLocalizedString("DELETE_MESSAGE_PLAYLIST_CONTENT", comment: "")
}
let cancelButton = VLCAlertButton(title: NSLocalizedString("BUTTON_CANCEL", comment: ""),
style: .cancel)
let deleteButton = VLCAlertButton(title: NSLocalizedString("BUTTON_DELETE", comment: ""),
style: .destructive,
action: {
[weak self] action in
self?.model.delete(objectsToDelete)
// Update directly the cached indexes since cells will be destroyed
self?.selectedCellIndexPaths.removeAll()
self?.delegate?.editControllerDidFinishEditing(editController: self)
editActions.delete({
[weak self] state in
if state == .success || state == .fail {
self?.resetSelections(resetUI: false)
self?.delegate?.editControllerDidFinishEditing(editController: self)
}
})
VLCAlertViewController.alertViewManager(title: NSLocalizedString("DELETE_TITLE", comment: ""),
errorMessage: message,
viewController: (UIApplication.shared.keyWindow?.rootViewController)!,
buttonsAction: [cancelButton,
deleteButton])
}
func editToolbarDidShare(_ editToolbar: EditToolbar, presentFrom button: UIButton) {
UIApplication.shared.beginIgnoringInteractionEvents()
let rootViewController = UIApplication.shared.keyWindow?.rootViewController
guard let controller = VLCActivityViewControllerVendor
.activityViewController(forFiles: fileURLsFromSelection(),
presenting: button,
presenting: rootViewController,
completionHandler: {
[weak self] completion in
self?.delegate?.editControllerDidFinishEditing(editController: self)
}) else {
UIApplication.shared.endIgnoringInteractionEvents()
guard !selectedCellIndexPaths.isEmpty else {
assertionFailure("EditController: Share called without selection")
return
}
controller.popoverPresentationController?.sourceView = editToolbar
rootViewController?.present(controller, animated: true) {
UIApplication.shared.endIgnoringInteractionEvents()
}
}
func fileURLsFromSelection() -> [URL] {
var fileURLS = [URL]()
for indexPath in selectedCellIndexPaths {
let file = model.anyfiles[indexPath.row]
if let collection = file as? MediaCollectionModel,
let files = collection.files() {
files.forEach {
if let mainFile = $0.mainFile() {
fileURLS.append(mainFile.mrl)
}
}
} else if let media = file as? VLCMLMedia, let mainFile = media.mainFile() {
fileURLS.append(mainFile.mrl)
} else {
assertionFailure("we're trying to share something that doesn't have an mrl")
return fileURLS
editActions.objects.removeAll()
getSelectedObjects()
editActions.share({
[weak self] state in
if state == .success || state == .fail {
self?.resetSelections(resetUI: false)
self?.delegate?.editControllerDidFinishEditing(editController: self)
}
}
return fileURLS
})
}
func editToolbarDidRename(_ editToolbar: EditToolbar) {
guard let indexPath = selectedCellIndexPaths.first else {
assertionFailure("EditController: Rename called without selection.")
guard !selectedCellIndexPaths.isEmpty else {
assertionFailure("EditController: Rename called without selection")
return
}
var mlObjectName = ""
let mlObject = model.anyfiles[indexPath.row]
if let media = mlObject as? VLCMLMedia {
mlObjectName = media.title
} else if let playlist = mlObject as? VLCMLPlaylist {
mlObjectName = playlist.name
} else {
assertionFailure("EditController: Rename called with wrong model.")
editActions.objects.removeAll()
for indexPath in selectedCellIndexPaths.sorted(by: { $0 > $1 }) {
editActions.objects.append(model.anyfiles[indexPath.row])
}
// Not using VLCAlertViewController to have more customization in text fields
let alertInfo = TextFieldAlertInfo(alertTitle: String(format: NSLocalizedString("RENAME_MEDIA_TO", comment: ""), mlObjectName),
textfieldText: mlObjectName,
confirmActionTitle: NSLocalizedString("BUTTON_RENAME", comment: ""))
presentTextFieldAlert(with: alertInfo, completionHandler: {
[weak self] text -> Void in
guard text != "" else {
VLCAlertViewController.alertViewManager(title: NSLocalizedString("ERROR_RENAME_FAILED", comment: ""),
errorMessage: NSLocalizedString("ERROR_EMPTY_NAME", comment: ""),
viewController: (UIApplication.shared.keyWindow?.rootViewController)!)
return
}
let mlObject = self?.model.anyfiles[indexPath.row]
if let media = mlObject as? VLCMLMedia {
media.updateTitle(text)
} else if let playlist = mlObject as? VLCMLPlaylist {
playlist.updateName(text)
}
guard let strongself = self else {
return
}
strongself.presentingView.deselectItem(at: indexPath, animated: true)
strongself.collectionView(strongself.presentingView, didDeselectItemAt: indexPath)
//call until all items are renamed
if !strongself.selectedCellIndexPaths.isEmpty {
strongself.editToolbarDidRename(editToolbar)
} else {
strongself.delegate?.editControllerDidFinishEditing(editController: self)
editActions.rename({
[weak self] state in
if state == .success || state == .fail {
self?.resetSelections(resetUI: true)
self?.delegate?.editControllerDidFinishEditing(editController: self)
}
})
}
......@@ -417,35 +305,3 @@ extension EditController: UICollectionViewDelegateFlowLayout {
}
}
// MARK: - AddToPlaylistViewControllerDelegate
extension EditController: AddToPlaylistViewControllerDelegate {
func addToPlaylistViewController(_ addToPlaylistViewController: AddToPlaylistViewController,
didSelectPlaylist playlist: VLCMLPlaylist) {
let files = model.anyfiles
var mediaObjects = [VLCMLObject]()
for index in selectedCellIndexPaths where index.row < files.count {
if let mediaCollection = files[index.row] as? MediaCollectionModel {
mediaObjects += mediaCollection.files() ?? []
} else {
mediaObjects.append(files[index.row])
}
}
for media in mediaObjects {
if !playlist.appendMedia(withIdentifier: media.identifier()) {
assertionFailure("EditController: AddToPlaylistViewControllerDelegate: Failed to add item.")
}
}
resetSelections(resetUI: false)
addToPlaylistViewController.dismiss(animated: true, completion: nil)
delegate?.editControllerDidFinishEditing(editController: self)
}
func addToPlaylistViewController(_ addToPlaylistViewController: AddToPlaylistViewController,
newPlaylistWithName name: String) {
createPlaylist(name)
addToPlaylistViewController.dismiss(animated: true, completion: nil)
}
}
......@@ -86,6 +86,7 @@
6B4E33D11BF2A39400A35255 /* playerControl.css in Resources */ = {isa = PBXBuildFile; fileRef = 6B4E33CF1BF2A39400A35255 /* playerControl.css */; };
6B4E33D21BF2A39400A35255 /* playerControl.js in Resources */ = {isa = PBXBuildFile; fileRef = 6B4E33D01BF2A39400A35255 /* playerControl.js */; };
6D220D0D234B8E3700BD694F /* FileManager+DeleteMediaFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D220D0C234B8E3700BD694F /* FileManager+DeleteMediaFolder.swift */; };
6D4756B123607D4A005F670E /* EditActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4756B023607D49005F670E /* EditActions.swift */; };
6D8E642A234CBF2200EBC8FC /* AudioCollectionModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D8E6429234CBF2200EBC8FC /* AudioCollectionModel.swift */; };
7AC8629D1765DC560011611A /* style.css in Resources */ = {isa = PBXBuildFile; fileRef = 7AC8629B1765DC560011611A /* style.css */; };
7AC862A61765E9510011611A /* jquery-1.10.1.min.js in Resources */ = {isa = PBXBuildFile; fileRef = 7AC8629E1765E90C0011611A /* jquery-1.10.1.min.js */; };
......@@ -566,6 +567,7 @@
6B4E33CF1BF2A39400A35255 /* playerControl.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = playerControl.css; path = Resources/web/playerControl.css; sourceTree = SOURCE_ROOT; };
6B4E33D01BF2A39400A35255 /* playerControl.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = playerControl.js; path = Resources/web/playerControl.js; sourceTree = SOURCE_ROOT; };
6D220D0C234B8E3700BD694F /* FileManager+DeleteMediaFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "FileManager+DeleteMediaFolder.swift"; path = "Sources/FileManager+DeleteMediaFolder.swift"; sourceTree = "<group>"; };
6D4756B023607D49005F670E /* EditActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = EditActions.swift; path = Sources/EditActions.swift; sourceTree = "<group>"; };
6D8E6429234CBF2200EBC8FC /* AudioCollectionModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioCollectionModel.swift; sourceTree = "<group>"; };
703A80CCC005093CCDFBECBF /* Pods-VLC-iOS-Screenshots.distribution.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VLC-iOS-Screenshots.distribution.xcconfig"; path = "Pods/Target Support Files/Pods-VLC-iOS-Screenshots/Pods-VLC-iOS-Screenshots.distribution.xcconfig"; sourceTree = "<group>"; };
7AC8629B1765DC560011611A /* style.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = style.css; sourceTree = "<group>"; };
......@@ -1403,6 +1405,7 @@
41D7DD0420C1853E00AD94F6 /* ButtonBarView.swift */,
41D7DD2620C3060300AD94F6 /* PagerStripViewController.swift */,
4170AF9622848DCB008881BA /* LibrarySearchDataSource.swift */,
6D4756B023607D49005F670E /* EditActions.swift */,
);
name = "Everything Playlist";
sourceTree = "<group>";
......@@ -3001,6 +3004,7 @@
41884A50214BE0D800D2C6B4 /* MediaCollectionViewCell.swift in Sources */,
7D5CAA891A4AD763003F2CBC /* VLCTrackSelectorTableViewCell.m in Sources */,
7D63C19018774B1700BD5256 /* VLCFirstStepsiTunesSyncViewController.m in Sources */,
6D4756B123607D4A005F670E /* EditActions.swift in Sources */,
8F91EC79195CEC7900F5BCBA /* VLCOpenInActivity.m in Sources */,
CAC7E3DE20F87BD000000751 /* VLCSettingsSpecifierManager.swift in Sources */,
7D37848F183A98B6009EE944 /* VLCMovieViewController.m in Sources */,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment