mirror of
				https://github.com/cinnyapp/cinny.git
				synced 2025-11-03 22:10:29 +03:00 
			
		
		
		
	Add new join with address prompt (#2442)
	
		
			
	
		
	
	
		
	
		
			Some checks are pending
		
		
	
	
		
			
				
	
				Deploy to Netlify (dev) / Deploy to Netlify (push) Waiting to run
				
			
		
		
	
	
				
					
				
			
		
			Some checks are pending
		
		
	
	Deploy to Netlify (dev) / Deploy to Netlify (push) Waiting to run
				
			This commit is contained in:
		
							parent
							
								
									367397fdd4
								
							
						
					
					
						commit
						c5d4530947
					
				
					 4 changed files with 201 additions and 35 deletions
				
			
		
							
								
								
									
										131
									
								
								src/app/components/join-address-prompt/JoinAddressPrompt.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/app/components/join-address-prompt/JoinAddressPrompt.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,131 @@
 | 
			
		|||
import React, { FormEventHandler, useState } from 'react';
 | 
			
		||||
import FocusTrap from 'focus-trap-react';
 | 
			
		||||
import {
 | 
			
		||||
  Dialog,
 | 
			
		||||
  Overlay,
 | 
			
		||||
  OverlayCenter,
 | 
			
		||||
  OverlayBackdrop,
 | 
			
		||||
  Header,
 | 
			
		||||
  config,
 | 
			
		||||
  Box,
 | 
			
		||||
  Text,
 | 
			
		||||
  IconButton,
 | 
			
		||||
  Icon,
 | 
			
		||||
  Icons,
 | 
			
		||||
  Button,
 | 
			
		||||
  Input,
 | 
			
		||||
  color,
 | 
			
		||||
} from 'folds';
 | 
			
		||||
import { stopPropagation } from '../../utils/keyboard';
 | 
			
		||||
import { isRoomAlias, isRoomId } from '../../utils/matrix';
 | 
			
		||||
import { parseMatrixToRoom, parseMatrixToRoomEvent, testMatrixTo } from '../../plugins/matrix-to';
 | 
			
		||||
import { tryDecodeURIComponent } from '../../utils/dom';
 | 
			
		||||
 | 
			
		||||
type JoinAddressProps = {
 | 
			
		||||
  onOpen: (roomIdOrAlias: string, via?: string[], eventId?: string) => void;
 | 
			
		||||
  onCancel: () => void;
 | 
			
		||||
};
 | 
			
		||||
export function JoinAddressPrompt({ onOpen, onCancel }: JoinAddressProps) {
 | 
			
		||||
  const [invalid, setInvalid] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const handleSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
 | 
			
		||||
    evt.preventDefault();
 | 
			
		||||
    setInvalid(false);
 | 
			
		||||
 | 
			
		||||
    const target = evt.target as HTMLFormElement | undefined;
 | 
			
		||||
    const addressInput = target?.addressInput as HTMLInputElement | undefined;
 | 
			
		||||
    const address = addressInput?.value.trim();
 | 
			
		||||
    if (!address) return;
 | 
			
		||||
 | 
			
		||||
    if (isRoomId(address) || isRoomAlias(address)) {
 | 
			
		||||
      onOpen(address);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (testMatrixTo(address)) {
 | 
			
		||||
      const decodedAddress = tryDecodeURIComponent(address);
 | 
			
		||||
      const toRoom = parseMatrixToRoom(decodedAddress);
 | 
			
		||||
      if (toRoom) {
 | 
			
		||||
        onOpen(toRoom.roomIdOrAlias, toRoom.viaServers);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const toEvent = parseMatrixToRoomEvent(decodedAddress);
 | 
			
		||||
      if (toEvent) {
 | 
			
		||||
        onOpen(toEvent.roomIdOrAlias, toEvent.viaServers, toEvent.eventId);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    setInvalid(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Overlay open backdrop={<OverlayBackdrop />}>
 | 
			
		||||
      <OverlayCenter>
 | 
			
		||||
        <FocusTrap
 | 
			
		||||
          focusTrapOptions={{
 | 
			
		||||
            initialFocus: false,
 | 
			
		||||
            onDeactivate: onCancel,
 | 
			
		||||
            clickOutsideDeactivates: true,
 | 
			
		||||
            escapeDeactivates: stopPropagation,
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Dialog variant="Surface">
 | 
			
		||||
            <Header
 | 
			
		||||
              style={{
 | 
			
		||||
                padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
 | 
			
		||||
              }}
 | 
			
		||||
              variant="Surface"
 | 
			
		||||
              size="500"
 | 
			
		||||
            >
 | 
			
		||||
              <Box grow="Yes">
 | 
			
		||||
                <Text size="H4">Join with Address</Text>
 | 
			
		||||
              </Box>
 | 
			
		||||
              <IconButton size="300" onClick={onCancel} radii="300">
 | 
			
		||||
                <Icon src={Icons.Cross} />
 | 
			
		||||
              </IconButton>
 | 
			
		||||
            </Header>
 | 
			
		||||
            <Box
 | 
			
		||||
              as="form"
 | 
			
		||||
              onSubmit={handleSubmit}
 | 
			
		||||
              style={{ padding: config.space.S400, paddingTop: 0 }}
 | 
			
		||||
              direction="Column"
 | 
			
		||||
              gap="400"
 | 
			
		||||
            >
 | 
			
		||||
              <Box direction="Column" gap="200">
 | 
			
		||||
                <Text priority="400" size="T300">
 | 
			
		||||
                  Enter public address to join the community. Addresses looks like:
 | 
			
		||||
                </Text>
 | 
			
		||||
                <Text as="ul" size="T200" priority="300" style={{ paddingLeft: config.space.S400 }}>
 | 
			
		||||
                  <li>#community:server</li>
 | 
			
		||||
                  <li>https://matrix.to/#/#community:server</li>
 | 
			
		||||
                  <li>https://matrix.to/#/!xYzAj?via=server</li>
 | 
			
		||||
                </Text>
 | 
			
		||||
              </Box>
 | 
			
		||||
              <Box direction="Column" gap="100">
 | 
			
		||||
                <Text size="L400">Address</Text>
 | 
			
		||||
                <Input
 | 
			
		||||
                  size="500"
 | 
			
		||||
                  autoFocus
 | 
			
		||||
                  name="addressInput"
 | 
			
		||||
                  variant="Background"
 | 
			
		||||
                  placeholder="#community:server"
 | 
			
		||||
                  required
 | 
			
		||||
                />
 | 
			
		||||
                {invalid && (
 | 
			
		||||
                  <Text size="T200" style={{ color: color.Critical.Main }}>
 | 
			
		||||
                    <b>Invalid Address</b>
 | 
			
		||||
                  </Text>
 | 
			
		||||
                )}
 | 
			
		||||
              </Box>
 | 
			
		||||
              <Button type="submit" variant="Primary">
 | 
			
		||||
                <Text size="B400">Open</Text>
 | 
			
		||||
              </Button>
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Dialog>
 | 
			
		||||
        </FocusTrap>
 | 
			
		||||
      </OverlayCenter>
 | 
			
		||||
    </Overlay>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								src/app/components/join-address-prompt/index.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/app/components/join-address-prompt/index.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
export * from './JoinAddressPrompt';
 | 
			
		||||
| 
						 | 
				
			
			@ -30,10 +30,12 @@ import {
 | 
			
		|||
  NavLink,
 | 
			
		||||
} from '../../../components/nav';
 | 
			
		||||
import {
 | 
			
		||||
  encodeSearchParamValueArray,
 | 
			
		||||
  getExplorePath,
 | 
			
		||||
  getHomeCreatePath,
 | 
			
		||||
  getHomeRoomPath,
 | 
			
		||||
  getHomeSearchPath,
 | 
			
		||||
  withSearchParam,
 | 
			
		||||
} from '../../pathUtils';
 | 
			
		||||
import { getCanonicalAliasOrRoomId } from '../../../utils/matrix';
 | 
			
		||||
import { useSelectedRoom } from '../../../hooks/router/useSelectedRoom';
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +51,6 @@ import { makeNavCategoryId } from '../../../state/closedNavCategories';
 | 
			
		|||
import { roomToUnreadAtom } from '../../../state/room/roomToUnread';
 | 
			
		||||
import { useCategoryHandler } from '../../../hooks/useCategoryHandler';
 | 
			
		||||
import { useNavToActivePathMapper } from '../../../hooks/useNavToActivePathMapper';
 | 
			
		||||
import { openJoinAlias } from '../../../../client/action/navigation';
 | 
			
		||||
import { PageNav, PageNavHeader, PageNavContent } from '../../../components/page';
 | 
			
		||||
import { useRoomsUnread } from '../../../state/hooks/unread';
 | 
			
		||||
import { markAsRead } from '../../../../client/action/notifications';
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +62,9 @@ import {
 | 
			
		|||
  getRoomNotificationMode,
 | 
			
		||||
  useRoomsNotificationPreferencesContext,
 | 
			
		||||
} from '../../../hooks/useRoomsNotificationPreferences';
 | 
			
		||||
import { UseStateProvider } from '../../../components/UseStateProvider';
 | 
			
		||||
import { JoinAddressPrompt } from '../../../components/join-address-prompt';
 | 
			
		||||
import { _RoomSearchParams } from '../../paths';
 | 
			
		||||
 | 
			
		||||
type HomeMenuProps = {
 | 
			
		||||
  requestClose: () => void;
 | 
			
		||||
| 
						 | 
				
			
			@ -77,11 +81,6 @@ const HomeMenu = forwardRef<HTMLDivElement, HomeMenuProps>(({ requestClose }, re
 | 
			
		|||
    requestClose();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleJoinAddress = () => {
 | 
			
		||||
    openJoinAlias();
 | 
			
		||||
    requestClose();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Menu ref={ref} style={{ maxWidth: toRem(160), width: '100vw' }}>
 | 
			
		||||
      <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
 | 
			
		||||
| 
						 | 
				
			
			@ -96,16 +95,6 @@ const HomeMenu = forwardRef<HTMLDivElement, HomeMenuProps>(({ requestClose }, re
 | 
			
		|||
            Mark as Read
 | 
			
		||||
          </Text>
 | 
			
		||||
        </MenuItem>
 | 
			
		||||
        <MenuItem
 | 
			
		||||
          onClick={handleJoinAddress}
 | 
			
		||||
          size="300"
 | 
			
		||||
          radii="300"
 | 
			
		||||
          after={<Icon size="100" src={Icons.Link} />}
 | 
			
		||||
        >
 | 
			
		||||
          <Text style={{ flexGrow: 1 }} as="span" size="T300" truncate>
 | 
			
		||||
            Join with Address
 | 
			
		||||
          </Text>
 | 
			
		||||
        </MenuItem>
 | 
			
		||||
      </Box>
 | 
			
		||||
    </Menu>
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			@ -268,22 +257,44 @@ export function Home() {
 | 
			
		|||
                  </NavItemContent>
 | 
			
		||||
                </NavButton>
 | 
			
		||||
              </NavItem>
 | 
			
		||||
              <NavItem variant="Background" radii="400">
 | 
			
		||||
                <NavButton onClick={() => openJoinAlias()}>
 | 
			
		||||
                  <NavItemContent>
 | 
			
		||||
                    <Box as="span" grow="Yes" alignItems="Center" gap="200">
 | 
			
		||||
                      <Avatar size="200" radii="400">
 | 
			
		||||
                        <Icon src={Icons.Link} size="100" />
 | 
			
		||||
                      </Avatar>
 | 
			
		||||
                      <Box as="span" grow="Yes">
 | 
			
		||||
                        <Text as="span" size="Inherit" truncate>
 | 
			
		||||
                          Join with Address
 | 
			
		||||
                        </Text>
 | 
			
		||||
                      </Box>
 | 
			
		||||
                    </Box>
 | 
			
		||||
                  </NavItemContent>
 | 
			
		||||
                </NavButton>
 | 
			
		||||
              </NavItem>
 | 
			
		||||
              <UseStateProvider initial={false}>
 | 
			
		||||
                {(open, setOpen) => (
 | 
			
		||||
                  <>
 | 
			
		||||
                    <NavItem variant="Background" radii="400">
 | 
			
		||||
                      <NavButton onClick={() => setOpen(true)}>
 | 
			
		||||
                        <NavItemContent>
 | 
			
		||||
                          <Box as="span" grow="Yes" alignItems="Center" gap="200">
 | 
			
		||||
                            <Avatar size="200" radii="400">
 | 
			
		||||
                              <Icon src={Icons.Link} size="100" />
 | 
			
		||||
                            </Avatar>
 | 
			
		||||
                            <Box as="span" grow="Yes">
 | 
			
		||||
                              <Text as="span" size="Inherit" truncate>
 | 
			
		||||
                                Join with Address
 | 
			
		||||
                              </Text>
 | 
			
		||||
                            </Box>
 | 
			
		||||
                          </Box>
 | 
			
		||||
                        </NavItemContent>
 | 
			
		||||
                      </NavButton>
 | 
			
		||||
                    </NavItem>
 | 
			
		||||
                    {open && (
 | 
			
		||||
                      <JoinAddressPrompt
 | 
			
		||||
                        onCancel={() => setOpen(false)}
 | 
			
		||||
                        onOpen={(roomIdOrAlias, viaServers, eventId) => {
 | 
			
		||||
                          setOpen(false);
 | 
			
		||||
                          const path = getHomeRoomPath(roomIdOrAlias, eventId);
 | 
			
		||||
                          navigate(
 | 
			
		||||
                            viaServers
 | 
			
		||||
                              ? withSearchParam<_RoomSearchParams>(path, {
 | 
			
		||||
                                  viaServers: encodeSearchParamValueArray(viaServers),
 | 
			
		||||
                                })
 | 
			
		||||
                              : path
 | 
			
		||||
                          );
 | 
			
		||||
                        }}
 | 
			
		||||
                      />
 | 
			
		||||
                    )}
 | 
			
		||||
                  </>
 | 
			
		||||
                )}
 | 
			
		||||
              </UseStateProvider>
 | 
			
		||||
              <NavItem variant="Background" radii="400" aria-selected={searchSelected}>
 | 
			
		||||
                <NavLink to={getHomeSearchPath()}>
 | 
			
		||||
                  <NavItemContent>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,15 +7,22 @@ import { stopPropagation } from '../../../utils/keyboard';
 | 
			
		|||
import { SequenceCard } from '../../../components/sequence-card';
 | 
			
		||||
import { SettingTile } from '../../../components/setting-tile';
 | 
			
		||||
import { ContainerColor } from '../../../styles/ContainerColor.css';
 | 
			
		||||
import { openJoinAlias } from '../../../../client/action/navigation';
 | 
			
		||||
import { getCreatePath } from '../../pathUtils';
 | 
			
		||||
import {
 | 
			
		||||
  encodeSearchParamValueArray,
 | 
			
		||||
  getCreatePath,
 | 
			
		||||
  getSpacePath,
 | 
			
		||||
  withSearchParam,
 | 
			
		||||
} from '../../pathUtils';
 | 
			
		||||
import { useCreateSelected } from '../../../hooks/router/useCreateSelected';
 | 
			
		||||
import { JoinAddressPrompt } from '../../../components/join-address-prompt';
 | 
			
		||||
import { _RoomSearchParams } from '../../paths';
 | 
			
		||||
 | 
			
		||||
export function CreateTab() {
 | 
			
		||||
  const createSelected = useCreateSelected();
 | 
			
		||||
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const [menuCords, setMenuCords] = useState<RectCords>();
 | 
			
		||||
  const [joinAddress, setJoinAddress] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
 | 
			
		||||
    setMenuCords(menuCords ? undefined : evt.currentTarget.getBoundingClientRect());
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +34,7 @@ export function CreateTab() {
 | 
			
		|||
  };
 | 
			
		||||
 | 
			
		||||
  const handleJoinWithAddress = () => {
 | 
			
		||||
    openJoinAlias();
 | 
			
		||||
    setJoinAddress(true);
 | 
			
		||||
    setMenuCords(undefined);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +110,22 @@ export function CreateTab() {
 | 
			
		|||
            >
 | 
			
		||||
              <Icon src={Icons.Plus} />
 | 
			
		||||
            </SidebarAvatar>
 | 
			
		||||
            {joinAddress && (
 | 
			
		||||
              <JoinAddressPrompt
 | 
			
		||||
                onCancel={() => setJoinAddress(false)}
 | 
			
		||||
                onOpen={(roomIdOrAlias, viaServers) => {
 | 
			
		||||
                  setJoinAddress(false);
 | 
			
		||||
                  const path = getSpacePath(roomIdOrAlias);
 | 
			
		||||
                  navigate(
 | 
			
		||||
                    viaServers
 | 
			
		||||
                      ? withSearchParam<_RoomSearchParams>(path, {
 | 
			
		||||
                          viaServers: encodeSearchParamValueArray(viaServers),
 | 
			
		||||
                        })
 | 
			
		||||
                      : path
 | 
			
		||||
                  );
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          </PopOut>
 | 
			
		||||
        )}
 | 
			
		||||
      </SidebarItemTooltip>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue