Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .changeset/feat_add_allow_arbitrary_animal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: minor
---

Change animal identity to allow arbitrary animals
11 changes: 7 additions & 4 deletions src/app/components/user-profile/UserRoomProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,17 @@ function UserExtendedSection({
const [renderAnimals] = useSetting(settingsAtom, 'renderAnimals');
const isCat = profile.isCat === true;
const hasCats = profile.hasCats === true;
const isAnimal = profile.isAnimal ?? (isCat && 'cat');
const hasAnimal = profile.hasAnimal ?? (hasCats && 'cats');
const animalNeed = profile.animalNeed ?? 'headpats';

const catStatusText = useMemo(() => {
if (!renderAnimals) return null;
if (isCat && hasCats) return 'Cat with cats—needs pets & love!';
if (isCat) return 'Is a cat—give pets & love!';
if (hasCats) return 'Has cats—send love!';
if (isAnimal && hasAnimal) return `${isAnimal} with ${hasAnimal}, give ${animalNeed}!`;
if (isAnimal) return `Is ${isAnimal}, give ${animalNeed}!`;
if (hasAnimal) return `Has ${hasAnimal}, give ${animalNeed}!`;
return null;
}, [renderAnimals, isCat, hasCats]);
}, [renderAnimals, isAnimal, hasAnimal, animalNeed]);

const languageFilterEnabled = getSettings().filterPronounsBasedOnLanguage ?? false;
const languagesToFilterFor = getSettings().filterPronounsLanguages ?? ['en'];
Expand Down
172 changes: 145 additions & 27 deletions src/app/features/settings/account/AnimalCosmetics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,67 @@ import type { UserProfile } from '$hooks/useUserProfile';
import { useSetting } from '$state/hooks/settings';
import { settingsAtom } from '$state/settings';
import { profilesCacheAtom } from '$state/userRoomProfile';
import { Box, Switch, Text } from 'folds';
import { Box, IconButton, Input, Switch, Text } from 'folds';
import { useSetAtom } from 'jotai';
import { useCallback } from 'react';
import { useCallback, useEffect, useState, type ChangeEvent } from 'react';
import { SequenceCardStyle } from '../styles.css';
import * as prefix from '$unstable/prefixes';
import { menuIcon, X } from '$components/icons/phosphor';

type inputProps = {
onSave: (newValue: unknown) => void;
onReset?: () => void;
initialValue?: string;
disabled?: boolean;
placeholder?: string;
};

function FreeInput({ initialValue: current, onSave, onReset, disabled, placeholder }: inputProps) {
const [val, setVal] = useState(current);

useEffect(() => setVal(current), [current]);

const handleSave = () => {
if (val === current) return;
onSave(val);
};

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
setVal(e.currentTarget.value);
};

return (
<>
<Input
value={val}
size="300"
radii="300"
disabled={disabled ?? false}
variant="Secondary"
placeholder={placeholder ?? 'Input...'}
onChange={handleChange}
onBlur={handleSave}
onKeyDown={(e) => e.key === 'Enter' && handleSave()}
style={{ width: '232px' }}
/>
<Box gap="0">
{onReset && (
<IconButton
size="300"
variant="Critical"
fill="None"
onClick={onReset}
radii="300"
title="Reset"
disabled={disabled}
>
{menuIcon(X)}
</IconButton>
)}
</Box>
</>
);
}

