Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { SelectAction, useActionGating } from '@hypha-platform/epics';
import { Locale } from '@hypha-platform/i18n';
import { isAbsoluteUrl } from '@hypha-platform/ui-utils';
import {
ArchiveIcon,
ArrowDownIcon,
ArrowLeftIcon,
ArrowRightIcon,
Expand Down Expand Up @@ -68,16 +67,6 @@ export const SelectSettingsAction = ({
baseTab: 'agreements',
icon: <ArrowRightIcon />,
},
{
group: 'Overview',
title: 'Archive Space (Coming Soon)',
description:
'Archive this space to disable activity while preserving its data and history.',
href: '#',
icon: <ArchiveIcon />,
baseTab: 'members',
disabled: true,
},
{
defaultDurationDays: 4,
group: 'Agreements',
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/app/[lang]/my-spaces/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export default async function Index(props: PageProps) {
title={space.title as string}
isSandbox={space.flags?.includes('sandbox') ?? false}
isDemo={space.flags?.includes('demo') ?? false}
isArchived={space.flags?.includes('archived') ?? false}
web3SpaceId={space.web3SpaceId as number}
createdAt={space.createdAt}
configPath={`${getDhoPathAgreements(
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/app/[lang]/network/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default async function Index(props: PageProps) {
const spaces = await getAllSpaces({
search: query?.trim() || undefined,
parentOnly: false,
omitArchived: true,
});

const uniqueCategories = extractUniqueCategories(spaces);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/categories/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ export type SpaceOrder = (typeof SPACE_ORDERS)[number];
/**
* @todo fix duplication. Origin at `packages/storage-postgres/src/schema/flags.ts`
*/
export const SPACE_FLAGS = ['sandbox', 'demo'] as const;
export const SPACE_FLAGS = ['sandbox', 'demo', 'archived'] as const;
export type SpaceFlags = (typeof SPACE_FLAGS)[number];
11 changes: 10 additions & 1 deletion packages/core/src/space/server/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ type FindAllSpacesProps = {
search?: string;
parentOnly?: boolean;
omitSandbox?: boolean;
omitArchived?: boolean;
};

export const findAllSpaces = async (
{ db }: DbConfig,
{ search, parentOnly = true, omitSandbox = false }: FindAllSpacesProps,
{
search,
parentOnly = true,
omitSandbox = false,
omitArchived = false,
}: FindAllSpacesProps,
) => {
const results = await db
.select({
Expand All @@ -50,6 +56,9 @@ export const findAllSpaces = async (
omitSandbox
? not(sql`${spaces.flags} @> '["sandbox"]'::jsonb`)
: undefined,
omitArchived
? not(sql`${spaces.flags} @> '["archived"]'::jsonb`)
: undefined,
search
? sql`(
-- Full-text search for exact word matches (highest priority)
Expand Down
64 changes: 58 additions & 6 deletions packages/epics/src/spaces/components/create-space-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,16 +300,23 @@ export const SpaceForm = ({
[flags],
);
const isDemo = React.useMemo(() => flags?.includes('demo') ?? false, [flags]);
const isArchived = React.useMemo(
() => flags?.includes('archived') ?? false,
[flags],
);
const isLive = React.useMemo(
() => !isDemo && !isSandbox,
[isDemo, isSandbox],
() => !isDemo && !isSandbox && !isArchived,
[isDemo, isSandbox, isArchived],
);

const toggleSandbox = React.useCallback(() => {
const current = form.getValues().flags ?? [];
const next = current.includes('sandbox')
? current.filter((f) => f !== 'sandbox')
: (['sandbox', ...current.filter((f) => f !== 'demo')] as SpaceFlags[]);
: ([
'sandbox',
...current.filter((f) => f !== 'demo' && f !== 'archived'),
] as SpaceFlags[]);
form.setValue('flags', next, { shouldDirty: true, shouldValidate: true });
if (next.includes('sandbox')) {
form.clearErrors('categories');
Expand All @@ -320,13 +327,29 @@ export const SpaceForm = ({
const current = form.getValues().flags ?? [];
const next = current.includes('demo')
? current.filter((f) => f !== 'demo')
: (['demo', ...current.filter((f) => f !== 'sandbox')] as SpaceFlags[]);
: ([
'demo',
...current.filter((f) => f !== 'sandbox' && f !== 'archived'),
] as SpaceFlags[]);
form.setValue('flags', next, { shouldDirty: true, shouldValidate: true });
}, [form]);

const toggleArchived = React.useCallback(() => {
const current = form.getValues().flags ?? [];
const next = current.includes('archived')
? current.filter((f) => f !== 'archived')
: ([
'archived',
...current.filter((f) => f !== 'demo' && f !== 'sandbox'),
] as SpaceFlags[]);
form.setValue('flags', next, { shouldDirty: true, shouldValidate: true });
}, [form]);

const toggleLive = React.useCallback(() => {
const current = form.getValues().flags ?? [];
const next = current.filter((f) => f !== 'demo' && f !== 'sandbox');
const next = current.filter(
(f) => f !== 'demo' && f !== 'sandbox' && f !== 'archived',
);
form.setValue('flags', next, { shouldDirty: true, shouldValidate: true });
}, [form]);

Expand Down Expand Up @@ -363,6 +386,7 @@ export const SpaceForm = ({
async (space) => {
if (
!space.flags?.includes('sandbox') &&
!space.flags?.includes('archived') &&
space.categories.length === 0
) {
showCategoriesError();
Expand All @@ -377,7 +401,11 @@ export const SpaceForm = ({
(e) => {
const flags = form.getValues()['flags'];
const categories = form.getValues()['categories'];
if (!flags?.includes('sandbox') && categories.length === 0) {
if (
!flags?.includes('sandbox') &&
!flags?.includes('archived') &&
categories.length === 0
) {
showCategoriesError();
}
if (parentSpaceId === -1) {
Expand Down Expand Up @@ -678,6 +706,30 @@ export const SpaceForm = ({
</span>
</div>
</Card>
{label === 'configure' && (
<Card
className={clsx(
'flex p-6 cursor-pointer space-x-4 items-center',
{
'border-accent-9': isArchived,
'hover:border-accent-5': !isArchived,
},
)}
onClick={toggleArchived}
>
<div className="flex flex-col">
<span className="text-2 font-medium">Archive Mode</span>
<span className="text-1 text-neutral-11">
<span>
Archive this space to temporarily pause activity or
deactivate it while keeping all data and history safe. You
can reactivate it anytime by selecting a different
activation mode.
</span>
</span>
</div>
</Card>
)}
</div>
<div className="flex justify-end w-full">
<Button
Expand Down
27 changes: 23 additions & 4 deletions packages/epics/src/spaces/components/my-filtered-spaces.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useMe } from '@hypha-platform/core/client';
import React from 'react';
import { Text } from '@radix-ui/themes';
import { useFilterSpacesListWithDiscoverability } from '../hooks/use-spaces-discoverability-batch';
import { SectionFilter, Input } from '@hypha-platform/ui';

export function filterSpaces(
spaces: Space[],
Expand Down Expand Up @@ -36,6 +37,7 @@ export function MyFilteredSpaces({
const { web3SpaceIds } = useMemberWeb3SpaceIds({
personAddress: person?.address as Address | undefined,
});
const [hideArchivedSpaces, setHideArchivedSpaces] = React.useState(true);

const memberFilteredSpaces = React.useMemo(
() => filterSpaces(spaces, person?.slug, web3SpaceIds),
Expand All @@ -48,14 +50,31 @@ export function MyFilteredSpaces({
useGeneralState: false,
});

const displayedSpaces = React.useMemo(() => {
if (hideArchivedSpaces) {
return filteredSpaces.filter(
(space) => !space.flags?.includes('archived'),
);
}
return filteredSpaces;
}, [filteredSpaces, hideArchivedSpaces]);

return (
<div className="space-y-6">
<div className="justify-between items-center flex">
<Text className="text-4">My Spaces | {filteredSpaces.length}</Text>
</div>
<SectionFilter count={displayedSpaces.length} label="My Spaces">
<label className="flex items-center gap-1">
<Input
type="checkbox"
checked={hideArchivedSpaces}
onChange={(e) => setHideArchivedSpaces(e.target.checked)}
className="h-4 w-4"
/>
<span>Hide archived spaces</span>
</label>
</SectionFilter>
<SpaceCardList
lang={lang}
spaces={filteredSpaces}
spaces={displayedSpaces}
showLoadMore={showLoadMore}
showExitButton={true}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function SpaceCardWithDiscoverability({
agreements={space.documentCount}
isSandbox={space.flags?.includes('sandbox') ?? false}
isDemo={space.flags?.includes('demo') ?? false}
isArchived={space.flags?.includes('archived') ?? false}
web3SpaceId={space.web3SpaceId as number}
configPath={`${getHref(space.slug).replace(
/\/+$/,
Expand Down
3 changes: 3 additions & 0 deletions packages/epics/src/spaces/components/space-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type SpaceCardProps = {
leadImage?: string;
isSandbox?: boolean;
isDemo?: boolean;
isArchived?: boolean;
configPath?: string;
web3SpaceId?: number;
createdAt: Date;
Expand All @@ -46,6 +47,7 @@ export const SpaceCard: React.FC<SpaceCardProps> = ({
leadImage,
isSandbox = false,
isDemo = false,
isArchived = false,
configPath,
web3SpaceId,
createdAt,
Expand Down Expand Up @@ -156,6 +158,7 @@ export const SpaceCard: React.FC<SpaceCardProps> = ({
web3SpaceId={web3SpaceId}
isSandbox={isSandbox}
isDemo={isDemo}
isArchived={isArchived}
configPath={configPath}
className="ml-2"
/>
Expand Down
41 changes: 32 additions & 9 deletions packages/epics/src/spaces/components/space-mode-label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import { useAuthentication } from '@hypha-platform/authentication';
import { Badge } from '@hypha-platform/ui';
import clsx from 'clsx';
import Link from 'next/link';
import { useSpaceMember } from '../hooks';
import { useRouter } from 'next/navigation';

interface SpaceModeLabelProps {
isSandbox: boolean;
isDemo: boolean;
isArchived: boolean;
configPath?: string;
web3SpaceId?: number;
className?: string;
Expand All @@ -18,15 +18,17 @@ interface SpaceModeLabelProps {
const LabelButton = ({
caption,
configPath,
colorVariant = 'accent',
}: {
caption: string;
configPath: string;
colorVariant?: 'accent' | 'error';
}) => {
const router = useRouter();
return (
<Badge
className="flex cursor-pointer"
colorVariant="accent"
colorVariant={colorVariant}
variant="outline"
role="link"
title="Change Space Configuration"
Expand All @@ -48,8 +50,14 @@ const LabelButton = ({
</Badge>
);
};
const LabelBadge = ({ caption }: { caption: string }) => (
<Badge className="flex" colorVariant="accent" variant="outline">
const LabelBadge = ({
caption,
colorVariant = 'accent',
}: {
caption: string;
colorVariant?: 'accent' | 'error';
}) => (
<Badge className="flex" colorVariant={colorVariant} variant="outline">
{caption}
</Badge>
);
Expand All @@ -58,42 +66,57 @@ const MemberLabel = ({
caption,
web3SpaceId,
configPath,
colorVariant = 'accent',
}: {
caption: string;
web3SpaceId: number;
configPath: string;
colorVariant?: 'accent' | 'error';
}) => {
const { isMember } = useSpaceMember({ spaceId: web3SpaceId });
return isMember ? (
<LabelButton caption={caption} configPath={configPath} />
<LabelButton
caption={caption}
configPath={configPath}
colorVariant={colorVariant}
/>
) : (
<LabelBadge caption={caption} />
<LabelBadge caption={caption} colorVariant={colorVariant} />
);
};

export const SpaceModeLabel = ({
isSandbox,
isDemo,
isArchived,
configPath,
web3SpaceId,
className,
}: SpaceModeLabelProps) => {
const { isAuthenticated } = useAuthentication();
const isLive = !isSandbox && !isDemo;
const isLive = !isSandbox && !isDemo && !isArchived;
if (isLive) {
return null;
}
const caption = isSandbox ? 'Sandbox' : isDemo ? 'Pilot' : '';
const caption = isSandbox
? 'Sandbox'
: isDemo
? 'Pilot'
: isArchived
? 'Archived'
: '';
const colorVariant = isArchived ? 'error' : 'accent';
return (
<div className={clsx('flex', className)}>
{isAuthenticated && !!configPath && Number.isFinite(web3SpaceId) ? (
<MemberLabel
caption={caption}
web3SpaceId={web3SpaceId as number}
configPath={configPath}
colorVariant={colorVariant}
/>
) : (
<LabelBadge caption={caption} />
<LabelBadge caption={caption} colorVariant={colorVariant} />
)}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion packages/storage-postgres/src/schema/flags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { pgEnum } from 'drizzle-orm/pg-core';

export const SPACE_FLAGS = ['sandbox', 'demo'] as const;
export const SPACE_FLAGS = ['sandbox', 'demo', 'archived'] as const;
export const spaceFlags = pgEnum('space_flags', SPACE_FLAGS);
Loading