mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-04 06:20:28 +03:00 
			
		
		
		
	Use a common CollapsibleCard element for collapsible settings cards
This commit is contained in:
		
							parent
							
								
									af9460ef8b
								
							
						
					
					
						commit
						d42bcc6e3d
					
				
					 7 changed files with 322 additions and 390 deletions
				
			
		| 
						 | 
				
			
			@ -195,8 +195,8 @@ function AccountDataEdit({
 | 
			
		|||
type AccountDataViewProps = {
 | 
			
		||||
  type: string;
 | 
			
		||||
  defaultContent: string;
 | 
			
		||||
  onEdit: () => void;
 | 
			
		||||
  requestClose: () => void;
 | 
			
		||||
  onEdit?: () => void;
 | 
			
		||||
  submitDelete?: AccountDataDeleteCallback;
 | 
			
		||||
};
 | 
			
		||||
function AccountDataView({ type, defaultContent, onEdit, requestClose, submitDelete }: AccountDataViewProps) {
 | 
			
		||||
| 
						 | 
				
			
			@ -231,9 +231,11 @@ function AccountDataView({ type, defaultContent, onEdit, requestClose, submitDel
 | 
			
		|||
            required
 | 
			
		||||
          />
 | 
			
		||||
        </Box>
 | 
			
		||||
        <Button variant="Secondary" size="400" radii="300" onClick={onEdit}>
 | 
			
		||||
          <Text size="B400">Edit</Text>
 | 
			
		||||
        </Button>
 | 
			
		||||
        {onEdit && (
 | 
			
		||||
          <Button variant="Secondary" size="400" radii="300" onClick={onEdit}>
 | 
			
		||||
            <Text size="B400">Edit</Text>
 | 
			
		||||
          </Button>
 | 
			
		||||
        )}
 | 
			
		||||
        {submitDelete && (
 | 
			
		||||
          <Button
 | 
			
		||||
            variant="Critical"
 | 
			
		||||
| 
						 | 
				
			
			@ -269,7 +271,7 @@ function AccountDataView({ type, defaultContent, onEdit, requestClose, submitDel
 | 
			
		|||
export type AccountDataEditorProps = {
 | 
			
		||||
  type?: string;
 | 
			
		||||
  content?: unknown;
 | 
			
		||||
  submitChange: AccountDataSubmitCallback;
 | 
			
		||||
  submitChange?: AccountDataSubmitCallback;
 | 
			
		||||
  submitDelete?: AccountDataDeleteCallback;
 | 
			
		||||
  requestClose: () => void;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -328,7 +330,7 @@ export function AccountDataEditor({
 | 
			
		|||
        </Box>
 | 
			
		||||
      </PageHeader>
 | 
			
		||||
      <Box grow="Yes" direction="Column">
 | 
			
		||||
        {edit ? (
 | 
			
		||||
        {(edit && submitChange) ? (
 | 
			
		||||
          <AccountDataEdit
 | 
			
		||||
            type={data.type}
 | 
			
		||||
            defaultContent={contentJSONStr}
 | 
			
		||||
| 
						 | 
				
			
			@ -340,8 +342,8 @@ export function AccountDataEditor({
 | 
			
		|||
          <AccountDataView
 | 
			
		||||
            type={data.type}
 | 
			
		||||
            defaultContent={contentJSONStr}
 | 
			
		||||
            onEdit={() => setEdit(true)}
 | 
			
		||||
            requestClose={requestClose}
 | 
			
		||||
            onEdit={submitChange ? () => setEdit(true) : undefined}
 | 
			
		||||
            submitDelete={submitDelete}
 | 
			
		||||
          />
 | 
			
		||||
        )}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										54
									
								
								src/app/components/CollapsibleCard.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/app/components/CollapsibleCard.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
import React, { ReactNode } from 'react';
 | 
			
		||||
import { Button, Icon, Icons, Text } from 'folds';
 | 
			
		||||
import { SequenceCard } from './sequence-card';
 | 
			
		||||
import { SequenceCardStyle } from '../features/settings/styles.css';
 | 
			
		||||
import { SettingTile } from './setting-tile';
 | 
			
		||||
 | 
			
		||||
type CollapsibleCardProps = {
 | 
			
		||||
  expand: boolean;
 | 
			
		||||
  setExpand: (expand: boolean) => void;
 | 
			
		||||
  title?: ReactNode;
 | 
			
		||||
  description?: ReactNode;
 | 
			
		||||
  before?: ReactNode;
 | 
			
		||||
  children?: ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function CollapsibleCard({
 | 
			
		||||
  expand,
 | 
			
		||||
  setExpand,
 | 
			
		||||
  title,
 | 
			
		||||
  description,
 | 
			
		||||
  before,
 | 
			
		||||
  children,
 | 
			
		||||
}: CollapsibleCardProps) {
 | 
			
		||||
  return (
 | 
			
		||||
    <SequenceCard
 | 
			
		||||
      className={SequenceCardStyle}
 | 
			
		||||
      variant="SurfaceVariant"
 | 
			
		||||
      direction="Column"
 | 
			
		||||
      gap="400"
 | 
			
		||||
    >
 | 
			
		||||
      <SettingTile
 | 
			
		||||
        title={title}
 | 
			
		||||
        description={description}
 | 
			
		||||
        before={before}
 | 
			
		||||
        after={
 | 
			
		||||
          <Button
 | 
			
		||||
            onClick={() => setExpand(!expand)}
 | 
			
		||||
            variant="Secondary"
 | 
			
		||||
            fill="Soft"
 | 
			
		||||
            size="300"
 | 
			
		||||
            radii="300"
 | 
			
		||||
            outlined
 | 
			
		||||
            before={
 | 
			
		||||
              <Icon src={expand ? Icons.ChevronTop : Icons.ChevronBottom} size="100" filled />
 | 
			
		||||
            }
 | 
			
		||||
          >
 | 
			
		||||
            <Text size="B300">{expand ? 'Collapse' : 'Expand'}</Text>
 | 
			
		||||
          </Button>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {expand && children}
 | 
			
		||||
    </SequenceCard>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ import {
 | 
			
		|||
  AccountDataSubmitCallback,
 | 
			
		||||
} from '../../../components/AccountDataEditor';
 | 
			
		||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
 | 
			
		||||
import { CollapsibleCard } from '../../../components/CollapsibleCard';
 | 
			
		||||
 | 
			
		||||
type DeveloperToolsProps = {
 | 
			
		||||
  requestClose: () => void;
 | 
			
		||||
| 
						 | 
				
			
			@ -175,216 +176,166 @@ export function DeveloperTools({ requestClose }: DeveloperToolsProps) {
 | 
			
		|||
                      }
 | 
			
		||||
                    />
 | 
			
		||||
                  </SequenceCard>
 | 
			
		||||
                  <SequenceCard
 | 
			
		||||
                    className={SequenceCardStyle}
 | 
			
		||||
                    variant="SurfaceVariant"
 | 
			
		||||
                    direction="Column"
 | 
			
		||||
                    gap="400"
 | 
			
		||||
                  <CollapsibleCard
 | 
			
		||||
                    expand={expandState}
 | 
			
		||||
                    setExpand={setExpandState}
 | 
			
		||||
                    title="Room State"
 | 
			
		||||
                    description="State events of the room."
 | 
			
		||||
                  >
 | 
			
		||||
                    <SettingTile
 | 
			
		||||
                      title="Room State"
 | 
			
		||||
                      description="State events of the room."
 | 
			
		||||
                      after={
 | 
			
		||||
                        <Button
 | 
			
		||||
                          onClick={() => setExpandState(!expandState)}
 | 
			
		||||
                          variant="Secondary"
 | 
			
		||||
                          fill="Soft"
 | 
			
		||||
                    <Box direction="Column" gap="100">
 | 
			
		||||
                      <Box justifyContent="SpaceBetween">
 | 
			
		||||
                        <Text size="L400">Events</Text>
 | 
			
		||||
                        <Text size="L400">Total: {roomState.size}</Text>
 | 
			
		||||
                      </Box>
 | 
			
		||||
                      <CutoutCard>
 | 
			
		||||
                        <MenuItem
 | 
			
		||||
                          onClick={() => setComposeEvent({ stateKey: '' })}
 | 
			
		||||
                          variant="Surface"
 | 
			
		||||
                          fill="None"
 | 
			
		||||
                          size="300"
 | 
			
		||||
                          radii="300"
 | 
			
		||||
                          outlined
 | 
			
		||||
                          before={
 | 
			
		||||
                            <Icon
 | 
			
		||||
                              src={expandState ? Icons.ChevronTop : Icons.ChevronBottom}
 | 
			
		||||
                              size="100"
 | 
			
		||||
                              filled
 | 
			
		||||
                            />
 | 
			
		||||
                          }
 | 
			
		||||
                          radii="0"
 | 
			
		||||
                          before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
                        >
 | 
			
		||||
                          <Text size="B300">{expandState ? 'Collapse' : 'Expand'}</Text>
 | 
			
		||||
                        </Button>
 | 
			
		||||
                      }
 | 
			
		||||
                    />
 | 
			
		||||
                    {expandState && (
 | 
			
		||||
                      <Box direction="Column" gap="100">
 | 
			
		||||
                        <Box justifyContent="SpaceBetween">
 | 
			
		||||
                          <Text size="L400">Events</Text>
 | 
			
		||||
                          <Text size="L400">Total: {roomState.size}</Text>
 | 
			
		||||
                        </Box>
 | 
			
		||||
                        <CutoutCard>
 | 
			
		||||
                          <MenuItem
 | 
			
		||||
                            onClick={() => setComposeEvent({ stateKey: '' })}
 | 
			
		||||
                            variant="Surface"
 | 
			
		||||
                            fill="None"
 | 
			
		||||
                            size="300"
 | 
			
		||||
                            radii="0"
 | 
			
		||||
                            before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
                          >
 | 
			
		||||
                            <Box grow="Yes">
 | 
			
		||||
                              <Text size="T200" truncate>
 | 
			
		||||
                                Add New
 | 
			
		||||
                              </Text>
 | 
			
		||||
                            </Box>
 | 
			
		||||
                          </MenuItem>
 | 
			
		||||
                          {Array.from(roomState.keys())
 | 
			
		||||
                            .sort()
 | 
			
		||||
                            .map((eventType) => {
 | 
			
		||||
                              const expanded = eventType === expandStateType;
 | 
			
		||||
                              const stateKeyToEvents = roomState.get(eventType);
 | 
			
		||||
                              if (!stateKeyToEvents) return null;
 | 
			
		||||
                          <Box grow="Yes">
 | 
			
		||||
                            <Text size="T200" truncate>
 | 
			
		||||
                              Add New
 | 
			
		||||
                            </Text>
 | 
			
		||||
                          </Box>
 | 
			
		||||
                        </MenuItem>
 | 
			
		||||
                        {Array.from(roomState.keys())
 | 
			
		||||
                          .sort()
 | 
			
		||||
                          .map((eventType) => {
 | 
			
		||||
                            const expanded = eventType === expandStateType;
 | 
			
		||||
                            const stateKeyToEvents = roomState.get(eventType);
 | 
			
		||||
                            if (!stateKeyToEvents) return null;
 | 
			
		||||
 | 
			
		||||
                              return (
 | 
			
		||||
                                <Box id={eventType} key={eventType} direction="Column" gap="100">
 | 
			
		||||
                                  <MenuItem
 | 
			
		||||
                                    onClick={() =>
 | 
			
		||||
                                      setExpandStateType(expanded ? undefined : eventType)
 | 
			
		||||
                                    }
 | 
			
		||||
                                    variant="Surface"
 | 
			
		||||
                                    fill="None"
 | 
			
		||||
                                    size="300"
 | 
			
		||||
                                    radii="0"
 | 
			
		||||
                                    before={
 | 
			
		||||
                                      <Icon
 | 
			
		||||
                                        size="50"
 | 
			
		||||
                                        src={expanded ? Icons.ChevronBottom : Icons.ChevronRight}
 | 
			
		||||
                                      />
 | 
			
		||||
                                    }
 | 
			
		||||
                                    after={<Text size="L400">{stateKeyToEvents.size}</Text>}
 | 
			
		||||
                            return (
 | 
			
		||||
                              <Box id={eventType} key={eventType} direction="Column" gap="100">
 | 
			
		||||
                                <MenuItem
 | 
			
		||||
                                  onClick={() =>
 | 
			
		||||
                                    setExpandStateType(expanded ? undefined : eventType)
 | 
			
		||||
                                  }
 | 
			
		||||
                                  variant="Surface"
 | 
			
		||||
                                  fill="None"
 | 
			
		||||
                                  size="300"
 | 
			
		||||
                                  radii="0"
 | 
			
		||||
                                  before={
 | 
			
		||||
                                    <Icon
 | 
			
		||||
                                      size="50"
 | 
			
		||||
                                      src={expanded ? Icons.ChevronBottom : Icons.ChevronRight}
 | 
			
		||||
                                    />
 | 
			
		||||
                                  }
 | 
			
		||||
                                  after={<Text size="L400">{stateKeyToEvents.size}</Text>}
 | 
			
		||||
                                >
 | 
			
		||||
                                  <Box grow="Yes">
 | 
			
		||||
                                    <Text size="T200" truncate>
 | 
			
		||||
                                      {eventType}
 | 
			
		||||
                                    </Text>
 | 
			
		||||
                                  </Box>
 | 
			
		||||
                                </MenuItem>
 | 
			
		||||
                                {expanded && (
 | 
			
		||||
                                  <div
 | 
			
		||||
                                    style={{
 | 
			
		||||
                                      marginLeft: config.space.S400,
 | 
			
		||||
                                      borderLeft: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
 | 
			
		||||
                                    }}
 | 
			
		||||
                                  >
 | 
			
		||||
                                    <Box grow="Yes">
 | 
			
		||||
                                      <Text size="T200" truncate>
 | 
			
		||||
                                        {eventType}
 | 
			
		||||
                                      </Text>
 | 
			
		||||
                                    </Box>
 | 
			
		||||
                                  </MenuItem>
 | 
			
		||||
                                  {expanded && (
 | 
			
		||||
                                    <div
 | 
			
		||||
                                      style={{
 | 
			
		||||
                                        marginLeft: config.space.S400,
 | 
			
		||||
                                        borderLeft: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
 | 
			
		||||
                                      }}
 | 
			
		||||
                                    <MenuItem
 | 
			
		||||
                                      onClick={() =>
 | 
			
		||||
                                        setComposeEvent({ type: eventType, stateKey: '' })
 | 
			
		||||
                                      }
 | 
			
		||||
                                      variant="Surface"
 | 
			
		||||
                                      fill="None"
 | 
			
		||||
                                      size="300"
 | 
			
		||||
                                      radii="0"
 | 
			
		||||
                                      before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
                                    >
 | 
			
		||||
                                      <MenuItem
 | 
			
		||||
                                        onClick={() =>
 | 
			
		||||
                                          setComposeEvent({ type: eventType, stateKey: '' })
 | 
			
		||||
                                        }
 | 
			
		||||
                                        variant="Surface"
 | 
			
		||||
                                        fill="None"
 | 
			
		||||
                                        size="300"
 | 
			
		||||
                                        radii="0"
 | 
			
		||||
                                        before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
                                      >
 | 
			
		||||
                                        <Box grow="Yes">
 | 
			
		||||
                                          <Text size="T200" truncate>
 | 
			
		||||
                                            Add New
 | 
			
		||||
                                          </Text>
 | 
			
		||||
                                        </Box>
 | 
			
		||||
                                      </MenuItem>
 | 
			
		||||
                                      {Array.from(stateKeyToEvents.keys())
 | 
			
		||||
                                        .sort()
 | 
			
		||||
                                        .map((stateKey) => (
 | 
			
		||||
                                          <MenuItem
 | 
			
		||||
                                            onClick={() => {
 | 
			
		||||
                                              setOpenStateEvent({
 | 
			
		||||
                                                type: eventType,
 | 
			
		||||
                                                stateKey,
 | 
			
		||||
                                              });
 | 
			
		||||
                                            }}
 | 
			
		||||
                                            key={stateKey}
 | 
			
		||||
                                            variant="Surface"
 | 
			
		||||
                                            fill="None"
 | 
			
		||||
                                            size="300"
 | 
			
		||||
                                            radii="0"
 | 
			
		||||
                                            after={<Icon size="50" src={Icons.ChevronRight} />}
 | 
			
		||||
                                          >
 | 
			
		||||
                                            <Box grow="Yes">
 | 
			
		||||
                                              <Text size="T200" truncate>
 | 
			
		||||
                                                {stateKey ? `"${stateKey}"` : 'Default'}
 | 
			
		||||
                                              </Text>
 | 
			
		||||
                                            </Box>
 | 
			
		||||
                                          </MenuItem>
 | 
			
		||||
                                        ))}
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                  )}
 | 
			
		||||
                                </Box>
 | 
			
		||||
                              );
 | 
			
		||||
                            })}
 | 
			
		||||
                        </CutoutCard>
 | 
			
		||||
                      </Box>
 | 
			
		||||
                    )}
 | 
			
		||||
                  </SequenceCard>
 | 
			
		||||
                  <SequenceCard
 | 
			
		||||
                    className={SequenceCardStyle}
 | 
			
		||||
                    variant="SurfaceVariant"
 | 
			
		||||
                    direction="Column"
 | 
			
		||||
                    gap="400"
 | 
			
		||||
                                      <Box grow="Yes">
 | 
			
		||||
                                        <Text size="T200" truncate>
 | 
			
		||||
                                          Add New
 | 
			
		||||
                                        </Text>
 | 
			
		||||
                                      </Box>
 | 
			
		||||
                                    </MenuItem>
 | 
			
		||||
                                    {Array.from(stateKeyToEvents.keys())
 | 
			
		||||
                                      .sort()
 | 
			
		||||
                                      .map((stateKey) => (
 | 
			
		||||
                                        <MenuItem
 | 
			
		||||
                                          onClick={() => {
 | 
			
		||||
                                            setOpenStateEvent({
 | 
			
		||||
                                              type: eventType,
 | 
			
		||||
                                              stateKey,
 | 
			
		||||
                                            });
 | 
			
		||||
                                          }}
 | 
			
		||||
                                          key={stateKey}
 | 
			
		||||
                                          variant="Surface"
 | 
			
		||||
                                          fill="None"
 | 
			
		||||
                                          size="300"
 | 
			
		||||
                                          radii="0"
 | 
			
		||||
                                          after={<Icon size="50" src={Icons.ChevronRight} />}
 | 
			
		||||
                                        >
 | 
			
		||||
                                          <Box grow="Yes">
 | 
			
		||||
                                            <Text size="T200" truncate>
 | 
			
		||||
                                              {stateKey ? `"${stateKey}"` : 'Default'}
 | 
			
		||||
                                            </Text>
 | 
			
		||||
                                          </Box>
 | 
			
		||||
                                        </MenuItem>
 | 
			
		||||
                                      ))}
 | 
			
		||||
                                  </div>
 | 
			
		||||
                                )}
 | 
			
		||||
                              </Box>
 | 
			
		||||
                            );
 | 
			
		||||
                          })}
 | 
			
		||||
                      </CutoutCard>
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  </CollapsibleCard>
 | 
			
		||||
                  <CollapsibleCard
 | 
			
		||||
                    expand={expandAccountData}
 | 
			
		||||
                    setExpand={setExpandAccountData}
 | 
			
		||||
                    title="Account Data"
 | 
			
		||||
                    description="Private personalization data stored within room"
 | 
			
		||||
                  >
 | 
			
		||||
                    <SettingTile
 | 
			
		||||
                      title="Account Data"
 | 
			
		||||
                      description="Private personalization data stored within room."
 | 
			
		||||
                      after={
 | 
			
		||||
                        <Button
 | 
			
		||||
                          onClick={() => setExpandAccountData(!expandAccountData)}
 | 
			
		||||
                          variant="Secondary"
 | 
			
		||||
                          fill="Soft"
 | 
			
		||||
                          size="300"
 | 
			
		||||
                          radii="300"
 | 
			
		||||
                          outlined
 | 
			
		||||
                          before={
 | 
			
		||||
                            <Icon
 | 
			
		||||
                              src={expandAccountData ? Icons.ChevronTop : Icons.ChevronBottom}
 | 
			
		||||
                              size="100"
 | 
			
		||||
                              filled
 | 
			
		||||
                            />
 | 
			
		||||
                          }
 | 
			
		||||
                        >
 | 
			
		||||
                          <Text size="B300">{expandAccountData ? 'Collapse' : 'Expand'}</Text>
 | 
			
		||||
                        </Button>
 | 
			
		||||
                      }
 | 
			
		||||
                    />
 | 
			
		||||
                    {expandAccountData && (
 | 
			
		||||
                      <Box direction="Column" gap="100">
 | 
			
		||||
                        <Box justifyContent="SpaceBetween">
 | 
			
		||||
                          <Text size="L400">Events</Text>
 | 
			
		||||
                          <Text size="L400">Total: {accountData.size}</Text>
 | 
			
		||||
                        </Box>
 | 
			
		||||
                        <CutoutCard>
 | 
			
		||||
                          <MenuItem
 | 
			
		||||
                            variant="Surface"
 | 
			
		||||
                            fill="None"
 | 
			
		||||
                            size="300"
 | 
			
		||||
                            radii="0"
 | 
			
		||||
                            before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
                            onClick={() => setAccountDataType(null)}
 | 
			
		||||
                          >
 | 
			
		||||
                            <Box grow="Yes">
 | 
			
		||||
                              <Text size="T200" truncate>
 | 
			
		||||
                                Add New
 | 
			
		||||
                              </Text>
 | 
			
		||||
                            </Box>
 | 
			
		||||
                          </MenuItem>
 | 
			
		||||
                          {Array.from(accountData.keys())
 | 
			
		||||
                            .sort()
 | 
			
		||||
                            .map((type) => (
 | 
			
		||||
                              <MenuItem
 | 
			
		||||
                                key={type}
 | 
			
		||||
                                variant="Surface"
 | 
			
		||||
                                fill="None"
 | 
			
		||||
                                size="300"
 | 
			
		||||
                                radii="0"
 | 
			
		||||
                                after={<Icon size="50" src={Icons.ChevronRight} />}
 | 
			
		||||
                                onClick={() => setAccountDataType(type)}
 | 
			
		||||
                              >
 | 
			
		||||
                                <Box grow="Yes">
 | 
			
		||||
                                  <Text size="T200" truncate>
 | 
			
		||||
                                    {type}
 | 
			
		||||
                                  </Text>
 | 
			
		||||
                                </Box>
 | 
			
		||||
                              </MenuItem>
 | 
			
		||||
                            ))}
 | 
			
		||||
                        </CutoutCard>
 | 
			
		||||
                    <Box direction="Column" gap="100">
 | 
			
		||||
                      <Box justifyContent="SpaceBetween">
 | 
			
		||||
                        <Text size="L400">Events</Text>
 | 
			
		||||
                        <Text size="L400">Total: {accountData.size}</Text>
 | 
			
		||||
                      </Box>
 | 
			
		||||
                    )}
 | 
			
		||||
                  </SequenceCard>
 | 
			
		||||
                      <CutoutCard>
 | 
			
		||||
                        <MenuItem
 | 
			
		||||
                          variant="Surface"
 | 
			
		||||
                          fill="None"
 | 
			
		||||
                          size="300"
 | 
			
		||||
                          radii="0"
 | 
			
		||||
                          before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
                          onClick={() => setAccountDataType(null)}
 | 
			
		||||
                        >
 | 
			
		||||
                          <Box grow="Yes">
 | 
			
		||||
                            <Text size="T200" truncate>
 | 
			
		||||
                              Add New
 | 
			
		||||
                            </Text>
 | 
			
		||||
                          </Box>
 | 
			
		||||
                        </MenuItem>
 | 
			
		||||
                        {Array.from(accountData.keys())
 | 
			
		||||
                          .sort()
 | 
			
		||||
                          .map((type) => (
 | 
			
		||||
                            <MenuItem
 | 
			
		||||
                              key={type}
 | 
			
		||||
                              variant="Surface"
 | 
			
		||||
                              fill="None"
 | 
			
		||||
                              size="300"
 | 
			
		||||
                              radii="0"
 | 
			
		||||
                              after={<Icon size="50" src={Icons.ChevronRight} />}
 | 
			
		||||
                              onClick={() => setAccountDataType(type)}
 | 
			
		||||
                            >
 | 
			
		||||
                              <Box grow="Yes">
 | 
			
		||||
                                <Text size="T200" truncate>
 | 
			
		||||
                                  {type}
 | 
			
		||||
                                </Text>
 | 
			
		||||
                              </Box>
 | 
			
		||||
                            </MenuItem>
 | 
			
		||||
                          ))}
 | 
			
		||||
                      </CutoutCard>
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  </CollapsibleCard>
 | 
			
		||||
                </Box>
 | 
			
		||||
              )}
 | 
			
		||||
            </Box>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,6 @@ import {
 | 
			
		|||
  Chip,
 | 
			
		||||
  color,
 | 
			
		||||
  config,
 | 
			
		||||
  Icon,
 | 
			
		||||
  Icons,
 | 
			
		||||
  Input,
 | 
			
		||||
  Spinner,
 | 
			
		||||
  Text,
 | 
			
		||||
| 
						 | 
				
			
			@ -33,6 +31,7 @@ import { useAlive } from '../../../hooks/useAlive';
 | 
			
		|||
import { StateEvent } from '../../../../types/matrix/room';
 | 
			
		||||
import { RoomPermissionsAPI } from '../../../hooks/useRoomPermissions';
 | 
			
		||||
import { getMxIdServer } from '../../../utils/matrix';
 | 
			
		||||
import { CollapsibleCard } from '../../../components/CollapsibleCard';
 | 
			
		||||
 | 
			
		||||
type RoomPublishedAddressesProps = {
 | 
			
		||||
  permissions: RoomPermissionsAPI;
 | 
			
		||||
| 
						 | 
				
			
			@ -373,64 +372,40 @@ export function RoomLocalAddresses({ permissions }: { permissions: RoomPermissio
 | 
			
		|||
  const { localAliasesState, addLocalAlias, removeLocalAlias } = useLocalAliases(room.roomId);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <SequenceCard
 | 
			
		||||
      className={SequenceCardStyle}
 | 
			
		||||
      variant="SurfaceVariant"
 | 
			
		||||
      direction="Column"
 | 
			
		||||
      gap="400"
 | 
			
		||||
    <CollapsibleCard
 | 
			
		||||
      expand={expand}
 | 
			
		||||
      setExpand={setExpand}
 | 
			
		||||
      title="Local Addresses"
 | 
			
		||||
      description="Set local address so users can join through your homeserver."
 | 
			
		||||
    >
 | 
			
		||||
      <SettingTile
 | 
			
		||||
        title="Local Addresses"
 | 
			
		||||
        description="Set local address so users can join through your homeserver."
 | 
			
		||||
        after={
 | 
			
		||||
          <Button
 | 
			
		||||
            type="button"
 | 
			
		||||
            onClick={() => setExpand(!expand)}
 | 
			
		||||
            size="300"
 | 
			
		||||
            variant="Secondary"
 | 
			
		||||
            fill="Soft"
 | 
			
		||||
            outlined
 | 
			
		||||
            radii="300"
 | 
			
		||||
            before={
 | 
			
		||||
              <Icon size="100" src={expand ? Icons.ChevronTop : Icons.ChevronBottom} filled />
 | 
			
		||||
            }
 | 
			
		||||
          >
 | 
			
		||||
            <Text as="span" size="B300" truncate>
 | 
			
		||||
              {expand ? 'Collapse' : 'Expand'}
 | 
			
		||||
      <CutoutCard variant="Surface" style={{ padding: config.space.S300 }}>
 | 
			
		||||
        {localAliasesState.status === AsyncStatus.Loading && (
 | 
			
		||||
          <Box gap="100">
 | 
			
		||||
            <Spinner variant="Secondary" size="100" />
 | 
			
		||||
            <Text size="T200">Loading...</Text>
 | 
			
		||||
          </Box>
 | 
			
		||||
        )}
 | 
			
		||||
        {localAliasesState.status === AsyncStatus.Success &&
 | 
			
		||||
          (localAliasesState.data.length === 0 ? (
 | 
			
		||||
            <Box direction="Column" gap="100">
 | 
			
		||||
              <Text size="L400">No Addresses</Text>
 | 
			
		||||
            </Box>
 | 
			
		||||
          ) : (
 | 
			
		||||
            <LocalAddressesList
 | 
			
		||||
              localAliases={localAliasesState.data}
 | 
			
		||||
              removeLocalAlias={removeLocalAlias}
 | 
			
		||||
              canEditCanonical={canEditCanonical}
 | 
			
		||||
            />
 | 
			
		||||
          ))}
 | 
			
		||||
        {localAliasesState.status === AsyncStatus.Error && (
 | 
			
		||||
          <Box gap="100">
 | 
			
		||||
            <Text size="T200" style={{ color: color.Critical.Main }}>
 | 
			
		||||
              {localAliasesState.error.message}
 | 
			
		||||
            </Text>
 | 
			
		||||
          </Button>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {expand && (
 | 
			
		||||
        <CutoutCard variant="Surface" style={{ padding: config.space.S300 }}>
 | 
			
		||||
          {localAliasesState.status === AsyncStatus.Loading && (
 | 
			
		||||
            <Box gap="100">
 | 
			
		||||
              <Spinner variant="Secondary" size="100" />
 | 
			
		||||
              <Text size="T200">Loading...</Text>
 | 
			
		||||
            </Box>
 | 
			
		||||
          )}
 | 
			
		||||
          {localAliasesState.status === AsyncStatus.Success &&
 | 
			
		||||
            (localAliasesState.data.length === 0 ? (
 | 
			
		||||
              <Box direction="Column" gap="100">
 | 
			
		||||
                <Text size="L400">No Addresses</Text>
 | 
			
		||||
              </Box>
 | 
			
		||||
            ) : (
 | 
			
		||||
              <LocalAddressesList
 | 
			
		||||
                localAliases={localAliasesState.data}
 | 
			
		||||
                removeLocalAlias={removeLocalAlias}
 | 
			
		||||
                canEditCanonical={canEditCanonical}
 | 
			
		||||
              />
 | 
			
		||||
            ))}
 | 
			
		||||
          {localAliasesState.status === AsyncStatus.Error && (
 | 
			
		||||
            <Box gap="100">
 | 
			
		||||
              <Text size="T200" style={{ color: color.Critical.Main }}>
 | 
			
		||||
                {localAliasesState.error.message}
 | 
			
		||||
              </Text>
 | 
			
		||||
            </Box>
 | 
			
		||||
          )}
 | 
			
		||||
        </CutoutCard>
 | 
			
		||||
      )}
 | 
			
		||||
          </Box>
 | 
			
		||||
        )}
 | 
			
		||||
      </CutoutCard>
 | 
			
		||||
      {expand && <LocalAddressInput addLocalAlias={addLocalAlias} />}
 | 
			
		||||
    </SequenceCard>
 | 
			
		||||
    </CollapsibleCard>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,86 +1,54 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import { Box, Text, Icon, Icons, Button, MenuItem } from 'folds';
 | 
			
		||||
import { SequenceCard } from '../../../components/sequence-card';
 | 
			
		||||
import { SequenceCardStyle } from '../styles.css';
 | 
			
		||||
import { SettingTile } from '../../../components/setting-tile';
 | 
			
		||||
import { Box, Text, Icon, Icons, MenuItem } from 'folds';
 | 
			
		||||
import { CutoutCard } from '../../../components/cutout-card';
 | 
			
		||||
 | 
			
		||||
type AccountDataListProps = {
 | 
			
		||||
  title?: string;
 | 
			
		||||
  description?: string;
 | 
			
		||||
  expand: boolean;
 | 
			
		||||
  setExpand: (expand: boolean) => void;
 | 
			
		||||
  types: string[];
 | 
			
		||||
  onSelect: (type: string | null) => void;
 | 
			
		||||
};
 | 
			
		||||
export function AccountDataList({ types, onSelect, expand, setExpand, title, description }: AccountDataListProps) {
 | 
			
		||||
export function AccountDataList({
 | 
			
		||||
  types,
 | 
			
		||||
  onSelect,
 | 
			
		||||
}: AccountDataListProps) {
 | 
			
		||||
  return (
 | 
			
		||||
    <SequenceCard
 | 
			
		||||
      className={SequenceCardStyle}
 | 
			
		||||
      variant="SurfaceVariant"
 | 
			
		||||
      direction="Column"
 | 
			
		||||
      gap="400"
 | 
			
		||||
    >
 | 
			
		||||
      <SettingTile
 | 
			
		||||
        title={title}
 | 
			
		||||
        description={description}
 | 
			
		||||
        after={
 | 
			
		||||
          <Button
 | 
			
		||||
            onClick={() => setExpand(!expand)}
 | 
			
		||||
            variant="Secondary"
 | 
			
		||||
            fill="Soft"
 | 
			
		||||
            size="300"
 | 
			
		||||
            radii="300"
 | 
			
		||||
            outlined
 | 
			
		||||
            before={
 | 
			
		||||
              <Icon src={expand ? Icons.ChevronTop : Icons.ChevronBottom} size="100" filled />
 | 
			
		||||
            }
 | 
			
		||||
          >
 | 
			
		||||
            <Text size="B300">{expand ? 'Collapse' : 'Expand'}</Text>
 | 
			
		||||
          </Button>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {expand && (
 | 
			
		||||
        <Box direction="Column" gap="100">
 | 
			
		||||
          <Box justifyContent="SpaceBetween">
 | 
			
		||||
            <Text size="L400">Fields</Text>
 | 
			
		||||
            <Text size="L400">Total: {types.length}</Text>
 | 
			
		||||
    <Box direction="Column" gap="100">
 | 
			
		||||
      <Box justifyContent="SpaceBetween">
 | 
			
		||||
        <Text size="L400">Fields</Text>
 | 
			
		||||
        <Text size="L400">Total: {types.length}</Text>
 | 
			
		||||
      </Box>
 | 
			
		||||
      <CutoutCard>
 | 
			
		||||
        <MenuItem
 | 
			
		||||
          variant="Surface"
 | 
			
		||||
          fill="None"
 | 
			
		||||
          size="300"
 | 
			
		||||
          radii="0"
 | 
			
		||||
          before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
          onClick={() => onSelect(null)}
 | 
			
		||||
        >
 | 
			
		||||
          <Box grow="Yes">
 | 
			
		||||
            <Text size="T200" truncate>
 | 
			
		||||
              Add New
 | 
			
		||||
            </Text>
 | 
			
		||||
          </Box>
 | 
			
		||||
          <CutoutCard>
 | 
			
		||||
            <MenuItem
 | 
			
		||||
              variant="Surface"
 | 
			
		||||
              fill="None"
 | 
			
		||||
              size="300"
 | 
			
		||||
              radii="0"
 | 
			
		||||
              before={<Icon size="50" src={Icons.Plus} />}
 | 
			
		||||
              onClick={() => onSelect(null)}
 | 
			
		||||
            >
 | 
			
		||||
              <Box grow="Yes">
 | 
			
		||||
                <Text size="T200" truncate>
 | 
			
		||||
                  Add New
 | 
			
		||||
                </Text>
 | 
			
		||||
              </Box>
 | 
			
		||||
            </MenuItem>
 | 
			
		||||
            {types.sort().map((type) => (
 | 
			
		||||
              <MenuItem
 | 
			
		||||
                key={type}
 | 
			
		||||
                variant="Surface"
 | 
			
		||||
                fill="None"
 | 
			
		||||
                size="300"
 | 
			
		||||
                radii="0"
 | 
			
		||||
                after={<Icon size="50" src={Icons.ChevronRight} />}
 | 
			
		||||
                onClick={() => onSelect(type)}
 | 
			
		||||
              >
 | 
			
		||||
                <Box grow="Yes">
 | 
			
		||||
                  <Text size="T200" truncate>
 | 
			
		||||
                    {type}
 | 
			
		||||
                  </Text>
 | 
			
		||||
                </Box>
 | 
			
		||||
              </MenuItem>
 | 
			
		||||
            ))}
 | 
			
		||||
          </CutoutCard>
 | 
			
		||||
        </Box>
 | 
			
		||||
      )}
 | 
			
		||||
    </SequenceCard>
 | 
			
		||||
        </MenuItem>
 | 
			
		||||
        {types.sort().map((type) => (
 | 
			
		||||
          <MenuItem
 | 
			
		||||
            key={type}
 | 
			
		||||
            variant="Surface"
 | 
			
		||||
            fill="None"
 | 
			
		||||
            size="300"
 | 
			
		||||
            radii="0"
 | 
			
		||||
            after={<Icon size="50" src={Icons.ChevronRight} />}
 | 
			
		||||
            onClick={() => onSelect(type)}
 | 
			
		||||
          >
 | 
			
		||||
            <Box grow="Yes">
 | 
			
		||||
              <Text size="T200" truncate>
 | 
			
		||||
                {type}
 | 
			
		||||
              </Text>
 | 
			
		||||
            </Box>
 | 
			
		||||
          </MenuItem>
 | 
			
		||||
        ))}
 | 
			
		||||
      </CutoutCard>
 | 
			
		||||
    </Box>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ import { copyToClipboard } from '../../../utils/dom';
 | 
			
		|||
import { AccountDataList } from './AccountDataList';
 | 
			
		||||
import { useExtendedProfile } from '../../../hooks/useExtendedProfile';
 | 
			
		||||
import { useAccountDataCallback } from '../../../hooks/useAccountDataCallback';
 | 
			
		||||
import { CollapsibleCard } from '../../../components/CollapsibleCard';
 | 
			
		||||
 | 
			
		||||
type DeveloperToolsPage =
 | 
			
		||||
  | { name: 'index' }
 | 
			
		||||
| 
						 | 
				
			
			@ -165,23 +166,29 @@ export function DeveloperTools({ requestClose }: DeveloperToolsProps) {
 | 
			
		|||
                  {developerTools && (
 | 
			
		||||
                    <Box direction="Column" gap="100">
 | 
			
		||||
                      <Text size="L400">Account Data</Text>
 | 
			
		||||
                      <AccountDataList
 | 
			
		||||
                        title="Account"
 | 
			
		||||
                        description="Private data stored in your account."
 | 
			
		||||
                      <CollapsibleCard
 | 
			
		||||
                        expand={globalExpand}
 | 
			
		||||
                        setExpand={setGlobalExpand}
 | 
			
		||||
                        types={accountDataTypes}
 | 
			
		||||
                        onSelect={(type) => setPage({ name: 'account-data', type })}
 | 
			
		||||
                      />
 | 
			
		||||
                      {extendedProfile && (
 | 
			
		||||
                        title="Account"
 | 
			
		||||
                        description="Private data stored in your account."
 | 
			
		||||
                      >
 | 
			
		||||
                        <AccountDataList
 | 
			
		||||
                          title="Profile"
 | 
			
		||||
                          description="Public data attached to your Matrix profile."
 | 
			
		||||
                          types={accountDataTypes}
 | 
			
		||||
                          onSelect={(type) => setPage({ name: 'account-data', type })}
 | 
			
		||||
                        />
 | 
			
		||||
                      </CollapsibleCard>
 | 
			
		||||
                      {extendedProfile && (
 | 
			
		||||
                        <CollapsibleCard
 | 
			
		||||
                          expand={profileExpand}
 | 
			
		||||
                          setExpand={setProfileExpand}
 | 
			
		||||
                          types={Object.keys(extendedProfile)}
 | 
			
		||||
                          onSelect={(type) => setPage({ name: 'profile-field', type })}
 | 
			
		||||
                        />
 | 
			
		||||
                          title="Profile"
 | 
			
		||||
                          description="Public data attached to your Matrix profile."
 | 
			
		||||
                        >
 | 
			
		||||
                          <AccountDataList
 | 
			
		||||
                            types={Object.keys(extendedProfile)}
 | 
			
		||||
                            onSelect={(type) => setPage({ name: 'profile-field', type })}
 | 
			
		||||
                          />
 | 
			
		||||
                        </CollapsibleCard>
 | 
			
		||||
                      )}
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  )}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,7 @@ import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
 | 
			
		|||
import { decryptMegolmKeyFile, encryptMegolmKeyFile } from '../../../../util/cryptE2ERoomKeys';
 | 
			
		||||
import { useAlive } from '../../../hooks/useAlive';
 | 
			
		||||
import { useFilePicker } from '../../../hooks/useFilePicker';
 | 
			
		||||
import { CollapsibleCard } from '../../../components/CollapsibleCard';
 | 
			
		||||
 | 
			
		||||
function ExportKeys() {
 | 
			
		||||
  const mx = useMatrixClient();
 | 
			
		||||
| 
						 | 
				
			
			@ -121,37 +122,18 @@ function ExportKeys() {
 | 
			
		|||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ExportKeysTile() {
 | 
			
		||||
function ExportKeysCard() {
 | 
			
		||||
  const [expand, setExpand] = useState(false);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <SettingTile
 | 
			
		||||
        title="Export Messages Data"
 | 
			
		||||
        description="Save password protected copy of encryption data on your device to decrypt messages later."
 | 
			
		||||
        after={
 | 
			
		||||
          <Box>
 | 
			
		||||
            <Button
 | 
			
		||||
              type="button"
 | 
			
		||||
              onClick={() => setExpand(!expand)}
 | 
			
		||||
              size="300"
 | 
			
		||||
              variant="Secondary"
 | 
			
		||||
              fill="Soft"
 | 
			
		||||
              outlined
 | 
			
		||||
              radii="300"
 | 
			
		||||
              before={
 | 
			
		||||
                <Icon size="100" src={expand ? Icons.ChevronTop : Icons.ChevronBottom} filled />
 | 
			
		||||
              }
 | 
			
		||||
            >
 | 
			
		||||
              <Text as="span" size="B300" truncate>
 | 
			
		||||
                {expand ? 'Collapse' : 'Expand'}
 | 
			
		||||
              </Text>
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Box>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {expand && <ExportKeys />}
 | 
			
		||||
    </>
 | 
			
		||||
    <CollapsibleCard
 | 
			
		||||
      expand={expand}
 | 
			
		||||
      setExpand={setExpand}
 | 
			
		||||
      title="Export Messages Data"
 | 
			
		||||
      description="Save password protected copy of encryption data on your device to decrypt messages later."
 | 
			
		||||
    >
 | 
			
		||||
      <ExportKeys />
 | 
			
		||||
    </CollapsibleCard>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -304,14 +286,7 @@ export function LocalBackup() {
 | 
			
		|||
  return (
 | 
			
		||||
    <Box direction="Column" gap="100">
 | 
			
		||||
      <Text size="L400">Local Backup</Text>
 | 
			
		||||
      <SequenceCard
 | 
			
		||||
        className={SequenceCardStyle}
 | 
			
		||||
        variant="SurfaceVariant"
 | 
			
		||||
        direction="Column"
 | 
			
		||||
        gap="400"
 | 
			
		||||
      >
 | 
			
		||||
        <ExportKeysTile />
 | 
			
		||||
      </SequenceCard>
 | 
			
		||||
      <ExportKeysCard />
 | 
			
		||||
      <SequenceCard
 | 
			
		||||
        className={SequenceCardStyle}
 | 
			
		||||
        variant="SurfaceVariant"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue