diff --git a/NextcloudApp/Models/MoveFileOrFolderPageParameters.cs b/NextcloudApp/Models/MoveFileOrFolderPageParameters.cs new file mode 100644 index 0000000..50e0d07 --- /dev/null +++ b/NextcloudApp/Models/MoveFileOrFolderPageParameters.cs @@ -0,0 +1,9 @@ +using NextcloudClient.Types; + +namespace NextcloudApp.Models +{ + public class MoveFileOrFolderPageParameters : PageParameters + { + public ResourceInfo ResourceInfo { get; set; } + } +} diff --git a/NextcloudApp/NextcloudApp.csproj b/NextcloudApp/NextcloudApp.csproj index 27f3652..b2e6d8d 100644 --- a/NextcloudApp/NextcloudApp.csproj +++ b/NextcloudApp/NextcloudApp.csproj @@ -114,6 +114,7 @@ + @@ -149,6 +150,7 @@ + @@ -157,6 +159,9 @@ FileUploadPage.xaml + + MoveFileOrFolder.xaml + SingleFileDownloadPage.xaml @@ -225,6 +230,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/NextcloudApp/PageTokens.cs b/NextcloudApp/PageTokens.cs index 0023204..8fe7472 100644 --- a/NextcloudApp/PageTokens.cs +++ b/NextcloudApp/PageTokens.cs @@ -8,6 +8,7 @@ Settings, SingleFileDownload, BulkFileDownload, - FileUpload + FileUpload, + MoveFileOrFolder } } diff --git a/NextcloudApp/Services/DirectoryService.cs b/NextcloudApp/Services/DirectoryService.cs index f1a6c54..4b5eb36 100644 --- a/NextcloudApp/Services/DirectoryService.cs +++ b/NextcloudApp/Services/DirectoryService.cs @@ -318,5 +318,33 @@ namespace NextcloudApp.Services } return success; } + + public async Task Move(string oldPath, string newPath) + { + var client = ClientService.GetClient(); + if (client == null) + { + return false; + } + + var success = false; + try + { + success = await client.Move(oldPath, newPath); + } + catch (ResponseError e) + { + if (e.StatusCode != "400") // ProtocolError + { + ResponseErrorHandlerService.HandleException(e); + } + } + + if (success) + { + await StartDirectoryListing(); + } + return success; + } } } diff --git a/NextcloudApp/Strings/de-DE/Resources.resw b/NextcloudApp/Strings/de-DE/Resources.resw index e19a867..a6a5e90 100644 --- a/NextcloudApp/Strings/de-DE/Resources.resw +++ b/NextcloudApp/Strings/de-DE/Resources.resw @@ -347,4 +347,19 @@ Möchten Sie die Anwendungsentwickler informieren und den vorbereiteten Fehlerbe Umbenennen + + Verschieben nach + + + Verschieben nach + + + Abbrechen + + + Fertig + + + Ordner auswählen + \ No newline at end of file diff --git a/NextcloudApp/Strings/en-US/Resources.resw b/NextcloudApp/Strings/en-US/Resources.resw index a2efe3a..c5217d6 100644 --- a/NextcloudApp/Strings/en-US/Resources.resw +++ b/NextcloudApp/Strings/en-US/Resources.resw @@ -347,4 +347,19 @@ Would you like to inform the application developers and submit the prepared erro Rename + + Move to + + + Move to + + + Cancel + + + Done + + + Choose folder + \ No newline at end of file diff --git a/NextcloudApp/ViewModels/DirectoryListPageViewModel.cs b/NextcloudApp/ViewModels/DirectoryListPageViewModel.cs index a7a21aa..00213ad 100644 --- a/NextcloudApp/ViewModels/DirectoryListPageViewModel.cs +++ b/NextcloudApp/ViewModels/DirectoryListPageViewModel.cs @@ -37,6 +37,7 @@ namespace NextcloudApp.ViewModels public ICommand UploadPhotosCommand { get; private set; } public ICommand DeleteResourceCommand { get; private set; } public ICommand RenameResourceCommand { get; private set; } + public ICommand MoveResourceCommand { get; private set; } public DirectoryListPageViewModel(INavigationService navigationService, IResourceLoader resourceLoader, DialogService dialogService) { @@ -85,6 +86,7 @@ namespace NextcloudApp.ViewModels UploadPhotosCommand = new DelegateCommand(UploadPhotos); DeleteResourceCommand = new RelayCommand(DeleteResource); RenameResourceCommand = new RelayCommand(RenameResource); + MoveResourceCommand = new RelayCommand(MoveResource); } public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary viewModelState) @@ -107,6 +109,20 @@ namespace NextcloudApp.ViewModels base.OnNavigatingFrom(e, viewModelState, suspending); } + private void MoveResource(object parameter) + { + var resourceInfo = parameter as ResourceInfo; + if (resourceInfo == null) + { + return; + } + var parameters = new MoveFileOrFolderPageParameters + { + ResourceInfo = resourceInfo + }; + _navigationService.Navigate(PageTokens.MoveFileOrFolder.ToString(), parameters.Serialize()); + } + private async void DeleteResource(object parameter) { var resourceInfo = parameter as ResourceInfo; diff --git a/NextcloudApp/ViewModels/FileInfoPageViewModel.cs b/NextcloudApp/ViewModels/FileInfoPageViewModel.cs index 385109e..44b2d4b 100644 --- a/NextcloudApp/ViewModels/FileInfoPageViewModel.cs +++ b/NextcloudApp/ViewModels/FileInfoPageViewModel.cs @@ -35,6 +35,7 @@ namespace NextcloudApp.ViewModels public ICommand DownloadCommand { get; private set; } public ICommand DeleteResourceCommand { get; private set; } public ICommand RenameResourceCommand { get; private set; } + public ICommand MoveResourceCommand { get; private set; } public FileInfoPageViewModel(INavigationService navigationService, IResourceLoader resourceLoader, DialogService dialogService) { @@ -56,6 +57,7 @@ namespace NextcloudApp.ViewModels }); DeleteResourceCommand = new DelegateCommand(DeleteResource); RenameResourceCommand = new DelegateCommand(RenameResource); + MoveResourceCommand = new RelayCommand(MoveResource); } public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary viewModelState) @@ -300,5 +302,19 @@ namespace NextcloudApp.ViewModels _navigationService.GoBack(); } } + + private void MoveResource(object obj) + { + if (ResourceInfo == null) + { + return; + } + + var parameters = new MoveFileOrFolderPageParameters + { + ResourceInfo = ResourceInfo + }; + _navigationService.Navigate(PageTokens.MoveFileOrFolder.ToString(), parameters.Serialize()); + } } } \ No newline at end of file diff --git a/NextcloudApp/ViewModels/MoveFileOrFolderPageViewModel.cs b/NextcloudApp/ViewModels/MoveFileOrFolderPageViewModel.cs new file mode 100644 index 0000000..181fca8 --- /dev/null +++ b/NextcloudApp/ViewModels/MoveFileOrFolderPageViewModel.cs @@ -0,0 +1,317 @@ +using System.Collections.Generic; +using System.Windows.Input; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using NextcloudApp.Models; +using NextcloudApp.Services; +using NextcloudClient.Types; +using Prism.Commands; +using Prism.Windows.AppModel; +using Prism.Windows.Navigation; + +namespace NextcloudApp.ViewModels +{ + public class MoveFileOrFolderPageViewModel : ViewModel + { + private Settings _settngs; + private DirectoryService _directoryService; + private ResourceInfo _selectedFileOrFolder; + private int _selectedPathIndex = -1; + private readonly INavigationService _navigationService; + private readonly IResourceLoader _resourceLoader; + private readonly DialogService _dialogService; + private bool _isNavigatingBack; + + public ICommand GroupByNameAscendingCommand { get; private set; } + public ICommand GroupByNameDescendingCommand { get; private set; } + public ICommand GroupByDateAscendingCommand { get; private set; } + public ICommand GroupByDateDescendingCommand { get; private set; } + public ICommand GroupBySizeAscendingCommand { get; private set; } + public ICommand GroupBySizeDescendingCommand { get; private set; } + public ICommand RefreshCommand { get; private set; } + public ICommand CreateDirectoryCommand { get; private set; } + public object CancelFolderSelectionCommand { get; private set; } + public object MoveToSelectedFolderCommand { get; private set; } + + public MoveFileOrFolderPageViewModel(INavigationService navigationService, IResourceLoader resourceLoader, DialogService dialogService) + { + _navigationService = navigationService; + _resourceLoader = resourceLoader; + _dialogService = dialogService; + Settings = SettingsService.Instance.Settings; + GroupByNameAscendingCommand = new DelegateCommand(() => + { + Directory.GroupByNameAscending(); + SelectedFileOrFolder = null; + }); + GroupByNameDescendingCommand = new DelegateCommand(() => + { + Directory.GroupByNameDescending(); + SelectedFileOrFolder = null; + }); + GroupByDateAscendingCommand = new DelegateCommand(() => + { + Directory.GroupByDateAscending(); + SelectedFileOrFolder = null; + }); + GroupByDateDescendingCommand = new DelegateCommand(() => + { + Directory.GroupByDateDescending(); + SelectedFileOrFolder = null; + }); + GroupBySizeAscendingCommand = new DelegateCommand(() => + { + Directory.GroupBySizeAscending(); + SelectedFileOrFolder = null; + }); + GroupBySizeDescendingCommand = new DelegateCommand(() => + { + Directory.GroupBySizeDescending(); + SelectedFileOrFolder = null; + }); + RefreshCommand = new DelegateCommand(async () => + { + ShowProgressIndicator(); + await Directory.Refresh(); + HideProgressIndicator(); + }); + CreateDirectoryCommand = new DelegateCommand(CreateDirectory); + MoveToSelectedFolderCommand = new DelegateCommand(MoveToSelectedFolder); + CancelFolderSelectionCommand = new DelegateCommand(CancelFolderSelection); + } + + public override void OnNavigatedTo(NavigatedToEventArgs e, Dictionary viewModelState) + { + base.OnNavigatedTo(e, viewModelState); + base.OnNavigatedTo(e, viewModelState); + var parameters = MoveFileOrFolderPageParameters.Deserialize(e.Parameter); + var resourceInfo = parameters?.ResourceInfo; + if (resourceInfo == null) + { + return; + } + ResourceInfo = resourceInfo; + Directory = DirectoryService.Instance; + StartDirectoryListing(); + _isNavigatingBack = false; + } + + public ResourceInfo ResourceInfo { get; private set; } + + public override void OnNavigatingFrom(NavigatingFromEventArgs e, Dictionary viewModelState, bool suspending) + { + _isNavigatingBack = true; + if (!suspending) + { + Directory.StopDirectoryListing(); + Directory = null; + _selectedFileOrFolder = null; + } + base.OnNavigatingFrom(e, viewModelState, suspending); + } + + private void CancelFolderSelection() + { + _navigationService.GoBack(); + } + + private async void MoveToSelectedFolder() + { + ShowProgressIndicator(); + var currentFolderResourceInfo = Directory.PathStack.Count > 0 + ? Directory.PathStack[Directory.PathStack.Count - 1].ResourceInfo + : new ResourceInfo(); + + var oldPath = string.IsNullOrEmpty(ResourceInfo.Path) ? "/" : ResourceInfo.Path; + oldPath = oldPath.TrimEnd('/'); + + if (!ResourceInfo.ContentType.Equals("dav/directory")) + { + oldPath = oldPath + "/" + ResourceInfo.Name; + } + + var newPath = string.IsNullOrEmpty(currentFolderResourceInfo.Path) ? "/" : currentFolderResourceInfo.Path; + newPath = newPath.TrimEnd('/'); + newPath = newPath + "/" + ResourceInfo.Name; + + var success = await Directory.Move(oldPath, newPath); + + HideProgressIndicator(); + + if (success) + { + _navigationService.GoBack(); + } + } + + private async void CreateDirectory() + { + while (true) + { + var dialog = new ContentDialog + { + Title = _resourceLoader.GetString("CreateNewFolder"), + Content = new TextBox() + { + Header = _resourceLoader.GetString("FolderName"), + PlaceholderText = _resourceLoader.GetString("NewFolder"), + Margin = new Thickness(0, 20, 0, 0) + }, + PrimaryButtonText = _resourceLoader.GetString("Create"), + SecondaryButtonText = _resourceLoader.GetString("Cancel") + }; + var dialogResult = await _dialogService.ShowAsync(dialog); + if (dialogResult != ContentDialogResult.Primary) + { + return; + } + var textBox = dialog.Content as TextBox; + if (textBox == null) + { + return; + } + var folderName = textBox.Text; + if (string.IsNullOrEmpty(folderName)) + { + folderName = _resourceLoader.GetString("NewFolder"); + } + ShowProgressIndicator(); + var success = await Directory.CreateDirectory(folderName); + HideProgressIndicator(); + if (success) + { + return; + } + + dialog = new ContentDialog + { + Title = _resourceLoader.GetString("CanNotCreateFolder"), + Content = new TextBlock + { + Text = _resourceLoader.GetString("SpecifyDifferentName"), + TextWrapping = TextWrapping.WrapWholeWords, + Margin = new Thickness(0, 20, 0, 0) + }, + PrimaryButtonText = _resourceLoader.GetString("Retry"), + SecondaryButtonText = _resourceLoader.GetString("Cancel") + }; + dialogResult = await _dialogService.ShowAsync(dialog); + if (dialogResult != ContentDialogResult.Primary) + { + return; + } + } + } + + public DirectoryService Directory + { + get { return _directoryService; } + private set { SetProperty(ref _directoryService, value); } + } + + public Settings Settings + { + get { return _settngs; } + private set { SetProperty(ref _settngs, value); } + } + + public ResourceInfo SelectedFileOrFolder + { + get { return _selectedFileOrFolder; } + set + { + if (_isNavigatingBack) + { + return; + } + try + { + if (!SetProperty(ref _selectedFileOrFolder, value)) + { + return; + } + } + catch + { + _selectedPathIndex = -1; + return; + } + + if (value == null) + { + return; + } + + if (Directory?.PathStack == null) + { + return; + } + + if (Directory.IsSorting) + { + return; + } + if (value.IsDirectory()) + { + Directory.PathStack.Add(new PathInfo + { + ResourceInfo = value + }); + SelectedPathIndex = Directory.PathStack.Count - 1; + } + } + } + + public int SelectedPathIndex + { + get { return _selectedPathIndex; } + set + { + try + { + if (!SetProperty(ref _selectedPathIndex, value)) + { + return; + } + } + catch + { + _selectedPathIndex = -1; + return; + } + + if (Directory?.PathStack == null) + { + return; + } + + while (Directory.PathStack.Count > 0 && Directory.PathStack.Count > _selectedPathIndex + 1) + { + Directory.PathStack.RemoveAt(Directory.PathStack.Count - 1); + } + + StartDirectoryListing(); + } + } + + private async void StartDirectoryListing() + { + ShowProgressIndicator(); + + await Directory.StartDirectoryListing(); + + HideProgressIndicator(); + } + + + public override bool CanRevertState() + { + return SelectedPathIndex > 0; + } + + public override void RevertState() + { + SelectedPathIndex--; + } + } +} \ No newline at end of file diff --git a/NextcloudApp/Views/DirectoryListPage.xaml b/NextcloudApp/Views/DirectoryListPage.xaml index 54db1bd..081c8e4 100644 --- a/NextcloudApp/Views/DirectoryListPage.xaml +++ b/NextcloudApp/Views/DirectoryListPage.xaml @@ -250,6 +250,13 @@ Command="{Binding DataContext.RenameResourceCommand, ElementName=Page}" CommandParameter="{Binding}" Style="{StaticResource MenuFlyoutIconItemStyle}"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NextcloudApp/Views/MoveFileOrFolder.xaml.cs b/NextcloudApp/Views/MoveFileOrFolder.xaml.cs new file mode 100644 index 0000000..a6f5fc8 --- /dev/null +++ b/NextcloudApp/Views/MoveFileOrFolder.xaml.cs @@ -0,0 +1,12 @@ +using Prism.Windows.Mvvm; + +namespace NextcloudApp.Views +{ + public sealed partial class MoveFileOrFolderPage : SessionStateAwarePage + { + public MoveFileOrFolderPage() + { + InitializeComponent(); + } + } +}