mirror of
https://github.com/colanode/colanode.git
synced 2026-08-29 10:08:23 +02:00
Improve server loading and syncing (#253)
This commit is contained in:
@@ -186,6 +186,10 @@ const initApp = async (): Promise<AppInitOutput> => {
|
|||||||
await app.metadata.delete('app', 'theme.mode');
|
await app.metadata.delete('app', 'theme.mode');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add default Colanode servers
|
||||||
|
await app.createServer(new URL('https://eu.colanode.com/config'));
|
||||||
|
await app.createServer(new URL('https://us.colanode.com/config'));
|
||||||
|
|
||||||
return 'success';
|
return 'success';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ import {
|
|||||||
import { QueryInput, QueryMap } from '@colanode/client/queries';
|
import { QueryInput, QueryMap } from '@colanode/client/queries';
|
||||||
import { AppMeta, AppService } from '@colanode/client/services';
|
import { AppMeta, AppService } from '@colanode/client/services';
|
||||||
import { AppInitOutput } from '@colanode/client/types';
|
import { AppInitOutput } from '@colanode/client/types';
|
||||||
import { build, extractFileSubtype, generateId, IdType } from '@colanode/core';
|
import {
|
||||||
|
build,
|
||||||
|
extractFileSubtype,
|
||||||
|
generateId,
|
||||||
|
IdType,
|
||||||
|
isColanodeDomain,
|
||||||
|
} from '@colanode/core';
|
||||||
import {
|
import {
|
||||||
BroadcastInitMessage,
|
BroadcastInitMessage,
|
||||||
BroadcastMessage,
|
BroadcastMessage,
|
||||||
@@ -72,6 +78,12 @@ navigator.locks.request('colanode', async () => {
|
|||||||
await app.metadata.set('app', 'version', build.version);
|
await app.metadata.set('app', 'version', build.version);
|
||||||
await app.metadata.set('app', 'platform', appMeta.platform);
|
await app.metadata.set('app', 'platform', appMeta.platform);
|
||||||
|
|
||||||
|
const domain = self.location.hostname;
|
||||||
|
if (isColanodeDomain(domain)) {
|
||||||
|
await app.createServer(new URL('https://eu.colanode.com/config'));
|
||||||
|
await app.createServer(new URL('https://us.colanode.com/config'));
|
||||||
|
}
|
||||||
|
|
||||||
appInitOutput = 'success';
|
appInitOutput = 'success';
|
||||||
|
|
||||||
broadcastMessage({
|
broadcastMessage({
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import { RecordFieldValueSetMutationHandler } from './records/record-field-value
|
|||||||
import { RecordNameUpdateMutationHandler } from './records/record-name-update';
|
import { RecordNameUpdateMutationHandler } from './records/record-name-update';
|
||||||
import { ServerCreateMutationHandler } from './servers/server-create';
|
import { ServerCreateMutationHandler } from './servers/server-create';
|
||||||
import { ServerDeleteMutationHandler } from './servers/server-delete';
|
import { ServerDeleteMutationHandler } from './servers/server-delete';
|
||||||
|
import { ServerSyncMutationHandler } from './servers/server-sync';
|
||||||
import { SpaceChildReorderMutationHandler } from './spaces/space-child-reorder';
|
import { SpaceChildReorderMutationHandler } from './spaces/space-child-reorder';
|
||||||
import { SpaceCreateMutationHandler } from './spaces/space-create';
|
import { SpaceCreateMutationHandler } from './spaces/space-create';
|
||||||
import { SpaceDeleteMutationHandler } from './spaces/space-delete';
|
import { SpaceDeleteMutationHandler } from './spaces/space-delete';
|
||||||
@@ -121,6 +122,7 @@ export const buildMutationHandlerMap = (
|
|||||||
'select.option.update': new SelectOptionUpdateMutationHandler(app),
|
'select.option.update': new SelectOptionUpdateMutationHandler(app),
|
||||||
'server.create': new ServerCreateMutationHandler(app),
|
'server.create': new ServerCreateMutationHandler(app),
|
||||||
'server.delete': new ServerDeleteMutationHandler(app),
|
'server.delete': new ServerDeleteMutationHandler(app),
|
||||||
|
'server.sync': new ServerSyncMutationHandler(app),
|
||||||
'space.create': new SpaceCreateMutationHandler(app),
|
'space.create': new SpaceCreateMutationHandler(app),
|
||||||
'space.delete': new SpaceDeleteMutationHandler(app),
|
'space.delete': new SpaceDeleteMutationHandler(app),
|
||||||
'user.role.update': new UserRoleUpdateMutationHandler(app),
|
'user.role.update': new UserRoleUpdateMutationHandler(app),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
ServerDeleteMutationOutput,
|
ServerDeleteMutationOutput,
|
||||||
} from '@colanode/client/mutations/servers/server-delete';
|
} from '@colanode/client/mutations/servers/server-delete';
|
||||||
import { AppService } from '@colanode/client/services/app-service';
|
import { AppService } from '@colanode/client/services/app-service';
|
||||||
import { isColanodeServer } from '@colanode/core';
|
import { isColanodeDomain } from '@colanode/core';
|
||||||
|
|
||||||
export class ServerDeleteMutationHandler
|
export class ServerDeleteMutationHandler
|
||||||
implements MutationHandler<ServerDeleteMutationInput>
|
implements MutationHandler<ServerDeleteMutationInput>
|
||||||
@@ -19,7 +19,7 @@ export class ServerDeleteMutationHandler
|
|||||||
async handleMutation(
|
async handleMutation(
|
||||||
input: ServerDeleteMutationInput
|
input: ServerDeleteMutationInput
|
||||||
): Promise<ServerDeleteMutationOutput> {
|
): Promise<ServerDeleteMutationOutput> {
|
||||||
if (isColanodeServer(input.domain)) {
|
if (isColanodeDomain(input.domain)) {
|
||||||
throw new MutationError(
|
throw new MutationError(
|
||||||
MutationErrorCode.ServerDeleteForbidden,
|
MutationErrorCode.ServerDeleteForbidden,
|
||||||
'Cannot delete Colanode server'
|
'Cannot delete Colanode server'
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import ms from 'ms';
|
||||||
|
|
||||||
|
import { MutationHandler } from '@colanode/client/lib/types';
|
||||||
|
import { MutationError, MutationErrorCode } from '@colanode/client/mutations';
|
||||||
|
import {
|
||||||
|
ServerSyncMutationInput,
|
||||||
|
ServerSyncMutationOutput,
|
||||||
|
} from '@colanode/client/mutations/servers/server-sync';
|
||||||
|
import { AppService } from '@colanode/client/services/app-service';
|
||||||
|
|
||||||
|
export class ServerSyncMutationHandler
|
||||||
|
implements MutationHandler<ServerSyncMutationInput>
|
||||||
|
{
|
||||||
|
private readonly app: AppService;
|
||||||
|
|
||||||
|
constructor(app: AppService) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleMutation(
|
||||||
|
input: ServerSyncMutationInput
|
||||||
|
): Promise<ServerSyncMutationOutput> {
|
||||||
|
const server = this.app.getServer(input.domain);
|
||||||
|
if (!server) {
|
||||||
|
throw new MutationError(
|
||||||
|
MutationErrorCode.ServerNotFound,
|
||||||
|
`Server ${input.domain} was not found! Try using a different server.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to sync if the server has been synced in the last minute
|
||||||
|
const lastSyncedAt = server.server.syncedAt;
|
||||||
|
if (lastSyncedAt && lastSyncedAt.getTime() > Date.now() - ms('1 minute')) {
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const success = await server.sync();
|
||||||
|
return {
|
||||||
|
success,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import ms from 'ms';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
JobHandler,
|
JobHandler,
|
||||||
JobOutput,
|
JobOutput,
|
||||||
@@ -38,6 +40,20 @@ export class ServerSyncJobHandler implements JobHandler<ServerSyncInput> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accounts = this.app
|
||||||
|
.getAccounts()
|
||||||
|
.filter((account) => account.server.domain === server.domain);
|
||||||
|
|
||||||
|
if (accounts.length === 0) {
|
||||||
|
// don't sync if the server has no active accounts and has been synced in the last day
|
||||||
|
const lastSyncedAt = server.server.syncedAt;
|
||||||
|
if (lastSyncedAt && lastSyncedAt.getTime() > Date.now() - ms('1 day')) {
|
||||||
|
return {
|
||||||
|
type: 'success',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await server.sync();
|
await server.sync();
|
||||||
return {
|
return {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export * from './users/users-create';
|
|||||||
export * from './files/temp-file-create';
|
export * from './files/temp-file-create';
|
||||||
export * from './apps/tab-create';
|
export * from './apps/tab-create';
|
||||||
export * from './apps/tab-update';
|
export * from './apps/tab-update';
|
||||||
|
export * from './servers/server-sync';
|
||||||
export * from './apps/tab-delete';
|
export * from './apps/tab-delete';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
|
|||||||
17
packages/client/src/mutations/servers/server-sync.ts
Normal file
17
packages/client/src/mutations/servers/server-sync.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export type ServerSyncMutationInput = {
|
||||||
|
type: 'server.sync';
|
||||||
|
domain: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerSyncMutationOutput = {
|
||||||
|
success: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare module '@colanode/client/mutations' {
|
||||||
|
interface MutationMap {
|
||||||
|
'server.sync': {
|
||||||
|
input: ServerSyncMutationInput;
|
||||||
|
output: ServerSyncMutationOutput;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -97,7 +97,7 @@ export class ServerService {
|
|||||||
await this.app.jobs.triggerJobSchedule(scheduleId);
|
await this.app.jobs.triggerJobSchedule(scheduleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sync() {
|
public async sync(): Promise<boolean> {
|
||||||
const config = await ServerService.fetchServerConfig(this.configUrl);
|
const config = await ServerService.fetchServerConfig(this.configUrl);
|
||||||
if (config) {
|
if (config) {
|
||||||
const attributes: ServerAttributes = {
|
const attributes: ServerAttributes = {
|
||||||
@@ -158,6 +158,8 @@ export class ServerService {
|
|||||||
type: 'server.updated',
|
type: 'server.updated',
|
||||||
server: this.server,
|
server: this.server,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return isAvailable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async fetchServerConfig(configUrl: URL | string) {
|
public static async fetchServerConfig(configUrl: URL | string) {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export const isColanodeServer = (domain: string) => {
|
export const isColanodeDomain = (domain: string) => {
|
||||||
return domain.endsWith('.colanode.com');
|
return domain.endsWith('.colanode.com');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import { useLiveQuery } from '@tanstack/react-db';
|
import { useLiveQuery } from '@tanstack/react-db';
|
||||||
import { PlusIcon, SettingsIcon } from 'lucide-react';
|
import { PlusIcon } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { Server } from '@colanode/client/types';
|
import { Server } from '@colanode/client/types';
|
||||||
import { collections } from '@colanode/ui/collections';
|
import { collections } from '@colanode/ui/collections';
|
||||||
import { ServerAvatar } from '@colanode/ui/components/servers/server-avatar';
|
import { ServerCard } from '@colanode/ui/components/servers/server-card';
|
||||||
import { ServerCreateDialog } from '@colanode/ui/components/servers/server-create-dialog';
|
import { ServerCreateDialog } from '@colanode/ui/components/servers/server-create-dialog';
|
||||||
import { ServerDeleteDialog } from '@colanode/ui/components/servers/server-delete-dialog';
|
|
||||||
import { ServerSettingsDialog } from '@colanode/ui/components/servers/server-settings-dialog';
|
|
||||||
|
|
||||||
interface AuthServerProps {
|
interface AuthServerProps {
|
||||||
onSelect: (server: Server) => void;
|
onSelect: (server: Server) => void;
|
||||||
@@ -15,16 +13,11 @@ interface AuthServerProps {
|
|||||||
|
|
||||||
export const AuthServer = ({ onSelect }: AuthServerProps) => {
|
export const AuthServer = ({ onSelect }: AuthServerProps) => {
|
||||||
const [openCreate, setOpenCreate] = useState(false);
|
const [openCreate, setOpenCreate] = useState(false);
|
||||||
const [settingsDomain, setSettingsDomain] = useState<string | null>(null);
|
|
||||||
const [deleteDomain, setDeleteDomain] = useState<string | null>(null);
|
|
||||||
const serversQuery = useLiveQuery((q) =>
|
const serversQuery = useLiveQuery((q) =>
|
||||||
q.from({ servers: collections.servers })
|
q.from({ servers: collections.servers })
|
||||||
);
|
);
|
||||||
const servers = serversQuery.data ?? [];
|
const servers = serversQuery.data ?? [];
|
||||||
const settingsServer = servers.find(
|
|
||||||
(server) => server.domain === settingsDomain
|
|
||||||
);
|
|
||||||
const deleteServer = servers.find((server) => server.domain === deleteDomain);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6 w-full">
|
<div className="flex flex-col gap-6 w-full">
|
||||||
@@ -40,31 +33,7 @@ export const AuthServer = ({ onSelect }: AuthServerProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{servers.map((server) => (
|
{servers.map((server) => (
|
||||||
<button
|
<ServerCard key={server.domain} server={server} onSelect={onSelect} />
|
||||||
key={server.domain}
|
|
||||||
onClick={() => onSelect(server)}
|
|
||||||
className="group/server relative flex w-full flex-row items-center gap-3 rounded-lg border border-border/60 bg-background p-2 text-left transition-all hover:cursor-pointer hover:border-border hover:bg-accent hover:shadow-md"
|
|
||||||
>
|
|
||||||
<ServerAvatar
|
|
||||||
url={server.avatar}
|
|
||||||
name={server.name}
|
|
||||||
className="size-8 rounded-md"
|
|
||||||
/>
|
|
||||||
<div className="grow">
|
|
||||||
<p className="grow font-semibold">{server.name}</p>
|
|
||||||
<p className="text-xs text-muted-foreground">{server.domain}</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="text-muted-foreground opacity-0 group-hover/server:opacity-100 hover:bg-input size-8 flex items-center justify-center rounded-md cursor-pointer"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
setSettingsDomain(server.domain);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SettingsIcon className="size-4" />
|
|
||||||
</button>
|
|
||||||
</button>
|
|
||||||
))}
|
))}
|
||||||
<button
|
<button
|
||||||
onClick={() => setOpenCreate(true)}
|
onClick={() => setOpenCreate(true)}
|
||||||
@@ -83,32 +52,6 @@ export const AuthServer = ({ onSelect }: AuthServerProps) => {
|
|||||||
{openCreate && (
|
{openCreate && (
|
||||||
<ServerCreateDialog onCancel={() => setOpenCreate(false)} />
|
<ServerCreateDialog onCancel={() => setOpenCreate(false)} />
|
||||||
)}
|
)}
|
||||||
{deleteServer && (
|
|
||||||
<ServerDeleteDialog
|
|
||||||
server={deleteServer}
|
|
||||||
open={!!deleteServer}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setDeleteDomain(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{settingsServer && (
|
|
||||||
<ServerSettingsDialog
|
|
||||||
server={settingsServer}
|
|
||||||
open={!!settingsServer}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
setSettingsDomain(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onDelete={() => {
|
|
||||||
setSettingsDomain(null);
|
|
||||||
setDeleteDomain(settingsServer.domain);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
87
packages/ui/src/components/servers/server-card.tsx
Normal file
87
packages/ui/src/components/servers/server-card.tsx
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { SettingsIcon } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
import { Server } from '@colanode/client/types';
|
||||||
|
import { ServerAvatar } from '@colanode/ui/components/servers/server-avatar';
|
||||||
|
import { ServerDeleteDialog } from '@colanode/ui/components/servers/server-delete-dialog';
|
||||||
|
import { ServerSettingsDialog } from '@colanode/ui/components/servers/server-settings-dialog';
|
||||||
|
import { Spinner } from '@colanode/ui/components/ui/spinner';
|
||||||
|
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
||||||
|
|
||||||
|
interface ServerCardProps {
|
||||||
|
server: Server;
|
||||||
|
onSelect: (server: Server) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ServerCard = ({ server, onSelect }: ServerCardProps) => {
|
||||||
|
const [openSettings, setOpenSettings] = useState(false);
|
||||||
|
const [openDelete, setOpenDelete] = useState(false);
|
||||||
|
const { mutate: syncServer, isPending: isSyncing } = useMutation();
|
||||||
|
|
||||||
|
const handleServerClick = () => {
|
||||||
|
if (isSyncing) return;
|
||||||
|
|
||||||
|
syncServer({
|
||||||
|
input: {
|
||||||
|
type: 'server.sync',
|
||||||
|
domain: server.domain,
|
||||||
|
},
|
||||||
|
onSuccess() {
|
||||||
|
onSelect(server);
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
toast.error(error.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
onClick={handleServerClick}
|
||||||
|
disabled={isSyncing}
|
||||||
|
className="group/server relative flex w-full flex-row items-center gap-3 rounded-lg border border-border/60 bg-background p-2 text-left transition-all hover:cursor-pointer hover:border-border hover:bg-accent hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
<ServerAvatar
|
||||||
|
url={server.avatar}
|
||||||
|
name={server.name}
|
||||||
|
className="size-8 rounded-md"
|
||||||
|
/>
|
||||||
|
<div className="grow">
|
||||||
|
<p className="grow font-semibold">{server.name}</p>
|
||||||
|
<p className="text-xs text-muted-foreground">{server.domain}</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="text-muted-foreground opacity-0 group-hover/server:opacity-100 hover:bg-input size-8 flex items-center justify-center rounded-md cursor-pointer disabled:opacity-100"
|
||||||
|
disabled={isSyncing}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
setOpenSettings(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isSyncing ? (
|
||||||
|
<Spinner className="size-4" />
|
||||||
|
) : (
|
||||||
|
<SettingsIcon className="size-4" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</button>
|
||||||
|
<ServerSettingsDialog
|
||||||
|
server={server}
|
||||||
|
open={openSettings}
|
||||||
|
onOpenChange={setOpenSettings}
|
||||||
|
onDelete={() => {
|
||||||
|
setOpenSettings(false);
|
||||||
|
setOpenDelete(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ServerDeleteDialog
|
||||||
|
server={server}
|
||||||
|
open={openDelete}
|
||||||
|
onOpenChange={setOpenDelete}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,7 +2,7 @@ import { TrashIcon } from 'lucide-react';
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { Server } from '@colanode/client/types';
|
import { Server } from '@colanode/client/types';
|
||||||
import { formatDate, isColanodeServer, timeAgo } from '@colanode/core';
|
import { formatDate, isColanodeDomain, timeAgo } from '@colanode/core';
|
||||||
import { ServerAvatar } from '@colanode/ui/components/servers/server-avatar';
|
import { ServerAvatar } from '@colanode/ui/components/servers/server-avatar';
|
||||||
import { Badge } from '@colanode/ui/components/ui/badge';
|
import { Badge } from '@colanode/ui/components/ui/badge';
|
||||||
import { Button } from '@colanode/ui/components/ui/button';
|
import { Button } from '@colanode/ui/components/ui/button';
|
||||||
@@ -45,7 +45,7 @@ export const ServerSettingsDialog = ({
|
|||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
const canDelete = !isColanodeServer(server.domain);
|
const canDelete = !isColanodeDomain(server.domain);
|
||||||
const isAvailable = server.state?.isAvailable ?? false;
|
const isAvailable = server.state?.isAvailable ?? false;
|
||||||
const isOutdated = server.isOutdated;
|
const isOutdated = server.isOutdated;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Cloud, ExternalLink } from 'lucide-react';
|
import { Cloud, ExternalLink } from 'lucide-react';
|
||||||
|
|
||||||
import { isColanodeServer } from '@colanode/core';
|
import { isColanodeDomain } from '@colanode/core';
|
||||||
import { Button } from '@colanode/ui/components/ui/button';
|
import { Button } from '@colanode/ui/components/ui/button';
|
||||||
import { useServer } from '@colanode/ui/contexts/server';
|
import { useServer } from '@colanode/ui/contexts/server';
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ const CLOUD_URL = 'https://cloud.colanode.com';
|
|||||||
|
|
||||||
export const WorkspaceStorageCloud = () => {
|
export const WorkspaceStorageCloud = () => {
|
||||||
const server = useServer();
|
const server = useServer();
|
||||||
if (!isColanodeServer(server.domain)) {
|
if (!isColanodeDomain(server.domain)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user