Skip to content

Commit

Permalink
[MS] Janky implementation of multi-org on Electron only
Browse files Browse the repository at this point in the history
  • Loading branch information
Max-7 committed Jan 22, 2024
1 parent f675d90 commit f76e998
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 33 deletions.
3 changes: 2 additions & 1 deletion client/src/components/organizations/OrganizationCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<ion-card-title class="card-title">
<span class="subtitles-normal">{{ device.organizationId }}</span>
<span class="subtitles-sm">{{ device.humanHandle.label }}</span>
<span v-show="isDeviceLoggedIn(device)">Connected!</span>
</ion-card-title>
</div>
</div>
Expand All @@ -19,7 +20,7 @@
</template>

<script setup lang="ts">
import { AvailableDevice } from '@/parsec';
import { AvailableDevice, isDeviceLoggedIn } from '@/parsec';
import { IonAvatar, IonCard, IonCardHeader, IonCardTitle } from '@ionic/vue';

defineProps<{
Expand Down
10 changes: 5 additions & 5 deletions client/src/parsec/invitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ export async function listUserInvitations(): Promise<Result<Array<UserInvitation
});
return result as any;
} else {
return new Promise<Result<Array<UserInvitation>, ListInvitationsError>>((resolve, _reject) => {
const ret: Array<UserInvitation> = [
return {
ok: true,
value: [
{
tag: InviteListItemTag.User,
addr: 'parsec://parsec.example.com/MyOrg?action=claim_device&token=12346565645645654645645645645645',
Expand All @@ -86,9 +87,8 @@ export async function listUserInvitations(): Promise<Result<Array<UserInvitation
claimerEmail: '[email protected]',
status: InvitationStatus.Ready,
},
];
resolve({ ok: true, value: ret });
});
],
};
}
}

Expand Down
48 changes: 46 additions & 2 deletions client/src/parsec/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ import {
UserProfile,
} from '@/parsec/types';

export interface LoggedInDeviceInfo {
handle: ConnectionHandle;
device: AvailableDevice;
}

const loggedInDevices: Array<LoggedInDeviceInfo> = [];

export async function getLoggedInDevices(): Promise<Array<LoggedInDeviceInfo>> {
return loggedInDevices;
}

export function isDeviceLoggedIn(device: AvailableDevice): boolean {
return loggedInDevices.find((info) => info.device.slug === device.slug) !== undefined;
}

export function getDeviceHandle(device: AvailableDevice): ConnectionHandle | null {
const info = loggedInDevices.find((info) => info.device.slug === device.slug);
if (info) {
return info.handle;
}
return null;
}

export async function listAvailableDevices(): Promise<Array<AvailableDevice>> {
return await libparsec.listAvailableDevices(window.getConfigDir());
}
Expand All @@ -34,16 +57,26 @@ export async function login(device: AvailableDevice, password: string): Promise<
console.log('Event received', event);
}

const info = loggedInDevices.find((info) => info.device.slug === device.slug);
if (info !== undefined) {
return { ok: true, value: info.handle };
}

