Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create troubleshooting dialog #1422

Merged
merged 4 commits into from
Jan 29, 2025
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/FwLite/FwLiteMaui/FwLiteMaui.csproj
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@

<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks>
<LangVersion>preview</LangVersion>

<!-- Note for MacCatalyst:
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
26 changes: 26 additions & 0 deletions backend/FwLite/FwLiteMaui/FwLiteMauiConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Diagnostics;
using System.Reflection;

namespace FwLiteMaui;

public class FwLiteMauiConfig
{
public FwLiteMauiConfig()
{
var defaultDataPath = FwLiteMauiKernel.IsPortableApp ? Directory.GetCurrentDirectory() : FileSystem.AppDataDirectory;
//when launching from a notification, the current directory may be C:\Windows\System32, so we'll use the path of the executable instead
if (defaultDataPath.StartsWith("C:\\Windows\\System32", StringComparison.OrdinalIgnoreCase))
defaultDataPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName ??
Assembly.GetExecutingAssembly().Location) ?? ".";
BaseDataDir = defaultDataPath;
}

public string BaseDataDir
{
get;
set => field = Path.GetFullPath(value);
}

public string AppLogFilePath => Path.Combine(BaseDataDir, "app.log");
public string AuthCacheFilePath => Path.Combine(BaseDataDir, "msal.cache");
}
17 changes: 9 additions & 8 deletions backend/FwLite/FwLiteMaui/FwLiteMauiKernel.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Diagnostics;
using System.Reflection;
using FwLiteMaui.Services;
using FwLiteShared;
using FwLiteShared.Auth;
using FwLiteShared.Services;
using LcmCrdt;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
@@ -88,12 +90,10 @@ public static void AddFwLiteMauiServices(this IServiceCollection services,
}
});

var defaultDataPath = IsPortableApp ? Directory.GetCurrentDirectory() : FileSystem.AppDataDirectory;
//when launching from a notification, the current directory may be C:\Windows\System32, so we'll use the path of the executable instead
if (defaultDataPath.StartsWith("C:\\Windows\\System32", StringComparison.OrdinalIgnoreCase))
defaultDataPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName ?? Assembly.GetExecutingAssembly().Location) ?? ".";
var baseDataPath = Path.GetFullPath(configuration.GetSection("FwLiteMaui").GetValue<string>("BaseDataDir") ??
defaultDataPath);

services.AddOptions<FwLiteMauiConfig>().BindConfiguration("FwLiteMaui");
var fwLiteMauiConfig = configuration.GetSection("FwLiteMaui").Get<FwLiteMauiConfig>() ?? new();
var baseDataPath = fwLiteMauiConfig.BaseDataDir;
logging.AddFilter("FwLiteShared.Auth.LoggerAdapter", LogLevel.Warning);
logging.AddFilter("Microsoft.EntityFrameworkCore.Database", LogLevel.Warning);
Directory.CreateDirectory(baseDataPath);
@@ -103,14 +103,15 @@ public static void AddFwLiteMauiServices(this IServiceCollection services,
});
services.Configure<AuthConfig>(config =>
{
config.CacheFileName = Path.Combine(baseDataPath, "msal.cache");
config.CacheFileName = fwLiteMauiConfig.AuthCacheFilePath;
config.SystemWebViewLogin = true;
});

logging.AddFile(Path.Combine(baseDataPath, "app.log"));
logging.AddFile(fwLiteMauiConfig.AppLogFilePath);
services.AddSingleton<IPreferences>(Preferences.Default);
services.AddSingleton<IVersionTracking>(VersionTracking.Default);
services.AddSingleton<IConnectivity>(Connectivity.Current);
services.AddSingleton<ITroubleshootingService, MauiTroubleshootingService>();
logging.AddConsole();
#if DEBUG
logging.AddDebug();
41 changes: 41 additions & 0 deletions backend/FwLite/FwLiteMaui/Services/MauiTroubleshootingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using FwLiteShared.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.JSInterop;

namespace FwLiteMaui.Services;

public class MauiTroubleshootingService(IOptions<FwLiteMauiConfig> config, ILogger<MauiTroubleshootingService> logger) : ITroubleshootingService
{
private readonly ILauncher _launcher = Launcher.Default;
private readonly IBrowser _browser = Browser.Default;
private FwLiteMauiConfig Config => config.Value;

[JSInvokable]
public async Task<bool> TryOpenDataDirectory()
{
try
{
//obviously intended to open a url in the browser, but on windows it just opens explorer
await _browser.OpenAsync("file://" + Config.BaseDataDir);
return true;
}
catch (Exception e)
{
logger.LogError(e, "Failed to open data directory");
return false;
}
}

[JSInvokable]
public Task<string> GetDataDirectory()
{
return Task.FromResult(Config.BaseDataDir);
}

[JSInvokable]
public async Task OpenLogFile()
{
await _launcher.OpenAsync(new OpenFileRequest("Log File", new FileResult(Config.AppLogFilePath)));
}
}
8 changes: 6 additions & 2 deletions backend/FwLite/FwLiteShared/Services/FwLiteProvider.cs
Original file line number Diff line number Diff line change
@@ -20,7 +20,8 @@ public class FwLiteProvider(
ImportFwdataService importFwdataService,
ILogger<FwLiteProvider> logger,
IOptions<FwLiteConfig> config,
IAppLauncher? appLauncher = null
IAppLauncher? appLauncher = null,
ITroubleshootingService? troubleshootingService = null
)
{
public const string OverrideServiceFunctionName = "setOverrideService";
@@ -36,6 +37,8 @@ public Dictionary<DotnetService, object> GetServices()
};
if (appLauncher is not null)
services[DotnetService.AppLauncher] = appLauncher;
if (troubleshootingService is not null)
services[DotnetService.TroubleshootingService] = troubleshootingService;
return services;
}

