feat: Finate some details
This commit is contained in:
parent
c80b2759a9
commit
91097940fc
@ -8,6 +8,7 @@
|
||||
<entry key="Flawless.Client/Theme/ToggleSwitch.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HelloSetup/LoginPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HelloSetup/RegisterPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HelloSetup/ServerSetupPageView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HelloView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HelloWindowView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
<entry key="Flawless.Client/Views/HomeView.axaml" value="Flawless.Client/Flawless.Client.csproj" />
|
||||
|
||||
@ -106,6 +106,12 @@ public static class WorkPath
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string FormatPathDirectorySeparator(string path)
|
||||
{
|
||||
return path.Replace(Path.DirectorySeparatorChar, DirectorySeparatorChar)
|
||||
.Replace(Path.AltDirectorySeparatorChar, DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Split work path into path vector.
|
||||
/// </summary>
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
using Avalonia.Controls.Notifications;
|
||||
|
||||
namespace Flawless.Client;
|
||||
|
||||
public static class ErrorGUIHandler
|
||||
{
|
||||
public static void OnError(Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -99,7 +99,7 @@ public class Api : BaseService<Api>
|
||||
public bool RequireRefreshToken()
|
||||
{
|
||||
if (_token.Value == null) return true;
|
||||
if (DateTime.UtcNow.AddMinutes(1) > _token.Value.Expiration) return true;
|
||||
if (DateTime.UtcNow > _token.Value.Expiration!.Value.UtcDateTime.AddMinutes(-2)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -115,6 +115,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return null;
|
||||
}
|
||||
@ -164,6 +165,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
@ -226,6 +228,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
@ -328,6 +331,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
await DeleteFromDiskAsync(repo);
|
||||
return false;
|
||||
@ -361,6 +365,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
@ -417,6 +422,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
@ -435,7 +441,6 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
if (manifest == null) return null;
|
||||
|
||||
// Prepare folders
|
||||
var path = PathUtility.GetWorkspacePath(repo.OwnerName, repo.Name);
|
||||
var depotsRoot = PathUtility.GetWorkspaceDepotCachePath(repo.OwnerName, repo.Name);
|
||||
Directory.CreateDirectory(depotsRoot);
|
||||
|
||||
@ -464,18 +469,22 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
|
||||
// Create mapping dictionary
|
||||
var mappingDict = downloadedDepots.ToDictionary(i => i.Item1, i => i.Item2!);
|
||||
var streamMap = downloadedDepots.ToDictionary(i => i.Item1, i => i.Item2!);
|
||||
foreach (var dl in mainDepotLabel)
|
||||
{
|
||||
if (mappingDict.ContainsKey(dl.Id)) continue;
|
||||
// If this file is not being opened, open it from file system
|
||||
if (!streamMap.ContainsKey(dl.Id))
|
||||
{
|
||||
var dst = Path.Combine(depotsRoot, dl.Id.ToString());
|
||||
mappingDict.Add(dl.Id, new FileStream(dst, FileMode.Create));
|
||||
streamMap.Add(dl.Id, new FileStream(dst, FileMode.Open, FileAccess.Read, FileShare.Read));
|
||||
}
|
||||
}
|
||||
|
||||
return new RepositoryFileTreeAccessor(mappingDict, manifest.Value);
|
||||
return new RepositoryFileTreeAccessor(streamMap, manifest.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
if (downloadedDepots != null)
|
||||
foreach (var t in downloadedDepots)
|
||||
{
|
||||
@ -537,6 +546,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return null;
|
||||
}
|
||||
@ -561,6 +571,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return null;
|
||||
}
|
||||
@ -575,6 +586,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
@ -630,8 +642,15 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
if (accessor == null) return null; //todo this is a really fatal issue...
|
||||
if (localDb.RepoAccessor != null)
|
||||
{
|
||||
try { await localDb.RepoAccessor.DisposeAsync(); }
|
||||
catch (Exception e) { Console.WriteLine(e); }
|
||||
try
|
||||
{
|
||||
await localDb.RepoAccessor.DisposeAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Point to newest state.
|
||||
@ -644,6 +663,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return null;
|
||||
}
|
||||
@ -735,6 +755,7 @@ public class RepositoryService : BaseService<RepositoryService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Directory.Delete(repoWs, true);
|
||||
Console.WriteLine(e);
|
||||
return null;
|
||||
|
||||
@ -50,6 +50,7 @@ public partial class UserService : BaseService<UserService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return null;
|
||||
}
|
||||
@ -80,6 +81,7 @@ public partial class UserService : BaseService<UserService>
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UIHelper.NotifyError(e);
|
||||
Console.WriteLine(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
96
Flawless.Client/UIHelper.cs
Normal file
96
Flawless.Client/UIHelper.cs
Normal file
@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Controls.Notifications;
|
||||
using Flawless.Client.ViewModels.ModalBox;
|
||||
using Flawless.Client.Views.ModalBox;
|
||||
using Ursa.Controls;
|
||||
using Notification = Ursa.Controls.Notification;
|
||||
using WindowNotificationManager = Ursa.Controls.WindowNotificationManager;
|
||||
|
||||
namespace Flawless.Client;
|
||||
|
||||
public static class UIHelper
|
||||
{
|
||||
|
||||
private static WindowNotificationManager _notificationManager = null!;
|
||||
|
||||
public static WindowNotificationManager Notify
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_notificationManager != null) return _notificationManager!;
|
||||
|
||||
var lf = ((IClassicDesktopStyleApplicationLifetime)App.Current.ApplicationLifetime);
|
||||
if (!WindowNotificationManager.TryGetNotificationManager(lf.MainWindow!, out _notificationManager))
|
||||
throw new Exception("Can not get notification manager");
|
||||
|
||||
_notificationManager!.Position = NotificationPosition.TopCenter;
|
||||
return _notificationManager!;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void NotifyError(Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
var content = ex.ToString();
|
||||
if (content.Length > 100) content = content.Substring(0, 100) + "...";
|
||||
var nf = new Notification(ex.GetType().Name, content, NotificationType.Error);
|
||||
Notify.Show(nf);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("Can not notify error to users: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void NotifyError(string title, string content)
|
||||
{
|
||||
try
|
||||
{
|
||||
var nf = new Notification(title, content, NotificationType.Error);
|
||||
Notify.Show(nf);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Can not notify error to users: {title} - {content}, {e}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Task<DialogResult> SimpleAskAsync(string content, DialogMode mode = DialogMode.None)
|
||||
{
|
||||
var opt = new OverlayDialogOptions
|
||||
{
|
||||
FullScreen = false,
|
||||
Buttons = DialogButton.YesNo,
|
||||
CanResize = false,
|
||||
CanDragMove = false,
|
||||
IsCloseButtonVisible = true,
|
||||
CanLightDismiss = true,
|
||||
Mode = mode
|
||||
};
|
||||
|
||||
var vm = new SimpleMessageDialogViewModel(content);
|
||||
return OverlayDialog.ShowModal<SimpleMessageDialogView, SimpleMessageDialogViewModel>(vm, AppDefaultValues.HostId, opt);
|
||||
}
|
||||
|
||||
public static Task SimpleAlert(string content, DialogMode mode = DialogMode.Error)
|
||||
{
|
||||
var opt = new OverlayDialogOptions
|
||||
{
|
||||
FullScreen = false,
|
||||
Buttons = DialogButton.YesNo,
|
||||
CanResize = false,
|
||||
CanDragMove = false,
|
||||
IsCloseButtonVisible = true,
|
||||
CanLightDismiss = true,
|
||||
Mode = mode
|
||||
};
|
||||
|
||||
var vm = new SimpleMessageDialogViewModel(content);
|
||||
return OverlayDialog.ShowModal<SimpleMessageDialogView, SimpleMessageDialogViewModel>(vm, AppDefaultValues.HostId, opt);
|
||||
}
|
||||
}
|
||||
@ -21,8 +21,6 @@ public partial class LoginPageViewModel : ViewModelBase, IRoutableViewModel
|
||||
|
||||
[Reactive] private string _password = "4453A2b33";
|
||||
|
||||
[Reactive(SetModifier = AccessModifier.Protected)] private string _issue = String.Empty;
|
||||
|
||||
public IObservable<bool> CanLogin;
|
||||
|
||||
public IObservable<bool> CanRegister => Api.C.Status.Select(s => s != null && s.AllowPublicRegister);
|
||||
@ -56,12 +54,12 @@ public partial class LoginPageViewModel : ViewModelBase, IRoutableViewModel
|
||||
catch (ApiException ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"Login as '{Username}' Failed: {ex.Content}");
|
||||
Issue = ex.Content ?? String.Empty;
|
||||
UIHelper.NotifyError(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"Login as '{Username}' Failed: {ex}");
|
||||
Issue = ex.Message;
|
||||
UIHelper.NotifyError(ex);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Login as '{Username}' success!");
|
||||
|
||||
@ -23,8 +23,6 @@ public partial class RegisterPageViewModel : ViewModelBase, IRoutableViewModel
|
||||
|
||||
[Reactive] private string _password;
|
||||
|
||||
[Reactive(SetModifier = AccessModifier.Protected)] private string _issue;
|
||||
|
||||
public RegisterPageViewModel(IScreen hostScreen)
|
||||
{
|
||||
HostScreen = hostScreen;
|
||||
@ -49,12 +47,12 @@ public partial class RegisterPageViewModel : ViewModelBase, IRoutableViewModel
|
||||
catch (ApiException ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"Register as '{Username}' Failed: {ex.Content}");
|
||||
Issue = ex.Content ?? String.Empty;
|
||||
UIHelper.NotifyError(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync($"Register as '{Username}' Failed: {ex}");
|
||||
Issue = ex.Message;
|
||||
UIHelper.NotifyError(ex);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Register as '{Username}' success!");
|
||||
|
||||
@ -10,6 +10,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Controls.Models.TreeDataGrid;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using Flawless.Abstraction;
|
||||
using Flawless.Client.Models;
|
||||
using Flawless.Client.Service;
|
||||
using Flawless.Core.Modal;
|
||||
@ -106,13 +107,14 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
|
||||
public HierarchicalTreeDataGridSource<LocalChangesNode> LocalChange { get; }
|
||||
|
||||
|
||||
public HierarchicalTreeDataGridSource<LocalChangesNode> FileTree { get; }
|
||||
|
||||
public FlatTreeDataGridSource<CommitTransitNode> Commits { get; }
|
||||
|
||||
public ObservableCollection<LocalChangesNode> LocalChangeSetRaw { get; } = new();
|
||||
|
||||
public ObservableCollection<LocalChangesNode> CurrentCommitFileTreeRaw { get; } = new();
|
||||
|
||||
public UserModel User { get; }
|
||||
|
||||
[Reactive] private bool _autoDetectChanges = true;
|
||||
@ -181,9 +183,48 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
}
|
||||
};
|
||||
|
||||
DetectLocalChangesAsyncCommand.Execute();
|
||||
FileTree = new HierarchicalTreeDataGridSource<LocalChangesNode>(CurrentCommitFileTreeRaw)
|
||||
{
|
||||
Columns =
|
||||
{
|
||||
new HierarchicalExpanderColumn<LocalChangesNode>(
|
||||
new TextColumn<LocalChangesNode, string>(
|
||||
"Name",
|
||||
n => Path.GetFileName(n.FullPath)),
|
||||
n => n.Contents),
|
||||
|
||||
new TextColumn<LocalChangesNode, string>(
|
||||
"File Type",
|
||||
n => n.Contents != null ? "Folder" : Path.GetExtension(n.FullPath)),
|
||||
|
||||
new TextColumn<LocalChangesNode, ulong>(
|
||||
"Size",
|
||||
n => 0),
|
||||
|
||||
new TextColumn<LocalChangesNode, DateTime?>(
|
||||
"ModifiedTime", n => n.ModifiedTime.HasValue ? n.ModifiedTime.Value.ToLocalTime() : null),
|
||||
}
|
||||
};
|
||||
|
||||
_ = StartupTasksAsync();
|
||||
}
|
||||
|
||||
private async Task StartupTasksAsync()
|
||||
{
|
||||
await DetectLocalChangesAsyncCommand.Execute();
|
||||
await RendererFileTreeAsync();
|
||||
}
|
||||
|
||||
private async ValueTask RendererFileTreeAsync()
|
||||
{
|
||||
if (LocalDatabase.RepoAccessor == null) return;
|
||||
var accessor = LocalDatabase.RepoAccessor;
|
||||
var nodes = await CalculateFileTreeOfChangesNodeAsync(accessor.Select(
|
||||
f => new LocalFileTreeAccessor.ChangeRecord(ChangeType.Add, f)));
|
||||
|
||||
CurrentCommitFileTreeRaw.Clear();
|
||||
CurrentCommitFileTreeRaw.AddRange(nodes);
|
||||
}
|
||||
|
||||
private void CollectChanges(List<LocalFileTreeAccessor.ChangeRecord> store, IEnumerable<LocalChangesNode> changesNode)
|
||||
{
|
||||
@ -201,21 +242,72 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
}
|
||||
}
|
||||
|
||||
private Task<List<LocalChangesNode>> CalculateFileTreeOfChangesNodeAsync(IEnumerable<LocalFileTreeAccessor.ChangeRecord> changesNode)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
// Generate a map of all folders
|
||||
var folderMap = new Dictionary<string, LocalChangesNode>();
|
||||
var nodes = new List<LocalChangesNode>();
|
||||
|
||||
foreach (var file in changesNode)
|
||||
{
|
||||
var n = LocalChangesNode.FromWorkspaceFile(file);
|
||||
var parentNode = AddParentToMap(file.File.WorkPath);
|
||||
if (parentNode == null) nodes.Add(n);
|
||||
else parentNode.Contents!.Add(n);
|
||||
}
|
||||
|
||||
return nodes;
|
||||
|
||||
LocalChangesNode? AddParentToMap(string path)
|
||||
{
|
||||
path = WorkPath.FormatPathDirectorySeparator(Path.GetDirectoryName(path) ?? string.Empty);
|
||||
|
||||
// 如果为空,则其直接文件夹就是根目录
|
||||
if (string.IsNullOrEmpty(path)) return null;
|
||||
|
||||
// 如果直接文件夹已经存在,则不再生成,直接返回即可
|
||||
if (folderMap.TryGetValue(path, out var node)) return node;
|
||||
|
||||
// 生成当前文件夹,并先找到这个文件夹的直接文件夹
|
||||
node = LocalChangesNode.FromFolder(path);
|
||||
var parent = AddParentToMap(path);
|
||||
folderMap.Add(path, node);
|
||||
if (parent != null) parent.Contents!.Add(node);
|
||||
else nodes.Add(node);
|
||||
|
||||
// 将新建的文件夹告知调用方
|
||||
return node;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private async Task CommitSelectedChangesAsync()
|
||||
{
|
||||
var changes = new List<LocalFileTreeAccessor.ChangeRecord>();
|
||||
CollectChanges(changes, LocalChangeSetRaw);
|
||||
|
||||
if (changes.Count == 0) return;
|
||||
var manifest = await RepositoryService.C.CommitWorkspaceAsBaselineAsync(Repository, changes,
|
||||
LocalDatabase.CommitMessage ?? string.Empty);
|
||||
if (changes.Count == 0)
|
||||
{
|
||||
await UIHelper.SimpleAlert("You haven't choose any changes yet!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(LocalDatabase.CommitMessage))
|
||||
{
|
||||
await UIHelper.SimpleAlert("Commit message can not be empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
var manifest = await RepositoryService.C.CommitWorkspaceAsBaselineAsync(Repository, changes, LocalDatabase.CommitMessage!);
|
||||
if (manifest == null) return;
|
||||
|
||||
LocalDatabase.LocalAccessor.SetBaseline(manifest.Value.FilePaths);
|
||||
LocalDatabase.CommitMessage = string.Empty;
|
||||
await DetectLocalChangesAsyncCommand.Execute();
|
||||
await RendererFileTreeAsync();
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
@ -242,37 +334,28 @@ public partial class RepositoryViewModel : RoutableViewModelBase
|
||||
[ReactiveCommand]
|
||||
private async ValueTask DetectLocalChangesAsync()
|
||||
{
|
||||
var ns = await Task.Run(() =>
|
||||
var ns = await Task.Run(async () =>
|
||||
{
|
||||
LocalDatabase.LocalAccessor.Refresh();
|
||||
|
||||
// Generate a map of all folders
|
||||
var folderMap = new Dictionary<string, LocalChangesNode>();
|
||||
foreach (var k in LocalDatabase.LocalAccessor.Changes.Keys)
|
||||
AddParentToMap(k);
|
||||
|
||||
var nodes = new List<LocalChangesNode>();
|
||||
foreach (var file in LocalDatabase.LocalAccessor.Changes.Values)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(file.File.WorkPath);
|
||||
var n = LocalChangesNode.FromWorkspaceFile(file);
|
||||
if (string.IsNullOrEmpty(directory)) nodes.Add(n);
|
||||
else folderMap[directory].Contents!.Add(n);
|
||||
}
|
||||
|
||||
nodes.AddRange(folderMap.Values);
|
||||
return nodes;
|
||||
|
||||
void AddParentToMap(string path)
|
||||
{
|
||||
var parent = Path.GetDirectoryName(path);
|
||||
if (string.IsNullOrEmpty(parent) || folderMap.ContainsKey(parent)) return;
|
||||
|
||||
folderMap.Add(parent, LocalChangesNode.FromFolder(parent));
|
||||
}
|
||||
return await CalculateFileTreeOfChangesNodeAsync(LocalDatabase.LocalAccessor.Changes.Values);
|
||||
});
|
||||
|
||||
LocalChangeSetRaw.Clear();
|
||||
LocalChangeSetRaw.AddRange(ns);
|
||||
}
|
||||
|
||||
[ReactiveCommand]
|
||||
private void SelectAllChanges()
|
||||
{
|
||||
foreach (var n in LocalChangeSetRaw)
|
||||
n.Included = true;
|
||||
}
|
||||
|
||||
|
||||
[ReactiveCommand]
|
||||
private void DeselectAllChanges()
|
||||
{
|
||||
foreach (var n in LocalChangeSetRaw)
|
||||
n.Included = false;
|
||||
}
|
||||
}
|
||||
@ -14,8 +14,6 @@ public partial class ServerSetupPageViewModel : RoutableViewModelBase
|
||||
|
||||
[Reactive] private string _host = "http://localhost:5256/";
|
||||
|
||||
[Reactive(SetModifier = AccessModifier.Protected)] private string? _issue;
|
||||
|
||||
public IObservable<bool> CanSetHost { get; }
|
||||
|
||||
public ServerSetupPageViewModel(IScreen hostScreen) : base(hostScreen)
|
||||
@ -33,19 +31,18 @@ public partial class ServerSetupPageViewModel : RoutableViewModelBase
|
||||
{
|
||||
try
|
||||
{
|
||||
Issue = string.Empty;
|
||||
await Api.C.SetGatewayAsync(Host);
|
||||
HostScreen.Router.Navigate.Execute(new LoginPageViewModel(HostScreen));
|
||||
}
|
||||
catch (ApiException ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync("Can not connect to server: " + ex.ToString());
|
||||
Issue = ex.Content;
|
||||
UIHelper.NotifyError(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Error.WriteLineAsync("Can not connect to server: " + ex.ToString());
|
||||
Issue = ex.Message;
|
||||
UIHelper.NotifyError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,5 @@
|
||||
<Button Content="Login" Command="{Binding LoginCommand}"/>
|
||||
<Button Content="Register" Command="{Binding RegisterCommand}"/>
|
||||
</StackPanel>
|
||||
<Label Content="{Binding Issue}" Foreground="Red"/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@ -20,6 +20,5 @@
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="{DynamicResource Semi}">
|
||||
<Button Content="Register" Command="{Binding RegisterCommand}"/>
|
||||
</StackPanel>
|
||||
<Label Content="{Binding Issue}" Foreground="Red"/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@ -16,6 +16,5 @@
|
||||
<StackPanel Spacing="10" HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<TextBox Watermark="Host" Text="{Binding Host, Mode=TwoWay}"/>
|
||||
<Button Content="Connect" Command="{Binding SetHostCommand}"/>
|
||||
<Label Content="{Binding Issue}" Foreground="Red"/>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
|
||||
@ -21,5 +21,6 @@
|
||||
<Panel>
|
||||
<rxui:RoutedViewHost Router="{Binding Router}"/>
|
||||
<ursa:OverlayDialogHost HostId="Overlay"/>
|
||||
<ursa:WindowNotificationManager/>
|
||||
</Panel>
|
||||
</ursa:UrsaWindow>
|
||||
@ -10,15 +10,9 @@
|
||||
|
||||
<Grid ColumnDefinitions="2*, *">
|
||||
<Border Grid.Column="0" Classes="Shadow" Theme="{StaticResource CardBorder}">
|
||||
<Grid RowDefinitions="Auto, *">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8"
|
||||
IsVisible="{Binding IsOwnerRole}">
|
||||
<u:IconButton Icon="{StaticResource SemiIconEdit}" Content="Edit"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<SelectableTextBlock Text="{Binding Repository.Description}"/>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ScrollViewer Grid.Column="1" Margin="36 20">
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Flawless.Client.Views.RepositoryPage.RepoFileTreePageView">
|
||||
<Grid ColumnDefinitions="2*, *">
|
||||
<TreeDataGrid Grid.Column="0">
|
||||
<TreeDataGrid Grid.Column="0" Grid.ColumnSpan="2" Source="{Binding FileTree}">
|
||||
</TreeDataGrid>
|
||||
<!-- <Border Grid.Column="1" Classes="Shadow" Theme="{StaticResource CardBorder}"> -->
|
||||
<!-- <ScrollViewer> -->
|
||||
|
||||
@ -6,7 +6,26 @@
|
||||
x:DataType="vm:RepositoryViewModel"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Flawless.Client.Views.RepositoryPage.RepoSettingPageView">
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<Label Content="Sit down and wait patience."></Label>
|
||||
<TabControl TabStripPlacement="Left">
|
||||
<TabItem Header="Members">
|
||||
<StackPanel Width="400" HorizontalAlignment="Stretch">
|
||||
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Statics" IsVisible="{Binding IsDeveloperRole}">
|
||||
<StackPanel Width="400" HorizontalAlignment="Stretch">
|
||||
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Admin Area" IsVisible="{Binding IsOwnerRole}">
|
||||
<StackPanel Width="400" HorizontalAlignment="Stretch">
|
||||
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
<TabItem Header="Hooks" IsVisible="{Binding IsOwnerRole}">
|
||||
<StackPanel Width="400" HorizontalAlignment="Stretch">
|
||||
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</UserControl>
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
<u:IconButton Icon="{StaticResource SemiIconRefresh}" Content="Detect"
|
||||
Command="{Binding DetectLocalChangesAsyncCommand}"/>
|
||||
<u:IconButton Icon="{StaticResource SemiIconDownload}" Content="Pull"/>
|
||||
<u:IconButton Icon="{StaticResource SemiIconCheckList}" Content="Select"/>
|
||||
<u:IconButton Icon="{StaticResource SemiIconList}" Content="Deselect"/>
|
||||
<!-- <ToggleButton Content="Auto Refresh" IsChecked="{Binding AutoDetectChanges}"/> -->
|
||||
</StackPanel>
|
||||
<TreeDataGrid Grid.Row="1" Grid.Column="0" Source="{Binding LocalChange}" CanUserSortColumns="True"/>
|
||||
|
||||
@ -382,6 +382,7 @@ public class RepositoryInnieController(
|
||||
}
|
||||
|
||||
[HttpPost("create_commit")]
|
||||
[RequestSizeLimit(long.MaxValue)]
|
||||
[ProducesResponseType<CommitSuccessResponse>(200)]
|
||||
public async Task<IActionResult> CommitAsync(string userName, string repositoryName, [FromForm] FormCommitRequest req)
|
||||
{
|
||||
|
||||
@ -8,6 +8,7 @@ using Flawless.Server.Services;
|
||||
using Flawless.Server.Utility;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
@ -38,6 +39,14 @@ public static class Program
|
||||
builder.WebHost.ConfigureKestrel(opt =>
|
||||
{
|
||||
opt.Limits.MaxRequestBodySize = long.MaxValue; // As big as possible...
|
||||
opt.Limits.MaxRequestHeaderCount = int.MaxValue; // As big as possible...
|
||||
});
|
||||
|
||||
builder.Services.Configure<FormOptions>(opt =>
|
||||
{
|
||||
opt.MultipartBodyLengthLimit = long.MaxValue;
|
||||
opt.ValueLengthLimit = int.MaxValue;
|
||||
opt.MultipartHeadersLengthLimit = int.MaxValue;
|
||||
});
|
||||
|
||||
// Api related
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user