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

close fwdata file when leaving the editor #925

Merged
merged 6 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 0 additions & 2 deletions backend/FwDataMiniLcmBridge/FwDataBridgeKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ public static IServiceCollection AddFwDataBridge(this IServiceCollection service
services.AddLogging();
services.AddSingleton<FwDataFactory>();
services.AddSingleton<IProjectLoader, ProjectLoader>();
//todo since this is scoped it gets created on each request (or hub method call), which opens the project file on each request
//this is not ideal since opening the project file can be slow. It should be done once per hub connection.
services.AddKeyedScoped<ILexboxApi>(FwDataApiKey, (provider, o) => provider.GetRequiredService<FwDataFactory>().GetCurrentFwDataMiniLcmApi(true));
services.AddSingleton<FwDataProjectContext>();
return services;
Expand Down
22 changes: 20 additions & 2 deletions backend/FwDataMiniLcmBridge/FwDataFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ private static void OnLcmProjectCacheEviction(object key, object? value, Evictio
var (logger, projects) = ((ILogger<FwDataFactory>, HashSet<string>))state!;
var name = lcmCache.ProjectId.Name;
logger.LogInformation("Evicting project {ProjectFileName} from cache", name);
lcmCache.Dispose();
logger.LogInformation("FW Data Project {ProjectFileName} disposed", name);
projects.Remove((string)key);
if (!lcmCache.IsDisposed)
{
lcmCache.Dispose();
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
logger.LogInformation("FW Data Project {ProjectFileName} disposed", name);
}
}

public void Dispose()
Expand All @@ -90,4 +93,19 @@ public FwDataMiniLcmApi GetCurrentFwDataMiniLcmApi(bool saveOnDispose)
}
return GetFwDataMiniLcmApi(fwDataProject, true);
}

public void CloseCurrentProject()
{
var fwDataProject = context.Project;
if (fwDataProject is null) return;
CloseProject(fwDataProject);
}

private void CloseProject(FwDataProject project)
{
var cacheKey = CacheKey(project);
var lcmCache = cache.Get<LcmCache>(cacheKey);
if (lcmCache is null) return;
cache.Remove(cacheKey);
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
}
}
1 change: 1 addition & 0 deletions backend/LocalWebApp/Hubs/CrdtMiniLcmApiHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@
public interface ILexboxClient
{
Task OnEntryUpdated(Entry entry);
Task OnProjectClosed();
}

public class CrdtMiniLcmApiHub(
ILexboxApi lexboxApi,
IOptions<JsonOptions> jsonOptions,

Check warning on line 17 in backend/LocalWebApp/Hubs/CrdtMiniLcmApiHub.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite

Parameter 'jsonOptions' is unread.

Check warning on line 17 in backend/LocalWebApp/Hubs/CrdtMiniLcmApiHub.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite

Parameter 'jsonOptions' is unread.

Check warning on line 17 in backend/LocalWebApp/Hubs/CrdtMiniLcmApiHub.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite

Parameter 'jsonOptions' is unread.
BackgroundSyncService backgroundSyncService,
SyncService syncService) : Hub<ILexboxClient>
{
Expand Down
22 changes: 21 additions & 1 deletion backend/LocalWebApp/Hubs/FwDataMiniLcmHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,31 @@

namespace LocalWebApp.Hubs;

public class FwDataMiniLcmHub([FromKeyedServices(FwDataBridgeKernel.FwDataApiKey)] ILexboxApi lexboxApi) : Hub<ILexboxClient>
public class FwDataMiniLcmHub([FromKeyedServices(FwDataBridgeKernel.FwDataApiKey)] ILexboxApi lexboxApi, FwDataFactory fwDataFactory,
FwDataProjectContext context) : Hub<ILexboxClient>
{
public const string ProjectRouteKey = "fwdata";
public override async Task OnConnectedAsync()
{
var project = context.Project;
if (project is null)
{
throw new InvalidOperationException("No project is set in the context.");
}
await Groups.AddToGroupAsync(Context.ConnectionId, project.Name);
}

public override async Task OnDisconnectedAsync(Exception? exception)
{
//todo if multiple clients are connected, this will close the project for all of them.
fwDataFactory.CloseCurrentProject();
var project = context.Project;
if (project is null)
{
throw new InvalidOperationException("No project is set in the context.");
}
await Clients.OthersInGroup(project.Name).OnProjectClosed();
await Groups.RemoveFromGroupAsync(Context.ConnectionId, project.Name);
}

public async Task<WritingSystems> GetWritingSystems()
Expand Down
3 changes: 2 additions & 1 deletion backend/LocalWebApp/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information"
"Default": "Information",
"LocalWebApp.Auth.LoggerAdapter": "Warning",
}
}
}
2 changes: 2 additions & 0 deletions frontend/viewer/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import TestProjectView from './TestProjectView.svelte';
import FwDataProjectView from './FwDataProjectView.svelte';
import HomeView from './HomeView.svelte';
import NotificationOutlet from './lib/notifications/NotificationOutlet.svelte';
import Sandbox from './lib/sandbox/Sandbox.svelte';

