diff --git a/.changeset/clean_up_the_design_of_persona_settings.md b/.changeset/clean_up_the_design_of_persona_settings.md new file mode 100644 index 000000000..49f3e9c89 --- /dev/null +++ b/.changeset/clean_up_the_design_of_persona_settings.md @@ -0,0 +1,5 @@ +--- +default: minor +--- + +# Clean up the design of Persona settings diff --git a/src/app/features/settings/Persona/PKCompat.tsx b/src/app/features/settings/Persona/PKCompat.tsx index 8d55eaec1..dd8fb2965 100644 --- a/src/app/features/settings/Persona/PKCompat.tsx +++ b/src/app/features/settings/Persona/PKCompat.tsx @@ -31,6 +31,13 @@ export function PKCompatSettings() { /> } /> + + void; onDelete?: (profileId: string) => void; }; @@ -38,7 +39,6 @@ export function PerMessageProfileEditor({ avatarMxcUrl, displayName, pronouns = Array(), - onChange, onDelete, }: Readonly) { const useAuthentication = useMediaAuthentication(); @@ -46,8 +46,6 @@ export function PerMessageProfileEditor({ const [currentId, setCurrentId] = useState(profileId); const [newId, setNewId] = useState(profileId); - console.warn(pronouns); - // Pronouns const [currentPronouns, setCurrentPronouns] = useState(pronouns); const [newPronouns, setNewPronouns] = useState(pronouns); @@ -86,22 +84,13 @@ export function PerMessageProfileEditor({ setImageFile(undefined); setImageHasChanges(true); }, []); - const handleUploaded = useCallback( - (upload: { status: string; mxc: string }) => { - if (upload?.status === 'success') { - setAvatarMxc(upload.mxc); - if (onChange) - onChange({ - id: profileId, - name: newDisplayName, - avatarUrl: upload.mxc, - }); - setImageHasChanges(true); - } - setImageFile(undefined); - }, - [onChange, profileId, newDisplayName] - ); + const handleUploaded = useCallback((upload: { status: string; mxc: string }) => { + if (upload?.status === 'success') { + setAvatarMxc(upload.mxc); + setImageHasChanges(true); + } + setImageFile(undefined); + }, []); const handleNameChange = useCallback((e: React.ChangeEvent) => { setNewDisplayName(e.target.value); }, []); @@ -187,109 +176,62 @@ export function PerMessageProfileEditor({ return ( + Profile + - {/* Profile ID heading and input */} - - - Profile ID: - - - - - - + + + placeholder="Profile ID" + style={{ paddingRight: config.space.S200 }} + aria-label="profile id" + title="profile id" + /> + + + + + + ( - p + {nameInitials(displayName)} )} alt={`Avatar for profile ${profileId}`} /> + } + > + - {uploadAtom && ( - - - - )} - - - Display Name: - + + + )} + + + + + + - - Pronouns: - + + + + + + - - {/* Rechte Spalte: Save Button */} - - - - - + } + > + + + + + ); } diff --git a/src/app/features/settings/Persona/PerMessageProfileEditorView.tsx b/src/app/features/settings/Persona/PerMessageProfileEditorView.tsx new file mode 100644 index 000000000..c23bb712d --- /dev/null +++ b/src/app/features/settings/Persona/PerMessageProfileEditorView.tsx @@ -0,0 +1,63 @@ +import { Box, IconButton, Text, Scroll, Chip } from 'folds'; +import { ArrowLeft, composerIcon, sizedIcon, X } from '$components/icons/phosphor'; +import { Page, PageHeader, PageContent } from '$components/page'; +import type { MatrixClient } from 'matrix-js-sdk'; +import type { PronounSet } from '$utils/pronouns'; +import { PerMessageProfileEditor } from './PerMessageProfileEditor'; + +type PerMessageProfileEditorViewProps = { + mx: MatrixClient; + profileId: string; + avatarMxcUrl?: string; + displayName?: string; + pronouns?: PronounSet[]; + onChange?: (profile: { id: string; name: string; avatarUrl?: string }) => void; + onDelete?: (profileId: string) => void; + requestClose: () => void; +}; +export function PerMessageProfileEditorView({ + mx, + profileId, + avatarMxcUrl, + displayName, + pronouns = Array(), + requestClose, +}: Readonly) { + return ( + + + + + + Persona + + + + + {composerIcon(X)} + + + + + + + + + + + + + ); +} diff --git a/src/app/features/settings/Persona/PerMessageProfileListItem.tsx b/src/app/features/settings/Persona/PerMessageProfileListItem.tsx new file mode 100644 index 000000000..40fa89dc6 --- /dev/null +++ b/src/app/features/settings/Persona/PerMessageProfileListItem.tsx @@ -0,0 +1,90 @@ +import { color, Box, Text, Avatar, Chip } from 'folds'; +import type { MatrixClient } from '$types/matrix-sdk'; +import { useCallback, useMemo } from 'react'; +import { nameInitials } from '$utils/common'; +import { mxcUrlToHttp } from '$utils/matrix'; +import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; +import { UserAvatar } from '$components/user-avatar'; +import type { PronounSet } from '$utils/pronouns'; +import { SettingTile } from '$components/setting-tile'; +import { toSettingsFocusIdPart } from '../settingsLink'; +import { Pronouns } from '$features/room/message'; + +/** + * the props we use for the per-message profile list item, which is used to display a per-message profile. This is used in the settings page when the user wants to view all of their profiles. + */ +type PerMessageProfileListItemProps = { + mx: MatrixClient; + profileId: string; + avatarMxcUrl?: string; + displayName?: string; + pronouns?: PronounSet[]; + onOpenEditor: (profileId: string) => void; +}; + +export function PerMessageProfileListItem({ + mx, + profileId, + avatarMxcUrl, + displayName, + pronouns = Array(), + onOpenEditor, +}: Readonly) { + const useAuthentication = useMediaAuthentication(); + + const avatarUrl = useMemo(() => { + if (avatarMxcUrl) { + return mxcUrlToHttp(mx, avatarMxcUrl, useAuthentication, 96, 96, 'crop') ?? undefined; + } + return undefined; + }, [avatarMxcUrl, mx, useAuthentication]); + + const handleOpenEditor = useCallback(() => onOpenEditor(profileId), [profileId, onOpenEditor]); + + return ( + + {displayName ?? 'Unnamed Profile'} + + + } + description={profileId} + before={ + + ( + + + {nameInitials(displayName ?? '?')} + + + )} + alt={`Avatar for profile ${profileId}`} + /> + + } + after={ + + Edit + + } + > + ); +} diff --git a/src/app/features/settings/Persona/PerMessageProfileOverview.tsx b/src/app/features/settings/Persona/PerMessageProfileOverview.tsx index 61a2ce57e..2f6dd55a1 100644 --- a/src/app/features/settings/Persona/PerMessageProfileOverview.tsx +++ b/src/app/features/settings/Persona/PerMessageProfileOverview.tsx @@ -3,17 +3,28 @@ import type { PerMessageProfile } from '$hooks/usePerMessageProfile'; import { addOrUpdatePerMessageProfile, getAllPerMessageProfiles, + getPerMessageProfileById, } from '$hooks/usePerMessageProfile'; import { useEffect, useState } from 'react'; import { Box, Button, Text } from 'folds'; import { generateShortId } from '$utils/shortIdGen'; -import { PerMessageProfileEditor } from './PerMessageProfileEditor'; +import { SequenceCard } from '$components/sequence-card'; +import { SequenceCardStyle } from '../styles.css'; +import { PerMessageProfileListItem } from './PerMessageProfileListItem'; +import { SettingTile } from '$components/setting-tile'; +type PerMessageProfileOverviewProps = { + onCreateProfile: (profile: PerMessageProfile) => void; + onEditProfile: (profile: PerMessageProfile) => void; +}; /** * Renders a list of per-message profiles along with an editor. * @returns rendering of per message profile list including editor */ -export function PerMessageProfileOverview() { +export function PerMessageProfileOverview({ + onCreateProfile, + onEditProfile, +}: PerMessageProfileOverviewProps) { const mx = useMatrixClient(); const [profiles, setProfiles] = useState([]); @@ -25,40 +36,61 @@ export function PerMessageProfileOverview() { fetchProfiles(); }, [mx]); - // Handler to remove a profile from the list after deletion - const handleDelete = (profileId: string) => { - setProfiles((prevProfiles) => prevProfiles.filter((profile) => profile.id !== profileId)); + const handleEdit = async (profileId: string) => { + const profile = await getPerMessageProfileById(mx, profileId); + if (profile) onEditProfile(profile); }; return ( - - - Per-Message Profiles - - + + Personas + + + { + const newProfile: PerMessageProfile = { + id: generateShortId(5), + name: 'New Profile', + }; + addOrUpdatePerMessageProfile(mx, newProfile).then(() => { + onCreateProfile(newProfile); + }); + }} + > + Add + + } + /> + + {profiles.map((profile) => ( - + > + + ))} ); diff --git a/src/app/features/settings/Persona/ProfilesPage.tsx b/src/app/features/settings/Persona/ProfilesPage.tsx index 961d8af0b..a193138dc 100644 --- a/src/app/features/settings/Persona/ProfilesPage.tsx +++ b/src/app/features/settings/Persona/ProfilesPage.tsx @@ -1,9 +1,13 @@ -import { PageNavContent } from '$components/page'; -import { Box } from 'folds'; +import { PageContent } from '$components/page'; +import { Box, Scroll } from 'folds'; import { SettingsSectionPage } from '../SettingsSectionPage'; import { PerMessageProfileOverview } from './PerMessageProfileOverview'; import { PKCompatSettings } from './PKCompat'; import { PickerPageSettings } from './PickerPage'; +import type { PerMessageProfile } from '$hooks/usePerMessageProfile'; +import { useState } from 'react'; +import { useMatrixClient } from '$hooks/useMatrixClient'; +import { PerMessageProfileEditorView } from './PerMessageProfileEditorView'; type PerMessageProfilePageProps = { requestBack?: () => void; @@ -11,27 +15,41 @@ type PerMessageProfilePageProps = { }; export function PerMessageProfilePage({ requestBack, requestClose }: PerMessageProfilePageProps) { + const mx = useMatrixClient(); + const [editingProfile, setEditingProfile] = useState(); + + const handleEditorClose = () => { + setEditingProfile(undefined); + }; + + if (editingProfile) { + return ( + + ); + } return ( - - - - - - - + + + + + + + + + + + ); }