@@ -78,5 +81,6 @@ public enum DotnetService
FwLiteConfig,
ProjectServicesProvider,
HistoryService,
AppLauncher
AppLauncher,
TroubleshootingService
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace FwLiteShared.Services;

public interface ITroubleshootingService
{
Task<bool> TryOpenDataDirectory();
Task<string> GetDataDirectory();
Task OpenLogFile();
}
Original file line number Diff line number Diff line change
@@ -98,6 +98,7 @@ private static void ConfigureFwLiteSharedTypes(ConfigurationBuilder builder)
typeof(ProjectServicesProvider),
typeof(IAppLauncher)
], exportBuilder => exportBuilder.WithPublicMethods(b => b.AlwaysReturnPromise().OnlyJsInvokable()));
builder.ExportAsInterfaces([typeof(ITroubleshootingService)], exportBuilder => exportBuilder.WithPublicMethods(b => b.AlwaysReturnPromise()));

builder.ExportAsInterfaces([
typeof(ServerStatus),
1 change: 0 additions & 1 deletion frontend/viewer/src/App.svelte
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@
}
}
window.svelteNavigate = (to: string) => {
console.log('svelteNavigate', to);
navigate(to, {replace: true});
};
</script>
14 changes: 12 additions & 2 deletions frontend/viewer/src/HomeView.svelte
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
mdiChatQuestion,
mdiChevronRight,
mdiCloud,
mdiFaceAgent,
mdiRefresh,
mdiTestTube,
} from '@mdi/js';
@@ -22,11 +23,12 @@
useAuthService,
useFwLiteConfig,
useImportFwdataService,
useProjectsService
useProjectsService, useTroubleshootingService
} from './lib/services/service-provider';
import type {ILexboxServer, IServerStatus} from '$lib/dotnet-types';
import LoginButton from '$lib/auth/LoginButton.svelte';
import AnchorListItem from '$lib/utils/AnchorListItem.svelte';
import TroubleshootDialog from '$lib/troubleshoot/TroubleshootDialog.svelte';