if (!needsMocks()) {
const clientConfig = getClientConfig();
const strategy: DeviceAccessStrategyPassword = {
tag: DeviceAccessStrategyTag.Password,
password: password,
keyFile: device.keyFilePath,
};
return await libparsec.clientStart(clientConfig, parsecEventCallback, strategy);
const result = await libparsec.clientStart(clientConfig, parsecEventCallback, strategy);
if (result.ok) {
loggedInDevices.push({ handle: result.value, device: device });
}
return result;
} else {
if (password === 'P@ssw0rd.' || password === 'AVeryL0ngP@ssw0rd') {
loggedInDevices.push({ handle: DEFAULT_HANDLE, device: device });
return { ok: true, value: DEFAULT_HANDLE };
}
return {
Expand All @@ -60,8 +93,19 @@ export async function logout(): Promise<Result<null, ClientStopError>> {
const handle = getParsecHandle();

if (handle !== null && !needsMocks()) {
return await libparsec.clientStop(handle);
const result = await libparsec.clientStop(handle);
if (result.ok) {
const index = loggedInDevices.findIndex((info) => info.handle === handle);
if (index !== -1) {
loggedInDevices.splice(index, 1);
}
}
return result;
} else {
const index = loggedInDevices.findIndex((info) => info.handle === handle);
if (index !== -1) {
loggedInDevices.splice(index, 1);
}
return { ok: true, value: null };
}
}
Expand Down
1 change: 1 addition & 0 deletions client/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export {
getWorkspaceId,
} from '@/router/params';
export * from '@/router/types';
export { watchOrganizationSwitch, watchRoute } from '@/router/watchers';
61 changes: 59 additions & 2 deletions client/src/router/navigation.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

import { startWorkspace, WorkspaceID } from '@/parsec';
import { ConnectionHandle, startWorkspace, WorkspaceID } from '@/parsec';
import { getConnectionHandle } from '@/router/params';
import { getRouter, Routes } from '@/router/types';
import { getCurrentRoute, getRouter, Routes } from '@/router/types';
import { organizationKey } from '@/router/watchers';
import { LocationQueryRaw, RouteParamsRaw } from 'vue-router';

export interface NavigationOptions {
Expand Down Expand Up @@ -51,3 +52,59 @@ export async function routerGoBack(): Promise<void> {
const router = getRouter();
router.go(-1);
}

interface RouteBackup {
handle: ConnectionHandle;
data: {
route: Routes;
params: object;
query: object;
};
}

const routesBackup: Array<RouteBackup> = [];

export async function switchOrganization(handle: ConnectionHandle | null, backup = true): Promise<void> {
if (backup) {
const currentHandle = getConnectionHandle();

if (currentHandle === null) {
console.error('No current handle');
return;
}
if (handle === currentHandle) {
console.error('Cannot switch to same organization');
return;
}
// Backup the current route
const currentRoute = getCurrentRoute();
const index = routesBackup.findIndex((bk) => bk.handle === currentHandle);
if (index !== -1) {
routesBackup.splice(index, 1);
}
console.log('Saving', currentRoute.value.name, currentRoute.value.params, currentRoute.value.query);
routesBackup.push({
handle: currentHandle,
data: {
route: currentRoute.value.name as Routes,
params: currentRoute.value.params,
query: currentRoute.value.query,
},
});
}

// No handle, navigate to organization list
if (!handle) {
await navigateTo(Routes.Home, { skipHandle: true, replace: true });
organizationKey.value += 1;
} else {
const backup = routesBackup.find((bk) => bk.handle === handle);
if (!backup) {
console.error('No backup, organization was not connected');
return;
}
console.log('Restoring', backup.data.route, backup.data.params, backup.data.query);
await navigateTo(backup.data.route, { params: backup.data.params, query: backup.data.query, skipHandle: true, replace: true });
organizationKey.value += 1;
}
}
7 changes: 1 addition & 6 deletions client/src/router/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

import { createRouter, createWebHistory } from '@ionic/vue-router';
import { Ref, WatchStopHandle, watch } from 'vue';
import { Ref } from 'vue';
import { RouteLocationNormalizedLoaded, RouteRecordRaw, Router } from 'vue-router';

export enum Routes {
Expand Down Expand Up @@ -128,8 +128,3 @@ export function getRouter(): Router {
export function getCurrentRoute(): Ref<RouteLocationNormalizedLoaded> {
return (router as Router).currentRoute;
}

export function watchRoute(callback: () => Promise<void>): WatchStopHandle {
const currentRoute = getCurrentRoute();
return watch(currentRoute, callback);
}
15 changes: 15 additions & 0 deletions client/src/router/watchers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

import { getCurrentRoute } from '@/router/types';
import { Ref, WatchStopHandle, ref, watch } from 'vue';

export const organizationKey: Ref<number> = ref(0);

export function watchRoute(callback: () => Promise<void>): WatchStopHandle {
const currentRoute = getCurrentRoute();
return watch(currentRoute, callback);
}

export function watchOrganizationSwitch(callback: () => Promise<void>): WatchStopHandle {
return watch(organizationKey, callback);
}
7 changes: 7 additions & 0 deletions client/src/views/files/FoldersPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ const allFilesSelected = ref(false);

const routeWatchCancel = watchRoute(async () => {
const newPath = getDocumentPath();
if (newPath === '') {
return;
}
if (newPath !== currentPath.value) {
currentPath.value = newPath;
}
Expand All @@ -226,6 +229,10 @@ let callbackId: string | null = null;

onMounted(async () => {
callbackId = await importManager.registerCallback(onFileImportState);
const path = getDocumentPath();
if (path !== '') {
currentPath.value = path;
}
await listFolder();
});

Expand Down
6 changes: 6 additions & 0 deletions client/src/views/header/HeaderPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ const userInfo: Ref<ClientInfo | null> = ref(null);
const fullPath: Ref<RouterPathNode[]> = ref([]);

const routeWatchCancel = watchRoute(async () => {
const result = await getClientInfo();
if (result.ok) {
userInfo.value = result.value;
} else {
console.log('Could not get user info', result.error);
}
await updateRoute();
});

Expand Down
13 changes: 9 additions & 4 deletions client/src/views/home/HomePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
<script setup lang="ts">
import { Validity, claimDeviceLinkValidator, claimLinkValidator, claimUserLinkValidator } from '@/common/validators';
import { MsModalResult, getTextInputFromUser } from '@/components/core';
import { AvailableDevice, login as parsecLogin } from '@/parsec';
import { NavigationOptions, Routes, getCurrentRouteQuery, navigateTo, watchRoute } from '@/router';
import { AvailableDevice, getDeviceHandle, isDeviceLoggedIn, login as parsecLogin } from '@/parsec';
import { NavigationOptions, Routes, getCurrentRouteQuery, navigateTo, switchOrganization, watchRoute } from '@/router';
import { Notification, NotificationKey, NotificationLevel, NotificationManager } from '@/services/notificationManager';
import { StorageManager, StorageManagerKey, StoredDeviceData } from '@/services/storageManager';
import { translate } from '@/services/translation';
Expand Down Expand Up @@ -155,8 +155,13 @@ async function backToOrganizations(): Promise<void> {
}

function onOrganizationSelected(device: AvailableDevice): void {
selectedDevice.value = device;
state.value = HomePageState.Login;
if (isDeviceLoggedIn(device)) {
const handle = getDeviceHandle(device);
switchOrganization(handle, false);
} else {
selectedDevice.value = device;
state.value = HomePageState.Login;
}
}

async function login(device: AvailableDevice, password: string): Promise<void> {
Expand Down
Loading

0 comments on commit f76e998

Please sign in to comment.