using Dalamud.Interface; using Dalamud.Interface.Colors; using Dalamud.Interface.Windowing; using ImGuiNET; using MareSynchronos.WebAPI; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Numerics; using System.Threading.Tasks; using MareSynchronos.API; using MareSynchronos.Utils; namespace MareSynchronos.UI { public delegate void SwitchUi(); public class MainUi : Window, IDisposable { private readonly Configuration _configuration; private readonly WindowSystem _windowSystem; private readonly ApiController _apiController; private readonly UiShared _uiShared; public event SwitchUi? SwitchFromMainUiToIntro; public MainUi(WindowSystem windowSystem, UiShared uiShared, Configuration configuration, ApiController apiController) : base("Mare Synchronos Settings", ImGuiWindowFlags.None) { Logger.Debug("Creating " + nameof(MainUi)); SizeConstraints = new WindowSizeConstraints() { MinimumSize = new Vector2(800, 400), MaximumSize = new Vector2(800, 2000), }; _configuration = configuration; _windowSystem = windowSystem; _apiController = apiController; _uiShared = uiShared; windowSystem.AddWindow(this); } public void Dispose() { Logger.Debug("Disposing " + nameof(MainUi)); _windowSystem.RemoveWindow(this); } public override void Draw() { if (!IsOpen) { return; } var pluginState = _uiShared.DrawOtherPluginState(); if (pluginState) DrawSettingsContent(); } private void DrawSettingsContent() { _uiShared.PrintServerState(); ImGui.Separator(); ImGui.SetWindowFontScale(1.2f); ImGui.Text("Your UID"); ImGui.SameLine(); if (_apiController.IsConnected) { ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.UID); ImGui.SameLine(); ImGui.SetWindowFontScale(1.0f); if (ImGui.Button("Copy UID")) { ImGui.SetClipboardText(_apiController.UID); } ImGui.Text("Share this UID to other Mare users so they pair their client with yours."); } else { string error = _configuration.FullPause ? "Fully Paused" : _apiController.ServerAlive ? "Unauthorized" : "Service unavailable"; ImGui.TextColored(ImGuiColors.DalamudRed, $"No UID ({error})"); ImGui.SetWindowFontScale(1.0f); if (_apiController.ServerAlive && !_configuration.FullPause) { ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); UiShared.TextWrapped("Your account is not present on the service anymore or you are banned."); ImGui.PopStyleColor(); ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); UiShared.TextWrapped("If you think your secret key is just invalid, use the following button to reset the local secret key to be able to re-register. If you continue to see this message after registering, tough luck, asshole."); ImGui.PopStyleColor(); if (ImGui.Button("Reset Secret Key")) { _configuration.ClientSecret.Remove(_configuration.ApiUri); _configuration.Save(); SwitchFromMainUiToIntro?.Invoke(); } } } ImGui.Separator(); if (_apiController.IsConnected) DrawPairedClientsContent(); DrawFileCacheSettings(); if (_apiController.IsConnected) DrawCurrentTransfers(); DrawUserAdministration(_apiController.IsConnected); if (_apiController.IsConnected && _apiController.IsModerator) DrawAdministration(); } private string _forbiddenFileHashEntry = string.Empty; private string _forbiddenFileHashForbiddenBy = string.Empty; private string _bannedUserHashEntry = string.Empty; private string _bannedUserReasonEntry = string.Empty; private void DrawAdministration() { if (ImGui.TreeNode("Administrative Actions")) { if (ImGui.TreeNode("Forbidden Files Changes")) { if (ImGui.BeginTable("ForbiddenFilesTable", 3, ImGuiTableFlags.RowBg)) { ImGui.TableSetupColumn("File Hash", ImGuiTableColumnFlags.None, 290); ImGui.TableSetupColumn("Forbidden By", ImGuiTableColumnFlags.None, 290); ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 70); ImGui.TableHeadersRow(); foreach (var forbiddenFile in _apiController.ForbiddenFiles) { ImGui.TableNextColumn(); ImGui.Text(forbiddenFile.Hash); ImGui.TableNextColumn(); string by = forbiddenFile.ForbiddenBy; if (ImGui.InputText("##forbiddenBy" + forbiddenFile.Hash, ref by, 255)) { forbiddenFile.ForbiddenBy = by; } ImGui.TableNextColumn(); if (_apiController.IsAdmin) { ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button( FontAwesomeIcon.Upload.ToIconString() + "##updateFile" + forbiddenFile.Hash)) { _ = _apiController.AddOrUpdateForbiddenFileEntry(forbiddenFile); } ImGui.SameLine(); if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString() + "##deleteFile" + forbiddenFile.Hash)) { _ = _apiController.DeleteForbiddenFileEntry(forbiddenFile); } ImGui.PopFont(); } } if (_apiController.IsAdmin) { ImGui.TableNextColumn(); ImGui.InputText("##addFileHash", ref _forbiddenFileHashEntry, 255); ImGui.TableNextColumn(); ImGui.InputText("##addForbiddenBy", ref _forbiddenFileHashForbiddenBy, 255); ImGui.TableNextColumn(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString() + "##addForbiddenFile")) { _ = _apiController.AddOrUpdateForbiddenFileEntry(new ForbiddenFileDto() { ForbiddenBy = _forbiddenFileHashForbiddenBy, Hash = _forbiddenFileHashEntry }); } ImGui.PopFont(); ImGui.NextColumn(); } ImGui.EndTable(); } ImGui.TreePop(); } if (ImGui.TreeNode("Banned Users")) { if (ImGui.BeginTable("BannedUsersTable", 3, ImGuiTableFlags.RowBg)) { ImGui.TableSetupColumn("Character Hash", ImGuiTableColumnFlags.None, 290); ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 290); ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 70); ImGui.TableHeadersRow(); foreach (var bannedUser in _apiController.BannedUsers) { ImGui.TableNextColumn(); ImGui.Text(bannedUser.CharacterHash); ImGui.TableNextColumn(); string reason = bannedUser.Reason; ImGuiInputTextFlags moderatorFlags = _apiController.IsModerator ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None; if (ImGui.InputText("##bannedReason" + bannedUser.CharacterHash, ref reason, 255, moderatorFlags)) { bannedUser.Reason = reason; } ImGui.TableNextColumn(); ImGui.PushFont(UiBuilder.IconFont); if (_apiController.IsAdmin) { if (ImGui.Button(FontAwesomeIcon.Upload.ToIconString() + "##updateUser" + bannedUser.CharacterHash)) { _ = _apiController.AddOrUpdateBannedUserEntry(bannedUser); } ImGui.SameLine(); } if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString() + "##deleteUser" + bannedUser.CharacterHash)) { _ = _apiController.DeleteBannedUserEntry(bannedUser); } ImGui.PopFont(); } ImGui.TableNextColumn(); ImGui.InputText("##addUserHash", ref _bannedUserHashEntry, 255); ImGui.TableNextColumn(); if (_apiController.IsAdmin) { ImGui.InputText("##addUserReason", ref _bannedUserReasonEntry, 255); } else { _bannedUserReasonEntry = "Banned by " + _uiShared.PlayerName; ImGui.InputText("##addUserReason", ref _bannedUserReasonEntry, 255, ImGuiInputTextFlags.ReadOnly); } ImGui.TableNextColumn(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString() + "##addForbiddenFile")) { _ = _apiController.AddOrUpdateBannedUserEntry(new BannedUserDto() { CharacterHash = _forbiddenFileHashForbiddenBy, Reason = _forbiddenFileHashEntry }); } ImGui.PopFont(); ImGui.EndTable(); } ImGui.TreePop(); } if (ImGui.TreeNode("Online Users")) { if (ImGui.Button("Refresh Online Users")) { _ = _apiController.RefreshOnlineUsers(); } if (ImGui.BeginTable("OnlineUsersTable", 3, ImGuiTableFlags.RowBg)) { ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 100); ImGui.TableSetupColumn("Character Hash", ImGuiTableColumnFlags.None, 300); ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 70); ImGui.TableHeadersRow(); foreach (var onlineUser in _apiController.AdminOnlineUsers) { ImGui.TableNextColumn(); ImGui.PushFont(UiBuilder.IconFont); string icon = onlineUser.IsModerator ? FontAwesomeIcon.ChessKing.ToIconString() : onlineUser.IsAdmin ? FontAwesomeIcon.Crown.ToIconString() : FontAwesomeIcon.User.ToIconString(); ImGui.Text(icon); ImGui.PopFont(); ImGui.SameLine(); ImGui.Text(onlineUser.UID); ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Copy.ToIconString() + "##onlineUserCopyUID" + onlineUser.CharacterNameHash)) { ImGui.SetClipboardText(onlineUser.UID); } ImGui.PopFont(); ImGui.TableNextColumn(); string charNameHash = onlineUser.CharacterNameHash; ImGui.InputText("##onlineUserHash" + onlineUser.CharacterNameHash, ref charNameHash, 255, ImGuiInputTextFlags.ReadOnly); ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Copy.ToIconString() + "##onlineUserCopyHash" + onlineUser.CharacterNameHash)) { ImGui.SetClipboardText(onlineUser.UID); } ImGui.PopFont(); ImGui.TableNextColumn(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.SkullCrossbones.ToIconString() + "##onlineUserBan" + onlineUser.CharacterNameHash)) { _ = _apiController.AddOrUpdateBannedUserEntry(new BannedUserDto { CharacterHash = onlineUser.CharacterNameHash, Reason = "Banned by " + _uiShared.PlayerName }); } ImGui.SameLine(); if (onlineUser.UID != _apiController.UID && _apiController.IsAdmin) { if (!onlineUser.IsModerator) { if (ImGui.Button(FontAwesomeIcon.ChessKing.ToIconString() + "##onlineUserModerator" + onlineUser.CharacterNameHash)) { _apiController.PromoteToModerator(onlineUser.UID); } } else { if (ImGui.Button(FontAwesomeIcon.User.ToIconString() + "##onlineUserNonModerator" + onlineUser.CharacterNameHash)) { _apiController.DemoteFromModerator(onlineUser.UID); } } } ImGui.PopFont(); } ImGui.EndTable(); } ImGui.TreePop(); } ImGui.TreePop(); } } private bool _deleteFilesPopupModalShown = false; private bool _deleteAccountPopupModalShown = false; private void DrawUserAdministration(bool serverAlive) { if (ImGui.TreeNode( $"User Administration")) { if (serverAlive) { if (ImGui.Button("Delete all my files")) { _deleteFilesPopupModalShown = true; ImGui.OpenPopup("Delete all your files?"); } UiShared.DrawHelpText("Completely deletes all your uploaded files on the service."); if (ImGui.BeginPopupModal("Delete all your files?", ref _deleteFilesPopupModalShown, ImGuiWindowFlags.AlwaysAutoResize)) { UiShared.TextWrapped( "All your own uploaded files on the service will be deleted.\nThis operation cannot be undone."); ImGui.Text("Are you sure you want to continue?"); ImGui.Separator(); if (ImGui.Button("Delete everything", new Vector2(150, 0))) { Task.Run(() => _apiController.DeleteAllMyFiles()); ImGui.CloseCurrentPopup(); _deleteFilesPopupModalShown = false; } ImGui.SameLine(); if (ImGui.Button("Cancel##cancelDelete", new Vector2(150, 0))) { ImGui.CloseCurrentPopup(); _deleteFilesPopupModalShown = false; } ImGui.EndPopup(); } if (ImGui.Button("Delete account")) { _deleteAccountPopupModalShown = true; ImGui.OpenPopup("Delete your account?"); } UiShared.DrawHelpText("Completely deletes your account and all uploaded files to the service."); if (ImGui.BeginPopupModal("Delete your account?", ref _deleteAccountPopupModalShown, ImGuiWindowFlags.AlwaysAutoResize)) { UiShared.TextWrapped( "Your account and all associated files and data on the service will be deleted."); UiShared.TextWrapped("Your UID will be removed from all pairing lists."); ImGui.Text("Are you sure you want to continue?"); ImGui.Separator(); if (ImGui.Button("Delete account", new Vector2(150, 0))) { Task.Run(() => _apiController.DeleteAccount()); ImGui.CloseCurrentPopup(); _deleteAccountPopupModalShown = false; SwitchFromMainUiToIntro?.Invoke(); } ImGui.SameLine(); if (ImGui.Button("Cancel##cancelDelete", new Vector2(150, 0))) { ImGui.CloseCurrentPopup(); _deleteAccountPopupModalShown = false; } ImGui.EndPopup(); } } if (!_configuration.FullPause) { ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); UiShared.TextWrapped("Note: to change servers you need to pause Mare Synchronos."); ImGui.PopStyleColor(); } var marePaused = _configuration.FullPause; if (_configuration.HasValidSetup()) { if (ImGui.Checkbox("Pause Mare Synchronos", ref marePaused)) { _configuration.FullPause = marePaused; _configuration.Save(); Task.Run(_apiController.CreateConnections); } UiShared.DrawHelpText("Completely pauses the sync and clear your current data (not uploaded files) on the service."); } else { ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudYellow); ImGui.TextUnformatted("You cannot resume pause without a valid account on the service."); ImGui.PopStyleColor(); } if (marePaused) { _uiShared.DrawServiceSelection(() => SwitchFromMainUiToIntro?.Invoke()); } ImGui.TreePop(); } } private void DrawCurrentTransfers() { if (ImGui.TreeNode( $"Current Transfers")) { bool showTransferWindow = _configuration.ShowTransferWindow; if (ImGui.Checkbox("Show separate Transfer window while transfers are active", ref showTransferWindow)) { _configuration.ShowTransferWindow = showTransferWindow; _configuration.Save(); } if (ImGui.BeginTable("TransfersTable", 2)) { ImGui.TableSetupColumn( $"Uploads ({UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentUploads.Sum(a => a.Total))})"); ImGui.TableSetupColumn($"Downloads ({UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Transferred))} / {UiShared.ByteToString(_apiController.CurrentDownloads.Sum(a => a.Total))})"); ImGui.TableHeadersRow(); ImGui.TableNextColumn(); if (ImGui.BeginTable("UploadsTable", 3)) { ImGui.TableSetupColumn("File"); ImGui.TableSetupColumn("Uploaded"); ImGui.TableSetupColumn("Size"); ImGui.TableHeadersRow(); foreach (var transfer in _apiController.CurrentUploads) { var color = UiShared.UploadColor((transfer.Transferred, transfer.Total)); ImGui.PushStyleColor(ImGuiCol.Text, color); ImGui.TableNextColumn(); ImGui.Text(transfer.Hash); ImGui.TableNextColumn(); ImGui.Text(UiShared.ByteToString(transfer.Transferred)); ImGui.TableNextColumn(); ImGui.Text(UiShared.ByteToString(transfer.Total)); ImGui.PopStyleColor(); ImGui.TableNextRow(); } ImGui.EndTable(); } ImGui.TableNextColumn(); if (ImGui.BeginTable("DownloadsTable", 3)) { ImGui.TableSetupColumn("File"); ImGui.TableSetupColumn("Downloaded"); ImGui.TableSetupColumn("Size"); ImGui.TableHeadersRow(); foreach (var transfer in _apiController.CurrentDownloads) { var color = UiShared.UploadColor((transfer.Transferred, transfer.Total)); ImGui.PushStyleColor(ImGuiCol.Text, color); ImGui.TableNextColumn(); ImGui.Text(transfer.Hash); ImGui.TableNextColumn(); ImGui.Text(UiShared.ByteToString(transfer.Transferred)); ImGui.TableNextColumn(); ImGui.Text(UiShared.ByteToString(transfer.Total)); ImGui.PopStyleColor(); ImGui.TableNextRow(); } ImGui.EndTable(); } ImGui.EndTable(); } ImGui.TreePop(); } } private void DrawFileCacheSettings() { if (ImGui.TreeNode("File Cache Settings")) { _uiShared.DrawFileScanState(); _uiShared.DrawCacheDirectorySetting(); ImGui.Text($"Local cache size: {UiShared.ByteToString(_uiShared.FileCacheSize)}"); ImGui.SameLine(); if (ImGui.Button("Clear local cache")) { Task.Run(() => { foreach (var file in Directory.GetFiles(_configuration.CacheFolder)) { File.Delete(file); } _uiShared.ForceRescan(); }); } ImGui.TreePop(); } } private void DrawPairedClientsContent() { if (!_apiController.ServerAlive) return; if (ImGui.TreeNode("Pairing Configuration")) { if (ImGui.BeginTable("PairedClientsTable", 5)) { ImGui.TableSetupColumn("Pause", ImGuiTableColumnFlags.WidthFixed, 50); ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.WidthFixed, 110); ImGui.TableSetupColumn("Status", ImGuiTableColumnFlags.WidthFixed, 140); ImGui.TableSetupColumn("Comment", ImGuiTableColumnFlags.WidthFixed, 400); ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 70); ImGui.TableHeadersRow(); foreach (var item in _apiController.PairedClients.ToList()) { ImGui.TableNextColumn(); ImGui.PushFont(UiBuilder.IconFont); string pauseIcon = item.IsPaused ? FontAwesomeIcon.Play.ToIconString() : FontAwesomeIcon.Pause.ToIconString(); if (ImGui.Button(pauseIcon + "##paused" + item.OtherUID)) { _ = _apiController.SendPairedClientPauseChange(item.OtherUID, !item.IsPaused); } ImGui.PopFont(); ImGui.TableNextColumn(); ImGui.TextColored( UiShared.GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused), item.OtherUID); ImGui.TableNextColumn(); string pairString = !item.IsSynced ? "Has not added you" : ((item.IsPaused || item.IsPausedFromOthers) ? "Unpaired" : "Paired"); ImGui.TextColored(UiShared.GetBoolColor(item.IsSynced && !item.IsPaused && !item.IsPausedFromOthers), pairString); ImGui.TableNextColumn(); string charComment = _configuration.GetCurrentServerUidComments().ContainsKey(item.OtherUID) ? _configuration.GetCurrentServerUidComments()[item.OtherUID] : string.Empty; ImGui.SetNextItemWidth(400); if (ImGui.InputTextWithHint("##comment" + item.OtherUID, "Add your comment here (comments will not be synced)", ref charComment, 255)) { if (_configuration.GetCurrentServerUidComments().Count == 0) { _configuration.UidServerComments[_configuration.ApiUri] = new Dictionary(); } _configuration.SetCurrentServerUidComment(item.OtherUID, charComment); _configuration.Save(); } ImGui.TableNextColumn(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Trash.ToIconString() + "##" + item.OtherUID)) { _ = _apiController.SendPairedClientRemoval(item.OtherUID); _apiController.PairedClients.Remove(item); } ImGui.PopFont(); ImGui.TableNextRow(); } ImGui.EndTable(); } var pairedClientEntry = _tempNameUID; ImGui.SetNextItemWidth(200); if (ImGui.InputText("UID", ref pairedClientEntry, 20)) { _tempNameUID = pairedClientEntry; } ImGui.SameLine(); ImGui.PushFont(UiBuilder.IconFont); if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString() + "##addToPairedClients")) { if (_apiController.PairedClients.All(w => w.OtherUID != _tempNameUID)) { var nameToSend = _tempNameUID; _tempNameUID = string.Empty; _ = _apiController.SendPairedClientAddition(nameToSend); } } ImGui.PopFont(); ImGui.TreePop(); } } private string _tempNameUID = string.Empty; } }