const projectsService = useProjectsService();
const authService = useAuthService();
@@ -141,6 +143,8 @@
.find(([_server, projects]) => matchesProject(projects, project))?.[0];
return authority ? serversStatus.find(s => s.server.authority == authority)?.server : undefined;
}
const supportsTroubleshooting = useTroubleshootingService();
let showTroubleshooting = false;
</script>
<AppBar title="Projects" class="bg-secondary min-h-12 shadow-md justify-between" menuIcon={null}>
<div slot="title" class="text-lg flex gap-2 items-center">
@@ -151,7 +155,7 @@
</picture>
<h3>Projects</h3>
</div>
<div slot="actions">
<div slot="actions" class="flex gap-2">
<Button
href={fwLiteConfig.feedbackUrl}
target="_blank"
@@ -160,6 +164,12 @@
icon={mdiChatQuestion}>
Feedback
</Button>
{#if supportsTroubleshooting}
<Button size="sm" variant="outline" icon={mdiFaceAgent} title="Troubleshoot" iconOnly={false}
on:click={() => showTroubleshooting = !showTroubleshooting}>
</Button>
<TroubleshootDialog bind:open={showTroubleshooting}/>
{/if}
</div>
</AppBar>
<div class="mx-auto md:w-full md:py-4 max-w-2xl">
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ export enum DotnetService {
FwLiteConfig = "FwLiteConfig",
ProjectServicesProvider = "ProjectServicesProvider",
HistoryService = "HistoryService",
AppLauncher = "AppLauncher"
AppLauncher = "AppLauncher",
TroubleshootingService = "TroubleshootingService"
}
/* eslint-enable */
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint-disable */
// This code was generated by a Reinforced.Typings tool.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.

export interface ITroubleshootingService
{
tryOpenDataDirectory() : Promise<boolean>;
getDataDirectory() : Promise<string>;
openLogFile() : Promise<void>;
}
/* eslint-enable */
20 changes: 18 additions & 2 deletions frontend/viewer/src/lib/layout/AppBarMenu.svelte
Original file line number Diff line number Diff line change
@@ -2,13 +2,22 @@
import AboutDialog from '$lib/about/AboutDialog.svelte';
import ActivityView from '$lib/activity/ActivityView.svelte';
import {useFeatures} from '$lib/services/feature-service';
import {mdiDotsVertical, mdiEyeSettingsOutline, mdiHistory, mdiInformationVariantCircle, mdiNoteEdit} from '@mdi/js';
import {
mdiDotsVertical,
mdiEyeSettingsOutline,
mdiFaceAgent,
mdiHistory,
mdiInformationVariantCircle,
mdiNoteEdit
} from '@mdi/js';
import {createEventDispatcher} from 'svelte';
import {Button, MenuItem, ResponsiveMenu, Toggle} from 'svelte-ux';
import {asScottyPortal} from './Scotty.svelte';
import {useProjectViewState} from '$lib/services/project-view-state-service';
import WritingSystemDialog from '$lib/writing-system/WritingSystemDialog.svelte';
import DevContent from '$lib/layout/DevContent.svelte';
import TroubleshootDialog from '$lib/troubleshoot/TroubleshootDialog.svelte';
import {useTroubleshootingService} from '$lib/services/service-provider';

const dispatch = createEventDispatcher<{
showOptionsDialog: void;
@@ -19,10 +28,12 @@

const features = useFeatures();
const projectViewState = useProjectViewState();
const supportsTroubleshooting = useTroubleshootingService();

let activityViewOpen = false;
let aboutDialogOpen = false;
let wsEditDialogOpen = false;
let troubleshootDialogOpen = false;
</script>

<!-- #key prevents rendering ugly delayed state updates -->
@@ -49,6 +60,9 @@
Edit WS
</MenuItem>
</DevContent>
{#if supportsTroubleshooting}
<MenuItem icon={mdiFaceAgent} on:click={() => troubleshootDialogOpen = true}>Troubleshoot</MenuItem>
{/if}
</button>
</ResponsiveMenu>
</Button>
@@ -61,7 +75,9 @@
{#if about}
<AboutDialog bind:open={aboutDialogOpen} text={about} />
{/if}

{#if supportsTroubleshooting}
<TroubleshootDialog bind:open={troubleshootDialogOpen}/>
{/if}
<WritingSystemDialog bind:open={wsEditDialogOpen}/>

<style lang="postcss" global>
10 changes: 9 additions & 1 deletion frontend/viewer/src/lib/services/service-provider.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,9 @@ import type {
IHistoryServiceJsInvokable
} from '$lib/dotnet-types/generated-types/FwLiteShared/Services/IHistoryServiceJsInvokable';
import type {IAppLauncher} from '$lib/dotnet-types/generated-types/FwLiteShared/Services/IAppLauncher';
import type {
ITroubleshootingService
} from '$lib/dotnet-types/generated-types/FwLiteShared/Services/ITroubleshootingService';

export type ServiceKey = keyof LexboxServiceRegistry;
export type LexboxServiceRegistry = {
@@ -22,7 +25,8 @@ export type LexboxServiceRegistry = {
[DotnetService.FwLiteConfig]: IFwLiteConfig,
[DotnetService.ProjectServicesProvider]: IProjectServicesProvider,
[DotnetService.HistoryService]: IHistoryServiceJsInvokable,
[DotnetService.AppLauncher]: IAppLauncher
[DotnetService.AppLauncher]: IAppLauncher,
[DotnetService.TroubleshootingService]: ITroubleshootingService
};

export const SERVICE_KEYS = Object.values(DotnetService);
@@ -97,3 +101,7 @@ export function useProjectServicesProvider(): IProjectServicesProvider {
export function useAppLauncher(): IAppLauncher | undefined {
return window.lexbox.ServiceProvider.tryGetService(DotnetService.AppLauncher);
}

export function useTroubleshootingService(): ITroubleshootingService | undefined {
return window.lexbox.ServiceProvider.tryGetService(DotnetService.TroubleshootingService);
}
31 changes: 31 additions & 0 deletions frontend/viewer/src/lib/troubleshoot/TroubleshootDialog.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import {Button, Dialog, Field} from 'svelte-ux';
import {useTroubleshootingService} from '$lib/services/service-provider';
import {AppNotification} from '$lib/notifications/notifications';
import {mdiFileEye, mdiFolderSearch} from '@mdi/js';

const service = useTroubleshootingService();
export let open = false;

async function tryOpenDataDirectory() {
if (!await service?.tryOpenDataDirectory()) {
AppNotification.display('Failed to open data directory, use the path in the text field instead', 'error');
}
}
</script>
<Dialog bind:open={open} style="height: auto">
<div slot="title">Troubleshoot</div>
<div class="flex flex-col gap-4 items-start p-4">
{#await service?.getDataDirectory() then value}
<Field label="Data Directory" {value} class="self-stretch">
<span slot="append">
<Button icon={mdiFolderSearch} title="Open Data Directory" class="text-surface-content/50 p-2" on:click={() => tryOpenDataDirectory()}/>
</span>
</Field>
{/await}
<Button variant="fill-light" icon={mdiFileEye} on:click={() => service?.openLogFile()}>Open Log file</Button>
</div>
<div slot="actions">
<Button on:click={() => open = false}>Close</Button>
</div>
</Dialog>
Loading