export let url = '';
Expand Down Expand Up @@ -38,3 +39,4 @@
</Route>
</div>
</Router>
<NotificationOutlet/>
6 changes: 6 additions & 0 deletions frontend/viewer/src/FwDataProjectView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import {onDestroy, setContext} from 'svelte';
import {SetupSignalR} from './lib/services/service-provider-signalr';
import ProjectView from './ProjectView.svelte';
import {navigate} from 'svelte-routing';
import {AppNotification} from './lib/notifications/notifications';

export let projectName: string;
const connection = new HubConnectionBuilder()
Expand All @@ -18,6 +20,10 @@
SetupSignalR(connection, {
history: false,
write: true,
},
async () => {
navigate('/');
AppNotification.display('Project closed on another tab', 'warning', 'long');
});
let connected = false;
</script>
Expand Down
12 changes: 10 additions & 2 deletions frontend/viewer/src/ProjectView.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<script lang="ts">
import {AppBar, Button, ProgressCircle} from 'svelte-ux';
import {mdiArrowCollapseLeft, mdiArrowCollapseRight, mdiArrowLeft, mdiEyeSettingsOutline} from '@mdi/js';
import {mdiArrowCollapseLeft, mdiArrowCollapseRight, mdiArrowLeft, mdiEyeSettingsOutline, mdiHome} from '@mdi/js';
import Editor from './lib/Editor.svelte';
import {navigate} from 'svelte-routing';
import {headword, pickBestAlternative} from './lib/utils';
import {views} from './lib/config-data';
import {useLexboxApi} from './lib/services/service-provider';
Expand Down Expand Up @@ -57,6 +58,7 @@

export let projectName: string;
export let isConnected: boolean;
export let showHomeButton = true;
$: connected.set(isConnected);

