Private servers with password and invite links (Experimental)
This commit is contained in:
102
src/app/app.ts
102
src/app/app.ts
@@ -2,6 +2,7 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
inject,
|
||||
HostListener
|
||||
} from '@angular/core';
|
||||
@@ -35,6 +36,15 @@ import {
|
||||
STORAGE_KEY_LAST_VISITED_ROUTE
|
||||
} from './core/constants';
|
||||
|
||||
interface DeepLinkElectronApi {
|
||||
consumePendingDeepLink?: () => Promise<string | null>;
|
||||
onDeepLinkReceived?: (listener: (url: string) => void) => () => void;
|
||||
}
|
||||
|
||||
type DeepLinkWindow = Window & {
|
||||
electronAPI?: DeepLinkElectronApi;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
imports: [
|
||||
@@ -50,7 +60,7 @@ import {
|
||||
templateUrl: './app.html',
|
||||
styleUrl: './app.scss'
|
||||
})
|
||||
export class App implements OnInit {
|
||||
export class App implements OnInit, OnDestroy {
|
||||
store = inject(Store);
|
||||
currentRoom = this.store.selectSignal(selectCurrentRoom);
|
||||
desktopUpdates = inject(DesktopAppUpdateService);
|
||||
@@ -63,6 +73,7 @@ export class App implements OnInit {
|
||||
private timeSync = inject(TimeSyncService);
|
||||
private voiceSession = inject(VoiceSessionService);
|
||||
private externalLinks = inject(ExternalLinkService);
|
||||
private deepLinkCleanup: (() => void) | null = null;
|
||||
|
||||
@HostListener('document:click', ['$event'])
|
||||
onGlobalLinkClick(evt: MouseEvent): void {
|
||||
@@ -80,6 +91,8 @@ export class App implements OnInit {
|
||||
await this.timeSync.syncWithEndpoint(apiBase);
|
||||
} catch {}
|
||||
|
||||
await this.setupDesktopDeepLinks();
|
||||
|
||||
this.store.dispatch(UsersActions.loadCurrentUser());
|
||||
|
||||
this.store.dispatch(RoomsActions.loadRooms());
|
||||
@@ -87,8 +100,12 @@ export class App implements OnInit {
|
||||
const currentUserId = localStorage.getItem(STORAGE_KEY_CURRENT_USER_ID);
|
||||
|
||||
if (!currentUserId) {
|
||||
if (this.router.url !== '/login' && this.router.url !== '/register') {
|
||||
this.router.navigate(['/login']).catch(() => {});
|
||||
if (!this.isPublicRoute(this.router.url)) {
|
||||
this.router.navigate(['/login'], {
|
||||
queryParams: {
|
||||
returnUrl: this.router.url
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
} else {
|
||||
const last = localStorage.getItem(STORAGE_KEY_LAST_VISITED_ROUTE);
|
||||
@@ -116,6 +133,11 @@ export class App implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.deepLinkCleanup?.();
|
||||
this.deepLinkCleanup = null;
|
||||
}
|
||||
|
||||
openNetworkSettings(): void {
|
||||
this.settingsModal.open('network');
|
||||
}
|
||||
@@ -131,4 +153,78 @@ export class App implements OnInit {
|
||||
async restartToApplyUpdate(): Promise<void> {
|
||||
await this.desktopUpdates.restartToApplyUpdate();
|
||||
}
|
||||
|
||||
private async setupDesktopDeepLinks(): Promise<void> {
|
||||
const electronApi = this.getDeepLinkElectronApi();
|
||||
|
||||
if (!electronApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.deepLinkCleanup = electronApi.onDeepLinkReceived?.((url) => {
|
||||
void this.handleDesktopDeepLink(url);
|
||||
}) || null;
|
||||
|
||||
const pendingDeepLink = await electronApi.consumePendingDeepLink?.();
|
||||
|
||||
if (pendingDeepLink) {
|
||||
await this.handleDesktopDeepLink(pendingDeepLink);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleDesktopDeepLink(url: string): Promise<void> {
|
||||
const invite = this.parseDesktopInviteUrl(url);
|
||||
|
||||
if (!invite) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.router.navigate(['/invite', invite.inviteId], {
|
||||
queryParams: {
|
||||
server: invite.sourceUrl
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getDeepLinkElectronApi(): DeepLinkElectronApi | null {
|
||||
return typeof window !== 'undefined'
|
||||
? (window as DeepLinkWindow).electronAPI ?? null
|
||||
: null;
|
||||
}
|
||||
|
||||
private isPublicRoute(url: string): boolean {
|
||||
return url === '/login' ||
|
||||
url === '/register' ||
|
||||
url.startsWith('/invite/');
|
||||
}
|
||||
|
||||
private parseDesktopInviteUrl(url: string): { inviteId: string; sourceUrl: string } | null {
|
||||
try {
|
||||
const parsedUrl = new URL(url);
|
||||
|
||||
if (parsedUrl.protocol !== 'toju:') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pathSegments = [parsedUrl.hostname, ...parsedUrl.pathname.split('/').filter(Boolean)]
|
||||
.map((segment) => decodeURIComponent(segment));
|
||||
|
||||
if (pathSegments[0] !== 'invite' || !pathSegments[1]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sourceUrl = parsedUrl.searchParams.get('server')?.trim();
|
||||
|
||||
if (!sourceUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
inviteId: pathSegments[1],
|
||||
sourceUrl
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user