type AnimalCosmeticsProps = {
profile: UserProfile;
Expand All @@ -20,15 +76,12 @@ export function AnimalCosmetics({ profile, userId }: Readonly<AnimalCosmeticsPro
const setGlobalProfiles = useSetAtom(profilesCacheAtom);
const [renderAnimals, setRenderAnimals] = useSetting(settingsAtom, 'renderAnimals');

const isCat =
profile.isCat ||
profile.extended?.[prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_CAT_PROPERTY_NAME] === true;
const hasCats =
profile.hasCats ||
profile.extended?.[prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_CAT_PROPERTY_NAME] === true;
const isAnimal = profile.isAnimal;
const hasAnimal = profile.hasAnimal;
const animalNeed = profile.animalNeed;

const handleSaveField = useCallback(
async (key: string, value: boolean) => {
async (key: string, value: unknown) => {
await mx.setExtendedProfileProperty?.(key, value);
setGlobalProfiles((prev) => {
const newCache = { ...prev };
Expand All @@ -38,6 +91,34 @@ export function AnimalCosmetics({ profile, userId }: Readonly<AnimalCosmeticsPro
},
[mx, userId, setGlobalProfiles]
);
// this is for backwards compatibility, whenever someone will see this again, a long time from now, this will be safe to remove alongside the parent key
useEffect(() => {
const asyncClean = async () => {
const isCat = profile.isCat;
if (typeof isCat === 'boolean') {
await handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_CAT_PROPERTY_NAME,
null
);
await handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_ANIMAL_PROPERTY_NAME,
'cat'
);
}
const hasCats = profile.hasCats;
if (typeof hasCats === 'boolean') {
await handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_CAT_PROPERTY_NAME,
null
);
await handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_ANIMAL_PROPERTY_NAME,
'cats'
);
}
};
asyncClean();
}, [handleSaveField, profile.hasCats, profile.isCat]);

return (
<Box direction="Column" gap="100">
Expand All @@ -46,44 +127,81 @@ export function AnimalCosmetics({ profile, userId }: Readonly<AnimalCosmeticsPro
<SettingTile
title="Render Animals"
focusId="render-animals"
description="Render animals as animals as opposed to normal humans."
description="Render animals statuses."
after={<Switch variant="Primary" value={renderAnimals} onChange={setRenderAnimals} />}
/>
</SequenceCard>
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
<SettingTile
title="Is Cat"
focusId="is-cat"
description="Marks you as a cat."
title="Is animal"
focusId="is-animal"
description="Marks which animals you are."
after={
<Switch
variant="Primary"
value={isCat}
onChange={() =>
<FreeInput
initialValue={isAnimal}
onSave={(newValue) =>
handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_CAT_PROPERTY_NAME,
!isCat
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_ANIMAL_PROPERTY_NAME,
newValue
)
}
onReset={() =>
handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_ANIMAL_PROPERTY_NAME,
null
)
}
placeholder="bunny..."
/>
}
/>
</SequenceCard>
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
<SettingTile
title="Has Cats"
title="Has animals"
focusId="has-cats"
description="Marks that you have cats."
description="Marks which animals you have"
after={
<Switch
variant="Primary"
value={hasCats}
onChange={() =>
<FreeInput
initialValue={hasAnimal}
onSave={(newValue) =>
handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_ANIMAL_PROPERTY_NAME,
newValue
)
}
onReset={() =>
handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_ANIMAL_PROPERTY_NAME,
null
)
}
placeholder="sables..."
/>
}
/>
</SequenceCard>
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
<SettingTile
title="Requires"
focusId="animal-requires"
description="What do you need 🥺"
after={
<FreeInput
initialValue={animalNeed}
onSave={(newValue) =>
handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_ANIMAL_NEED_PROPERTY_NAME,
newValue
)
}
onReset={() =>
handleSaveField(
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_CAT_PROPERTY_NAME,
!hasCats
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_ANIMAL_NEED_PROPERTY_NAME,
null
)
}
placeholder="hugs..."
/>
}
/>
Expand Down
5 changes: 3 additions & 2 deletions src/app/features/settings/settingsLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ const settingsLinkFocusIdsBySection: Record<SettingsSectionId, readonly string[]
'blocked-users',
'display-name',
'email-address',
'has-cats',
'is-cat',
'has-animal',
'is-animal',
'animal-requires',
'user-hero-color',
'matrix-id',
'name-color',
Expand Down
21 changes: 19 additions & 2 deletions src/app/hooks/useUserProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export type UserProfile = {
heroColorScheme?: Record<string, string>;
isCat?: boolean;
hasCats?: boolean;
isAnimal?: string;
hasAnimal?: string;
animalNeed?: string;
extended?: Record<string, unknown>;
_fetched?: boolean;
};
Expand All @@ -61,6 +64,9 @@ const normalizeInfo = (info: Record<string, unknown>): UserProfile => {
prefix.MATRIX_COMMET_UNSTABLE_PROFILE_COLOR_SCHEME_PROPERTY_NAME,
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_CAT_PROPERTY_NAME,
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_CAT_PROPERTY_NAME,
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_ANIMAL_PROPERTY_NAME,
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_ANIMAL_PROPERTY_NAME,
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_ANIMAL_NEED_PROPERTY_NAME,
]);

const extended: Record<string, unknown> = {};
Expand Down Expand Up @@ -94,8 +100,19 @@ const normalizeInfo = (info: Record<string, unknown>): UserProfile => {
heroColorScheme: info[prefix.MATRIX_COMMET_UNSTABLE_PROFILE_COLOR_SCHEME_PROPERTY_NAME] as
| Record<string, string>
| undefined,
isCat: info[prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_CAT_PROPERTY_NAME] === true,
hasCats: info[prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_CAT_PROPERTY_NAME] === true,
isCat: info[prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_CAT_PROPERTY_NAME] as
| boolean
| undefined,
hasCats: info[prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_CAT_PROPERTY_NAME] as
| boolean
| undefined,
isAnimal: info[prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_ANIMAL_PROPERTY_NAME] as string,
hasAnimal: info[
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_ANIMAL_PROPERTY_NAME
] as string,
animalNeed: info[
prefix.MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_ANIMAL_NEED_PROPERTY_NAME
] as string,
extended,
_fetched: true,
};
Expand Down
4 changes: 4 additions & 0 deletions src/unstable/prefixes/sable/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ export const MATRIX_SABLE_UNSTABLE_NAME_COLOR_LIGHT_PROPERTY_NAME =
'moe.sable.app.name_color_light_theme';
export const MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_CAT_PROPERTY_NAME = 'kitty.meow.is_cat';
export const MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_CAT_PROPERTY_NAME = 'kitty.meow.has_cats';

export const MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_IS_ANIMAL_PROPERTY_NAME = 'pet.plz.me';
export const MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_HAS_ANIMAL_PROPERTY_NAME = 'pet.plz.my';
export const MATRIX_SABLE_UNSTABLE_ANIMAL_IDENTITY_ANIMAL_NEED_PROPERTY_NAME = 'pet.plz.gib';
Loading