This commit is contained in:
Artjom Graf 2018-12-26 13:48:40 +03:00
Родитель af17f41c79
Коммит 51b57574e0
11 изменённых файлов: 90 добавлений и 34 удалений

Просмотреть файл

@ -104,6 +104,7 @@
</Grid> </Grid>
<Grid Grid.Row="2"> <Grid Grid.Row="2">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@ -114,16 +115,20 @@
Classes="Rounded" Classes="Rounded"
IsVisible="{Binding CanLogout}" IsVisible="{Binding CanLogout}"
Command="{Binding Logout}" /> Command="{Binding Logout}" />
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="10"> <Button Grid.Column="1"
Content="Delete"
Classes="Rounded"
Command="{Binding DeleteSelectedFile}" />
<StackPanel Grid.Column="2" Orientation="Horizontal" Margin="10">
<TextBlock Text="You selected: " Foreground="#aaaaaa" /> <TextBlock Text="You selected: " Foreground="#aaaaaa" />
<TextBlock Text="{Binding SelectedFile.Name}" <TextBlock Text="{Binding SelectedFile.Name}"
Foreground="#888888" /> Foreground="#888888" />
</StackPanel> </StackPanel>
<Button Grid.Column="2" <Button Grid.Column="3"
Content="Upload" Content="Upload"
Classes="Rounded" Classes="Rounded"
Command="{Binding UploadToCurrentPath}" /> Command="{Binding UploadToCurrentPath}" />
<Button Grid.Column="3" <Button Grid.Column="4"
Content="Download" Content="Download"
Classes="Rounded" Classes="Rounded"
Command="{Binding DownloadSelectedFile}" /> Command="{Binding DownloadSelectedFile}" />

Просмотреть файл

@ -47,9 +47,7 @@ namespace Camelotia.Presentation.Tests
foreach (var model in real) foreach (var model in real)
expected.Should().Contain(drive => expected.Should().Contain(drive =>
model.Name == drive.Name && model.Name == drive.Name && model.IsFolder);
model.IsFolder == false &&
model.IsDrive);
} }
[Fact] [Fact]

Просмотреть файл