const connected = writable(false);
Expand Down Expand Up @@ -228,7 +230,13 @@
</div>
{:else}
<div class="project-view !flex flex-col PortalTarget" style="{spaceForEditorStyle}">
<AppBar title={projectName} class="bg-secondary min-h-12 shadow-md" menuIcon=''>
<AppBar title={projectName} class="bg-secondary min-h-12 shadow-md">
<Button
classes={{root: showHomeButton ? '' : 'hidden'}}
slot="menuIcon"
icon={mdiHome}
on:click={() => navigate('/')}
/>
<div class="flex-grow-0 flex-shrink-0 lg:hidden mx-2 sm:mr-0" class:invisible={!pickedEntry}>
<Button icon={mdiArrowLeft} size="sm" iconOnly rounded variant="outline" on:click={() => pickedEntry = false} />
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ export type ILexboxClient = {
* @returns Transpiled from System.Threading.Tasks.Task
*/
OnEntryUpdated(entry: Entry): Promise<void>;
OnProjectClosed(): Promise<void>;
}

Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,14 @@ class ILexboxClient_Binder implements ReceiverRegister<ILexboxClient> {
public readonly register = (connection: HubConnection, receiver: ILexboxClient): Disposable => {

const __onEntryUpdated = (...args: [Entry]) => receiver.OnEntryUpdated(...args);
const __onProjectClosed = () => receiver.OnProjectClosed();

connection.on("OnEntryUpdated", __onEntryUpdated);
connection.on("OnProjectClosed", __onProjectClosed);

const methodList: ReceiverMethod[] = [
{ methodName: "OnEntryUpdated", method: __onEntryUpdated }
{ methodName: "OnEntryUpdated", method: __onEntryUpdated },
{ methodName: "OnProjectClosed", method: __onProjectClosed },
]

return new ReceiverMethodSubscription(connection, methodList);
Expand Down
32 changes: 32 additions & 0 deletions frontend/viewer/src/lib/notifications/NotificationOutlet.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts">
import {AppNotification} from './notifications';
import {Notification, Icon} from 'svelte-ux';
import {
mdiAlert,
mdiAlertCircleOutline,
mdiCheckCircleOutline,
mdiInformationOutline
} from '@mdi/js';

const notifications = AppNotification.notifications;
</script>
<div class="fixed bottom-0 z-50 flex flex-col gap-2 p-4 w-full overflow-y-auto">
{#each $notifications as notification}
<div class="w-[400px] mx-auto">
<Notification open closeIcon>
<div slot="icon">
{#if notification.type === 'success'}
<Icon path={mdiCheckCircleOutline} size="1.5rem" class="text-success"/>
{:else if notification.type === 'error'}
<Icon path={mdiAlert} size="1.5rem" class="text-danger"/>
{:else if notification.type === 'info'}
<Icon path={mdiInformationOutline} size="1.5rem" class="text-info"/>
{:else if notification.type === 'warning'}
<Icon path={mdiAlertCircleOutline} size="1.5rem" class="text-warning"/>
{/if}
</div>
<div slot="title">{notification.message}</div>
</Notification>
</div>
{/each}
</div>
21 changes: 21 additions & 0 deletions frontend/viewer/src/lib/notifications/notifications.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {writable, type Writable, type Readable, readonly} from 'svelte/store';

export class AppNotification {
private static _notifications: Writable<AppNotification[]> = writable([]);
public static get notifications(): Writable<AppNotification[]> {
return this._notifications;
}
public static display(message: string, type: 'success' | 'error' | 'info' | 'warning', timeout: 'short' | 'long' | number = 'short') {
const notification = new AppNotification(message, type);
this._notifications.update(notifications => [...notifications, notification]);
if (timeout === -1) return;
if (typeof timeout === 'string') {
timeout = timeout === 'short' ? 5000 : 30000;
}
setTimeout(() => {
this._notifications.update(notifications => notifications.filter(n => n !== notification));
}, timeout);
}

private constructor(public message: string, public type: 'success' | 'error' | 'info' | 'warning') {}
}
6 changes: 4 additions & 2 deletions frontend/viewer/src/lib/services/service-provider-signalr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type { HubConnection } from '@microsoft/signalr';
import type { LexboxApiFeatures, LexboxApiMetadata } from './lexbox-api';
import {LexboxService} from './service-provider';

export function SetupSignalR(connection: HubConnection, features: LexboxApiFeatures) {
const noop = () => Promise.resolve();
export function SetupSignalR(connection: HubConnection, features: LexboxApiFeatures, onProjectClosed: () => Promise<void> = noop) {
const hubFactory = getHubProxyFactory('ILexboxApiHub');
const hubProxy = hubFactory.createHubProxy(connection);

Expand All @@ -17,7 +18,8 @@ export function SetupSignalR(connection: HubConnection, features: LexboxApiFeatu
getReceiverRegister('ILexboxClient').register(connection, {
OnEntryUpdated: async (entry: Entry) => {
console.log('OnEntryUpdated', entry);
}
},
OnProjectClosed: onProjectClosed
});
window.lexbox.ServiceProvider.setService(LexboxService.LexboxApi, lexboxApiHubProxy);
}
Loading