@ -95,7 +95,7 @@ namespace Camelotia.Presentation.Tests
[Fact] [Fact]
public void ShouldBeAbleToOpenSelectedPath() => new TestScheduler().With(scheduler => public void ShouldBeAbleToOpenSelectedPath() => new TestScheduler().With(scheduler =>
{ {
var file = new FileModel("foo", Separator + "foo", true, false, string.Empty); var file = new FileModel("foo", Separator + "foo", true, string.Empty);
_provider.Get(Separator).Returns(Enumerable.Repeat(file, 1)); _provider.Get(Separator).Returns(Enumerable.Repeat(file, 1));
_authViewModel.IsAuthenticated.Returns(true); _authViewModel.IsAuthenticated.Returns(true);
_provider.InitialPath.Returns(Separator); _provider.InitialPath.Returns(Separator);

Просмотреть файл

@ -115,6 +115,7 @@
</Grid> </Grid>
<Grid Grid.Row="2"> <Grid Grid.Row="2">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
@ -124,16 +125,19 @@
Style="{StaticResource ButtonRevealStyle}" Style="{StaticResource ButtonRevealStyle}"
Visibility="{x:Bind ViewModel.CanLogout, Mode=OneWay}" Visibility="{x:Bind ViewModel.CanLogout, Mode=OneWay}"
Command="{x:Bind ViewModel.Logout, Mode=OneWay}" /> Command="{x:Bind ViewModel.Logout, Mode=OneWay}" />
<StackPanel Grid.Column="1" Orientation="Horizontal" Margin="10"> <Button Grid.Column="1" Content="Delete" Margin="0 0 3 0"
Style="{StaticResource ButtonRevealStyle}"
Command="{x:Bind ViewModel.DeleteSelectedFile, Mode=OneWay}" />
<StackPanel Grid.Column="2" Orientation="Horizontal" Margin="10">
<TextBlock Text="You selected:" Margin="0 0 3 0" TextTrimming="CharacterEllipsis" <TextBlock Text="You selected:" Margin="0 0 3 0" TextTrimming="CharacterEllipsis"
Foreground="{ThemeResource ApplicationSecondaryForegroundThemeBrush}" /> Foreground="{ThemeResource ApplicationSecondaryForegroundThemeBrush}" />
<TextBlock Text="{x:Bind ViewModel.SelectedFile.Name, Mode=OneWay}" <TextBlock Text="{x:Bind ViewModel.SelectedFile.Name, Mode=OneWay}"
Foreground="{ThemeResource AppBarSeparatorForegroundThemeBrush}" /> Foreground="{ThemeResource AppBarSeparatorForegroundThemeBrush}" />
</StackPanel> </StackPanel>
<Button Grid.Column="2" Content="Upload" Margin="6 6 0 6" <Button Grid.Column="3" Content="Upload" Margin="6 6 0 6"
Style="{StaticResource ButtonRevealStyle}" Style="{StaticResource ButtonRevealStyle}"
Command="{x:Bind ViewModel.UploadToCurrentPath, Mode=OneWay}" /> Command="{x:Bind ViewModel.UploadToCurrentPath, Mode=OneWay}" />
<Button Grid.Column="3" Content="Download" Margin="6" <Button Grid.Column="4" Content="Download" Margin="6"
Style="{StaticResource ButtonRevealStyle}" Style="{StaticResource ButtonRevealStyle}"
Command="{x:Bind ViewModel.DownloadSelectedFile, Mode=OneWay}" /> Command="{x:Bind ViewModel.DownloadSelectedFile, Mode=OneWay}" />
</Grid> </Grid>

Просмотреть файл

@ -16,7 +16,9 @@ namespace Camelotia.Presentation.Interfaces
ICommand DownloadSelectedFile { get; } ICommand DownloadSelectedFile { get; }
ICommand UploadToCurrentPath { get; } ICommand UploadToCurrentPath { get; }
ICommand DeleteSelectedFile { get; }
ICommand Refresh { get; } ICommand Refresh { get; }
ICommand Logout { get; } ICommand Logout { get; }

Просмотреть файл

@ -24,6 +24,7 @@ namespace Camelotia.Presentation.ViewModels
private readonly ObservableAsPropertyHelper<bool> _isCurrentPathEmpty; private readonly ObservableAsPropertyHelper<bool> _isCurrentPathEmpty;
private readonly ReactiveCommand<Unit, Unit> _downloadSelectedFile; private readonly ReactiveCommand<Unit, Unit> _downloadSelectedFile;
private readonly ReactiveCommand<Unit, Unit> _uploadToCurrentPath; private readonly ReactiveCommand<Unit, Unit> _uploadToCurrentPath;
private readonly ReactiveCommand<Unit, Unit> _deleteSelectedFile;
private readonly ObservableAsPropertyHelper<string> _currentPath; private readonly ObservableAsPropertyHelper<string> _currentPath;
private readonly ObservableAsPropertyHelper<bool> _hasErrors; private readonly ObservableAsPropertyHelper<bool> _hasErrors;
private readonly ObservableAsPropertyHelper<bool> _isLoading; private readonly ObservableAsPropertyHelper<bool> _isLoading;
@ -66,7 +67,7 @@ namespace Camelotia.Presentation.ViewModels
var canOpenCurrentPath = this var canOpenCurrentPath = this
.WhenAnyValue(x => x.SelectedFile) .WhenAnyValue(x => x.SelectedFile)
.Select(file => file != null && (file.IsFolder || file.IsDrive)) .Select(file => file != null && file.IsFolder)
.CombineLatest(_refresh.IsExecuting, (folder, busy) => folder && !busy); .CombineLatest(_refresh.IsExecuting, (folder, busy) => folder && !busy);
_open = ReactiveCommand.Create( _open = ReactiveCommand.Create(
@ -123,7 +124,7 @@ namespace Camelotia.Presentation.ViewModels
var canDownloadSelectedFile = this var canDownloadSelectedFile = this
.WhenAnyValue(x => x.SelectedFile) .WhenAnyValue(x => x.SelectedFile)
.Select(file => file != null && !file.IsFolder && !file.IsDrive) .Select(file => file != null && !file.IsFolder)
.DistinctUntilChanged(); .DistinctUntilChanged();
_downloadSelectedFile = ReactiveCommand.CreateFromObservable( _downloadSelectedFile = ReactiveCommand.CreateFromObservable(
@ -141,7 +142,7 @@ namespace Camelotia.Presentation.ViewModels
.Subscribe(Console.WriteLine); .Subscribe(Console.WriteLine);
this.WhenAnyValue(x => x.SelectedFile) this.WhenAnyValue(x => x.SelectedFile)
.Where(file => file != null && (file.IsFolder || file.IsDrive)) .Where(file => file != null && file.IsFolder)
.Buffer(2, 1) .Buffer(2, 1)
.Select(files => (files.First().Path, files.Last().Path)) .Select(files => (files.First().Path, files.Last().Path))
.DistinctUntilChanged() .DistinctUntilChanged()
@ -159,6 +160,16 @@ namespace Camelotia.Presentation.ViewModels
_logout = ReactiveCommand.CreateFromTask(provider.Logout, canLogout); _logout = ReactiveCommand.CreateFromTask(provider.Logout, canLogout);
_canLogout = canLogout _canLogout = canLogout
.ToProperty(this, x => x.CanLogout, scheduler: currentThread); .ToProperty(this, x => x.CanLogout, scheduler: currentThread);
var canDeleteSelection = this
.WhenAnyValue(x => x.SelectedFile)
.Select(file => file != null && !file.IsFolder);
_deleteSelectedFile = ReactiveCommand.CreateFromTask(
() => provider.Delete(SelectedFile),
canDeleteSelection);
_deleteSelectedFile.InvokeCommand(Refresh);
Auth = authViewModel; Auth = authViewModel;
Activator = new ViewModelActivator(); Activator = new ViewModelActivator();
@ -183,7 +194,9 @@ namespace Camelotia.Presentation.ViewModels
public ICommand DownloadSelectedFile => _downloadSelectedFile; public ICommand DownloadSelectedFile => _downloadSelectedFile;
public ICommand UploadToCurrentPath => _uploadToCurrentPath; public ICommand UploadToCurrentPath => _uploadToCurrentPath;
public ICommand DeleteSelectedFile => _deleteSelectedFile;
public bool IsCurrentPathEmpty => _isCurrentPathEmpty.Value; public bool IsCurrentPathEmpty => _isCurrentPathEmpty.Value;
public IEnumerable<FileModel> Files => _files.Value; public IEnumerable<FileModel> Files => _files.Value;

Просмотреть файл

@ -22,6 +22,8 @@ namespace Camelotia.Services.Interfaces
Task DownloadFile(string from, Stream to); Task DownloadFile(string from, Stream to);
Task Delete(FileModel file);
IObservable<bool> IsAuthorized { get; } IObservable<bool> IsAuthorized { get; }
bool SupportsDirectAuth { get; } bool SupportsDirectAuth { get; }

Просмотреть файл

@ -2,12 +2,11 @@ namespace Camelotia.Services.Models
{ {
public sealed class FileModel public sealed class FileModel
{ {
public FileModel(string name, string path, bool isFolder, bool isDrive, string size) public FileModel(string name, string path, bool isFolder, string size)
{ {
Name = name; Name = name;
Path = path; Path = path;
IsFolder = isFolder; IsFolder = isFolder;
IsDrive = isDrive;
Size = size; Size = size;
} }
@ -17,8 +16,6 @@ namespace Camelotia.Services.Models
public bool IsFolder { get; } public bool IsFolder { get; }
public bool IsDrive { get; }
public string Size { get; } public string Size { get; }
} }
} }

Просмотреть файл

@ -20,7 +20,7 @@ namespace Camelotia.Services.Providers
public IObservable<bool> IsAuthorized { get; } = Observable.Return(true); public IObservable<bool> IsAuthorized { get; } = Observable.Return(true);
public bool SupportsDirectAuth => false; public bool SupportsDirectAuth => false;
public bool SupportsOAuth => false; public bool SupportsOAuth => false;
public string InitialPath => string.Empty; public string InitialPath => string.Empty;
@ -38,19 +38,19 @@ namespace Camelotia.Services.Providers
var driveQuery = from entity in GetAllDrives() var driveQuery = from entity in GetAllDrives()
where entity.IsReady where entity.IsReady
let size = ByteConverter.BytesToString(entity.AvailableFreeSpace) let size = ByteConverter.BytesToString(entity.AvailableFreeSpace)
select new FileModel(entity.Name, entity.Name, false, true, size); select new FileModel(entity.Name, entity.Name, true, size);
return driveQuery return driveQuery
.ToList() .ToList()
.AsEnumerable(); .AsEnumerable();
} }
if (!Directory.Exists(path)) if (!Directory.Exists(path))
throw new ArgumentException("Directory doesn't exist."); throw new ArgumentException("Directory doesn't exist.");
var query = from entity in Directory.GetFileSystemEntries(path) var query = from entity in Directory.GetFileSystemEntries(path)
let isDirectory = IsDirectory(entity) let isDirectory = IsDirectory(entity)
let size = isDirectory ? "*" : ByteConverter.BytesToString(new FileInfo(entity).Length) let size = isDirectory ? "*" : ByteConverter.BytesToString(new FileInfo(entity).Length)
select new FileModel(Path.GetFileName(entity), entity, isDirectory, false, size); select new FileModel(Path.GetFileName(entity), entity, isDirectory, size);
return query return query
.ToList() .ToList()
@ -60,7 +60,7 @@ namespace Camelotia.Services.Providers
public async Task DownloadFile(string from, Stream to) public async Task DownloadFile(string from, Stream to)
{ {
if (IsDirectory(from)) throw new InvalidOperationException("Can't download directory."); if (IsDirectory(from)) throw new InvalidOperationException("Can't download directory.");
using (var fileStream = File.OpenRead(from)) using (var fileStream = File.OpenRead(from))
{ {
fileStream.Seek(0, SeekOrigin.Begin); fileStream.Seek(0, SeekOrigin.Begin);
@ -71,7 +71,7 @@ namespace Camelotia.Services.Providers
public async Task UploadFile(string to, Stream from, string name) public async Task UploadFile(string to, Stream from, string name)
{ {
if (!IsDirectory(to)) throw new InvalidOperationException("Can't upload to a non-directory."); if (!IsDirectory(to)) throw new InvalidOperationException("Can't upload to a non-directory.");
var path = Path.Combine(to, name); var path = Path.Combine(to, name);
using (var fileStream = File.Create(path)) using (var fileStream = File.Create(path))
{ {
@ -80,6 +80,13 @@ namespace Camelotia.Services.Providers
} }
} }
public Task Delete(FileModel file) => Task.Run(() =>
{
var isDirectory = IsDirectory(file.Path);
if (isDirectory) Directory.Delete(file.Path, false);
else File.Delete(file.Path);
});
private static string GetSizeOnAllDisks() private static string GetSizeOnAllDisks()
{ {
var totalBytes = GetAllDrives() var totalBytes = GetAllDrives()

Просмотреть файл

@ -14,6 +14,7 @@ using VkNet.Enums.Filters;
using VkNet.Model; using VkNet.Model;
using VkNet; using VkNet;
using VkNet.Abstractions; using VkNet.Abstractions;
using VkNet.Model.Attachments;
namespace Camelotia.Services.Providers namespace Camelotia.Services.Providers
{ {
@ -78,19 +79,31 @@ namespace Camelotia.Services.Providers
var size = string.Empty; var size = string.Empty;
if (document.Size.HasValue) if (document.Size.HasValue)
size = ByteConverter.BytesToString(document.Size.Value); size = ByteConverter.BytesToString(document.Size.Value);
return new FileModel(document.Title, document.Uri, false, false, size); return new FileModel(document.Title, document.Id.ToString(), false, size);
}); });
} }
public async Task DownloadFile(string from, Stream to) public async Task DownloadFile(string from, Stream to)
{ {
var isValidUriString = Uri.IsWellFormedUriString(from, UriKind.Absolute); var id = long.Parse(from);
var users = await _api.Users.GetAsync(new long[0]);
var currentUser = users.First();
var documents = await _api.Docs.GetByIdAsync(new[] {new Document {Id = id, OwnerId = currentUser.Id}});
var document = documents.First();
Console.WriteLine (document.Uri);
var uri = document.Uri;
var isValidUriString = Uri.IsWellFormedUriString(uri, UriKind.Absolute);
if (!isValidUriString) throw new InvalidOperationException("Uri is invalid."); if (!isValidUriString) throw new InvalidOperationException("Uri is invalid.");
using (var http = new HttpClient()) using (var http = new HttpClient())
using (var response = await http.GetAsync(from).ConfigureAwait(false)) using (var response = await http.GetAsync(uri).ConfigureAwait(false))
using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
await stream.CopyToAsync(to).ConfigureAwait(false); await stream.CopyToAsync(to).ConfigureAwait(false);
await to.FlushAsync();
to.Close();
} }
public async Task UploadFile(string to, Stream from, string name) public async Task UploadFile(string to, Stream from, string name)
@ -111,7 +124,15 @@ namespace Camelotia.Services.Providers
var error = $"Unable to upload {name}{ext} \n{message}"; var error = $"Unable to upload {name}{ext} \n{message}";
throw new Exception(error); throw new Exception(error);
} }
}
public async Task Delete(FileModel file)
{
var id = long.Parse(file.Path);
var users = await _api.Users.GetAsync(new long[0]);
var currentUser = users.First();
await _api.Docs.DeleteAsync(currentUser.Id, id);
} }
private async void EnsureLoggedInIfTokenSaved() private async void EnsureLoggedInIfTokenSaved()

Просмотреть файл

@ -69,7 +69,6 @@ namespace Camelotia.Services.Providers
file.Name, file.Name,
file.Path.Replace("disk:", ""), file.Path.Replace("disk:", ""),
file.Type == "dir", file.Type == "dir",
false,
ByteConverter.BytesToString(file.Size))); ByteConverter.BytesToString(file.Size)));
return models; return models;
@ -113,6 +112,14 @@ namespace Camelotia.Services.Providers
} }
} }
public async Task Delete(FileModel file)
{
var encodedPath = WebUtility.UrlEncode(file.Path);
var pathUrl = CloudApiGetPathBase + encodedPath;
using (var response = await _http.DeleteAsync(pathUrl).ConfigureAwait(false))
response.EnsureSuccessStatusCode();
}
public async Task Logout() public async Task Logout()
{ {
await _tokenStorage.WriteToken<YandexFileSystemProvider>(null); await _tokenStorage.WriteToken<YandexFileSystemProvider>(null);