diff --git a/public/poses/aqua/amused.webp b/public/poses/aqua/amused.webp new file mode 100644 index 00000000..50ae021b Binary files /dev/null and b/public/poses/aqua/amused.webp differ diff --git a/public/poses/aqua/annoyed.webp b/public/poses/aqua/annoyed.webp new file mode 100644 index 00000000..924b789d Binary files /dev/null and b/public/poses/aqua/annoyed.webp differ diff --git a/public/poses/aqua/concerned.webp b/public/poses/aqua/concerned.webp new file mode 100644 index 00000000..09228316 Binary files /dev/null and b/public/poses/aqua/concerned.webp differ diff --git a/public/poses/aqua/conduct.webp b/public/poses/aqua/conduct.webp new file mode 100644 index 00000000..828d254f Binary files /dev/null and b/public/poses/aqua/conduct.webp differ diff --git a/public/poses/aqua/determined.webp b/public/poses/aqua/determined.webp new file mode 100644 index 00000000..c1a49115 Binary files /dev/null and b/public/poses/aqua/determined.webp differ diff --git a/public/poses/aqua/flourish.webp b/public/poses/aqua/flourish.webp new file mode 100644 index 00000000..46c6171b Binary files /dev/null and b/public/poses/aqua/flourish.webp differ diff --git a/public/poses/aqua/happy.webp b/public/poses/aqua/happy.webp new file mode 100644 index 00000000..1dcb13a2 Binary files /dev/null and b/public/poses/aqua/happy.webp differ diff --git a/public/poses/aqua/listen.webp b/public/poses/aqua/listen.webp new file mode 100644 index 00000000..b6447807 Binary files /dev/null and b/public/poses/aqua/listen.webp differ diff --git a/public/poses/aqua/neutral.webp b/public/poses/aqua/neutral.webp new file mode 100644 index 00000000..d8f03a89 Binary files /dev/null and b/public/poses/aqua/neutral.webp differ diff --git a/public/poses/aqua/patch.webp b/public/poses/aqua/patch.webp new file mode 100644 index 00000000..7c47973b Binary files /dev/null and b/public/poses/aqua/patch.webp differ diff --git a/public/poses/aqua/surprised.webp b/public/poses/aqua/surprised.webp new file mode 100644 index 00000000..bd7dbe03 Binary files /dev/null and b/public/poses/aqua/surprised.webp differ diff --git a/public/poses/aqua/thinking.webp b/public/poses/aqua/thinking.webp new file mode 100644 index 00000000..8c0bde4c Binary files /dev/null and b/public/poses/aqua/thinking.webp differ diff --git a/public/poses/eureka/amused.webp b/public/poses/eureka/amused.webp new file mode 100644 index 00000000..bbe1e9d9 Binary files /dev/null and b/public/poses/eureka/amused.webp differ diff --git a/public/poses/eureka/annoyed.webp b/public/poses/eureka/annoyed.webp new file mode 100644 index 00000000..ee832a1d Binary files /dev/null and b/public/poses/eureka/annoyed.webp differ diff --git a/public/poses/eureka/concerned.webp b/public/poses/eureka/concerned.webp new file mode 100644 index 00000000..b3c71b2b Binary files /dev/null and b/public/poses/eureka/concerned.webp differ diff --git a/public/poses/eureka/determined.webp b/public/poses/eureka/determined.webp new file mode 100644 index 00000000..547d4464 Binary files /dev/null and b/public/poses/eureka/determined.webp differ diff --git a/public/poses/eureka/happy.webp b/public/poses/eureka/happy.webp new file mode 100644 index 00000000..782a3463 Binary files /dev/null and b/public/poses/eureka/happy.webp differ diff --git a/public/poses/eureka/hood.webp b/public/poses/eureka/hood.webp new file mode 100644 index 00000000..1565d1d7 Binary files /dev/null and b/public/poses/eureka/hood.webp differ diff --git a/public/poses/eureka/inspect.webp b/public/poses/eureka/inspect.webp new file mode 100644 index 00000000..2d7bb798 Binary files /dev/null and b/public/poses/eureka/inspect.webp differ diff --git a/public/poses/eureka/neutral.webp b/public/poses/eureka/neutral.webp new file mode 100644 index 00000000..a0ec077a Binary files /dev/null and b/public/poses/eureka/neutral.webp differ diff --git a/public/poses/eureka/probe.webp b/public/poses/eureka/probe.webp new file mode 100644 index 00000000..8217aed3 Binary files /dev/null and b/public/poses/eureka/probe.webp differ diff --git a/public/poses/eureka/report.webp b/public/poses/eureka/report.webp new file mode 100644 index 00000000..c6ca480e Binary files /dev/null and b/public/poses/eureka/report.webp differ diff --git a/public/poses/eureka/surprised.webp b/public/poses/eureka/surprised.webp new file mode 100644 index 00000000..067fc33c Binary files /dev/null and b/public/poses/eureka/surprised.webp differ diff --git a/public/poses/eureka/thinking.webp b/public/poses/eureka/thinking.webp new file mode 100644 index 00000000..4789ee16 Binary files /dev/null and b/public/poses/eureka/thinking.webp differ diff --git a/public/poses/heimdall/amused.webp b/public/poses/heimdall/amused.webp new file mode 100644 index 00000000..d84ad757 Binary files /dev/null and b/public/poses/heimdall/amused.webp differ diff --git a/public/poses/heimdall/annoyed.webp b/public/poses/heimdall/annoyed.webp new file mode 100644 index 00000000..8bbb4d10 Binary files /dev/null and b/public/poses/heimdall/annoyed.webp differ diff --git a/public/poses/heimdall/badge.webp b/public/poses/heimdall/badge.webp new file mode 100644 index 00000000..c78cacbc Binary files /dev/null and b/public/poses/heimdall/badge.webp differ diff --git a/public/poses/heimdall/barrier.webp b/public/poses/heimdall/barrier.webp new file mode 100644 index 00000000..d35ea9ff Binary files /dev/null and b/public/poses/heimdall/barrier.webp differ diff --git a/public/poses/heimdall/concerned.webp b/public/poses/heimdall/concerned.webp new file mode 100644 index 00000000..549719a2 Binary files /dev/null and b/public/poses/heimdall/concerned.webp differ diff --git a/public/poses/heimdall/determined.webp b/public/poses/heimdall/determined.webp new file mode 100644 index 00000000..cbe34ae1 Binary files /dev/null and b/public/poses/heimdall/determined.webp differ diff --git a/public/poses/heimdall/happy.webp b/public/poses/heimdall/happy.webp new file mode 100644 index 00000000..a9bdc79b Binary files /dev/null and b/public/poses/heimdall/happy.webp differ diff --git a/public/poses/heimdall/neutral.webp b/public/poses/heimdall/neutral.webp new file mode 100644 index 00000000..ca9ed41f Binary files /dev/null and b/public/poses/heimdall/neutral.webp differ diff --git a/public/poses/heimdall/protect.webp b/public/poses/heimdall/protect.webp new file mode 100644 index 00000000..7daafa22 Binary files /dev/null and b/public/poses/heimdall/protect.webp differ diff --git a/public/poses/heimdall/revoke.webp b/public/poses/heimdall/revoke.webp new file mode 100644 index 00000000..96eb3215 Binary files /dev/null and b/public/poses/heimdall/revoke.webp differ diff --git a/public/poses/heimdall/surprised.webp b/public/poses/heimdall/surprised.webp new file mode 100644 index 00000000..f50c455d Binary files /dev/null and b/public/poses/heimdall/surprised.webp differ diff --git a/public/poses/heimdall/thinking.webp b/public/poses/heimdall/thinking.webp new file mode 100644 index 00000000..9c5e89a5 Binary files /dev/null and b/public/poses/heimdall/thinking.webp differ diff --git a/public/poses/kiko/amused.webp b/public/poses/kiko/amused.webp new file mode 100644 index 00000000..a9f7be7d Binary files /dev/null and b/public/poses/kiko/amused.webp differ diff --git a/public/poses/kiko/annoyed.webp b/public/poses/kiko/annoyed.webp new file mode 100644 index 00000000..b2063e8f Binary files /dev/null and b/public/poses/kiko/annoyed.webp differ diff --git a/public/poses/kiko/concerned.webp b/public/poses/kiko/concerned.webp new file mode 100644 index 00000000..5a1b9f42 Binary files /dev/null and b/public/poses/kiko/concerned.webp differ diff --git a/public/poses/kiko/confetti.webp b/public/poses/kiko/confetti.webp new file mode 100644 index 00000000..9610c0de Binary files /dev/null and b/public/poses/kiko/confetti.webp differ diff --git a/public/poses/kiko/debug.webp b/public/poses/kiko/debug.webp new file mode 100644 index 00000000..653e1073 Binary files /dev/null and b/public/poses/kiko/debug.webp differ diff --git a/public/poses/kiko/determined.webp b/public/poses/kiko/determined.webp new file mode 100644 index 00000000..d5e14232 Binary files /dev/null and b/public/poses/kiko/determined.webp differ diff --git a/public/poses/kiko/happy.webp b/public/poses/kiko/happy.webp new file mode 100644 index 00000000..a777070b Binary files /dev/null and b/public/poses/kiko/happy.webp differ diff --git a/public/poses/kiko/neutral.webp b/public/poses/kiko/neutral.webp new file mode 100644 index 00000000..8ae26999 Binary files /dev/null and b/public/poses/kiko/neutral.webp differ diff --git a/public/poses/kiko/sprint.webp b/public/poses/kiko/sprint.webp new file mode 100644 index 00000000..2fb4de3b Binary files /dev/null and b/public/poses/kiko/sprint.webp differ diff --git a/public/poses/kiko/surprised.webp b/public/poses/kiko/surprised.webp new file mode 100644 index 00000000..37adb7a6 Binary files /dev/null and b/public/poses/kiko/surprised.webp differ diff --git a/public/poses/kiko/thinking.webp b/public/poses/kiko/thinking.webp new file mode 100644 index 00000000..2ad2c9af Binary files /dev/null and b/public/poses/kiko/thinking.webp differ diff --git a/public/poses/kiko/tinker.webp b/public/poses/kiko/tinker.webp new file mode 100644 index 00000000..e53019f8 Binary files /dev/null and b/public/poses/kiko/tinker.webp differ diff --git a/public/poses/libby/amused.webp b/public/poses/libby/amused.webp new file mode 100644 index 00000000..03006036 Binary files /dev/null and b/public/poses/libby/amused.webp differ diff --git a/public/poses/libby/annoyed.webp b/public/poses/libby/annoyed.webp new file mode 100644 index 00000000..d4810a1e Binary files /dev/null and b/public/poses/libby/annoyed.webp differ diff --git a/public/poses/libby/concerned.webp b/public/poses/libby/concerned.webp new file mode 100644 index 00000000..bc11d149 Binary files /dev/null and b/public/poses/libby/concerned.webp differ diff --git a/public/poses/libby/determined.webp b/public/poses/libby/determined.webp new file mode 100644 index 00000000..2170715b Binary files /dev/null and b/public/poses/libby/determined.webp differ diff --git a/public/poses/libby/happy.webp b/public/poses/libby/happy.webp new file mode 100644 index 00000000..182ec8be Binary files /dev/null and b/public/poses/libby/happy.webp differ diff --git a/public/poses/libby/neutral.webp b/public/poses/libby/neutral.webp new file mode 100644 index 00000000..ae5a76ad Binary files /dev/null and b/public/poses/libby/neutral.webp differ diff --git a/public/poses/libby/research.webp b/public/poses/libby/research.webp new file mode 100644 index 00000000..8bf7d487 Binary files /dev/null and b/public/poses/libby/research.webp differ diff --git a/public/poses/libby/schema.webp b/public/poses/libby/schema.webp new file mode 100644 index 00000000..2dc8b116 Binary files /dev/null and b/public/poses/libby/schema.webp differ diff --git a/public/poses/libby/shush.webp b/public/poses/libby/shush.webp new file mode 100644 index 00000000..07466e6e Binary files /dev/null and b/public/poses/libby/shush.webp differ diff --git a/public/poses/libby/stamp.webp b/public/poses/libby/stamp.webp new file mode 100644 index 00000000..854d374c Binary files /dev/null and b/public/poses/libby/stamp.webp differ diff --git a/public/poses/libby/surprised.webp b/public/poses/libby/surprised.webp new file mode 100644 index 00000000..2ca82ba7 Binary files /dev/null and b/public/poses/libby/surprised.webp differ diff --git a/public/poses/libby/thinking.webp b/public/poses/libby/thinking.webp new file mode 100644 index 00000000..9a0e9b76 Binary files /dev/null and b/public/poses/libby/thinking.webp differ diff --git a/public/poses/nibu/amused.webp b/public/poses/nibu/amused.webp new file mode 100644 index 00000000..62f3a160 Binary files /dev/null and b/public/poses/nibu/amused.webp differ diff --git a/public/poses/nibu/annoyed.webp b/public/poses/nibu/annoyed.webp new file mode 100644 index 00000000..e9ce2d5d Binary files /dev/null and b/public/poses/nibu/annoyed.webp differ diff --git a/public/poses/nibu/concerned.webp b/public/poses/nibu/concerned.webp new file mode 100644 index 00000000..10f7b1e1 Binary files /dev/null and b/public/poses/nibu/concerned.webp differ diff --git a/public/poses/nibu/console.webp b/public/poses/nibu/console.webp new file mode 100644 index 00000000..afeac6b9 Binary files /dev/null and b/public/poses/nibu/console.webp differ diff --git a/public/poses/nibu/determined.webp b/public/poses/nibu/determined.webp new file mode 100644 index 00000000..96c5868e Binary files /dev/null and b/public/poses/nibu/determined.webp differ diff --git a/public/poses/nibu/glitch.webp b/public/poses/nibu/glitch.webp new file mode 100644 index 00000000..686978be Binary files /dev/null and b/public/poses/nibu/glitch.webp differ diff --git a/public/poses/nibu/happy.webp b/public/poses/nibu/happy.webp new file mode 100644 index 00000000..e9a10235 Binary files /dev/null and b/public/poses/nibu/happy.webp differ diff --git a/public/poses/nibu/lore.webp b/public/poses/nibu/lore.webp new file mode 100644 index 00000000..9e10a464 Binary files /dev/null and b/public/poses/nibu/lore.webp differ diff --git a/public/poses/nibu/neutral.webp b/public/poses/nibu/neutral.webp new file mode 100644 index 00000000..b07dc83b Binary files /dev/null and b/public/poses/nibu/neutral.webp differ diff --git a/public/poses/nibu/surprised.webp b/public/poses/nibu/surprised.webp new file mode 100644 index 00000000..1454b77b Binary files /dev/null and b/public/poses/nibu/surprised.webp differ diff --git a/public/poses/nibu/thinking.webp b/public/poses/nibu/thinking.webp new file mode 100644 index 00000000..fdf6bdac Binary files /dev/null and b/public/poses/nibu/thinking.webp differ diff --git a/public/poses/nibu/triumph.webp b/public/poses/nibu/triumph.webp new file mode 100644 index 00000000..ea6157b4 Binary files /dev/null and b/public/poses/nibu/triumph.webp differ diff --git a/public/poses/weksa/amused.webp b/public/poses/weksa/amused.webp new file mode 100644 index 00000000..8ec99ef3 Binary files /dev/null and b/public/poses/weksa/amused.webp differ diff --git a/public/poses/weksa/annoyed.webp b/public/poses/weksa/annoyed.webp new file mode 100644 index 00000000..9f43f936 Binary files /dev/null and b/public/poses/weksa/annoyed.webp differ diff --git a/public/poses/weksa/argue.webp b/public/poses/weksa/argue.webp new file mode 100644 index 00000000..7f23efcb Binary files /dev/null and b/public/poses/weksa/argue.webp differ diff --git a/public/poses/weksa/concerned.webp b/public/poses/weksa/concerned.webp new file mode 100644 index 00000000..c98206f9 Binary files /dev/null and b/public/poses/weksa/concerned.webp differ diff --git a/public/poses/weksa/determined.webp b/public/poses/weksa/determined.webp new file mode 100644 index 00000000..b5a33793 Binary files /dev/null and b/public/poses/weksa/determined.webp differ diff --git a/public/poses/weksa/fieldwork.webp b/public/poses/weksa/fieldwork.webp new file mode 100644 index 00000000..cfbef5c5 Binary files /dev/null and b/public/poses/weksa/fieldwork.webp differ diff --git a/public/poses/weksa/happy.webp b/public/poses/weksa/happy.webp new file mode 100644 index 00000000..81aa31ca Binary files /dev/null and b/public/poses/weksa/happy.webp differ diff --git a/public/poses/weksa/interlingua.webp b/public/poses/weksa/interlingua.webp new file mode 100644 index 00000000..88cc4649 Binary files /dev/null and b/public/poses/weksa/interlingua.webp differ diff --git a/public/poses/weksa/map.webp b/public/poses/weksa/map.webp new file mode 100644 index 00000000..2cbfa701 Binary files /dev/null and b/public/poses/weksa/map.webp differ diff --git a/public/poses/weksa/neutral.webp b/public/poses/weksa/neutral.webp new file mode 100644 index 00000000..925e5934 Binary files /dev/null and b/public/poses/weksa/neutral.webp differ diff --git a/public/poses/weksa/surprised.webp b/public/poses/weksa/surprised.webp new file mode 100644 index 00000000..5e74a195 Binary files /dev/null and b/public/poses/weksa/surprised.webp differ diff --git a/public/poses/weksa/thinking.webp b/public/poses/weksa/thinking.webp new file mode 100644 index 00000000..8f286291 Binary files /dev/null and b/public/poses/weksa/thinking.webp differ diff --git a/src/app/components/message/layout/Bubble.tsx b/src/app/components/message/layout/Bubble.tsx index 93ff84bc..7fc5171c 100644 --- a/src/app/components/message/layout/Bubble.tsx +++ b/src/app/components/message/layout/Bubble.tsx @@ -5,11 +5,12 @@ import * as css from './layout.css'; type BubbleArrowProps = { variant: ContainerColor; + pose?: boolean; }; -function BubbleLeftArrow({ variant }: BubbleArrowProps) { +function BubbleLeftArrow({ variant, pose }: BubbleArrowProps) { return ( ( - ({ hideBubble, before, header, children, ...props }, ref) => ( - - + ({ hideBubble, pose, before, header, children, ...props }, ref) => ( + + {before} @@ -48,11 +55,15 @@ export const BubbleLayout = as<'div', BubbleLayoutProps>( className={ hideBubble ? undefined - : classNames(css.BubbleContent, before ? css.BubbleContentArrowLeft : undefined) + : classNames( + css.BubbleContent, + before ? css.BubbleContentArrowLeft : undefined, + pose && css.BubbleContentPoseStacked + ) } direction="Column" > - {before ? : null} + {before ? : null} {children} diff --git a/src/app/components/message/layout/layout.css.ts b/src/app/components/message/layout/layout.css.ts index cc2cd0c6..cb53a62d 100644 --- a/src/app/components/message/layout/layout.css.ts +++ b/src/app/components/message/layout/layout.css.ts @@ -138,6 +138,34 @@ export const BubbleBefore = style({ minWidth: toRem(36), }); +// Pose portraits: beside the bubble on wide screens; on phones the row stacks, so the +// portrait costs a little height instead of a column of width, and the left arrow goes. +export const BubblePose = style({ + '@media': { + 'screen and (max-width: 750px)': { + flexDirection: 'column', + gap: config.space.S100, + }, + }, +}); + +export const BubbleBeforePose = style({ + minWidth: toRem(64), + '@media': { + 'screen and (max-width: 750px)': { + minWidth: 0, + }, + }, +}); + +export const BubbleArrowPose = style({ + '@media': { + 'screen and (max-width: 750px)': { + display: 'none', + }, + }, +}); + export const BubbleContent = style({ maxWidth: toRem(800), padding: config.space.S200, @@ -151,6 +179,34 @@ export const BubbleContentArrowLeft = style({ borderTopLeftRadius: 0, }); +export const BubbleContentPoseStacked = style({ + '@media': { + 'screen and (max-width: 750px)': { + borderTopLeftRadius: config.radii.R500, + }, + }, +}); + +export const PoseButton = style({ + padding: 0, + border: 'none', + background: 'none', + cursor: 'pointer', + lineHeight: 0, +}); + +export const PosePortrait = style({ + width: toRem(64), + height: toRem(64), + objectFit: 'contain', + '@media': { + 'screen and (max-width: 750px)': { + width: toRem(48), + height: toRem(48), + }, + }, +}); + export const BubbleLeftArrow = style({ width: toRem(9), height: toRem(8), diff --git a/src/app/components/message/pose/PoseAvatar.tsx b/src/app/components/message/pose/PoseAvatar.tsx new file mode 100644 index 00000000..ed8bc485 --- /dev/null +++ b/src/app/components/message/pose/PoseAvatar.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import * as css from '../layout/layout.css'; +import { poseSrc } from './poses'; + +type PoseAvatarProps = { + mind: string; + pose: string; + userId: string; + onClick: React.MouseEventHandler; +}; + +export function PoseAvatar({ mind, pose, userId, onClick }: PoseAvatarProps) { + return ( + {`${mind}, + + ); +} diff --git a/src/app/components/message/pose/poses.test.ts b/src/app/components/message/pose/poses.test.ts new file mode 100644 index 00000000..0915cfa7 --- /dev/null +++ b/src/app/components/message/pose/poses.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest'; +import { mindOfSender, poseSrc, resolvePose } from './poses'; + +describe('resolvePose', () => { + it('keeps shared and own-signature labels', () => { + expect(resolvePose('aqua', 'amused')).toBe('amused'); + expect(resolvePose('aqua', 'conduct')).toBe('conduct'); + expect(resolvePose('kiko', ' Debug ')).toBe('debug'); + }); + it('falls back to neutral for missing, unknown, non-string or foreign-signature labels', () => { + expect(resolvePose('aqua', undefined)).toBe('neutral'); + expect(resolvePose('aqua', 'smug')).toBe('neutral'); + expect(resolvePose('aqua', 7)).toBe('neutral'); + expect(resolvePose('aqua', 'probe')).toBe('neutral'); + expect(resolvePose('nobody', 'probe')).toBe('neutral'); + }); +}); + +describe('mindOfSender', () => { + it('matches localpart or first display-name word', () => { + expect(mindOfSender('Eureka')).toBe('eureka'); + expect(mindOfSender('x', 'Libby the Archivist')).toBe('libby'); + }); + it('ignores humans and Object prototype names', () => { + expect(mindOfSender('alice', 'Alice')).toBeUndefined(); + expect(mindOfSender('constructor')).toBeUndefined(); + expect(mindOfSender()).toBeUndefined(); + }); +}); + +describe('poseSrc', () => { + it('builds the asset path', () => { + expect(poseSrc('nibu', 'glitch', '/app/')).toBe('/app/public/poses/nibu/glitch.webp'); + }); +}); diff --git a/src/app/components/message/pose/poses.ts b/src/app/components/message/pose/poses.ts new file mode 100644 index 00000000..58ce59b8 --- /dev/null +++ b/src/app/components/message/pose/poses.ts @@ -0,0 +1,48 @@ +// Pose labels travel in the event content under POSE_KEY. Art exists per Mind; +// every Mind has the shared set plus four signature poses of its own. +export const POSE_KEY = 'org.gamecult.pose'; + +export const SHARED_POSES = [ + 'neutral', + 'happy', + 'amused', + 'thinking', + 'concerned', + 'annoyed', + 'surprised', + 'determined', +] as const; + +export const MIND_POSES: Record = { + aqua: ['conduct', 'listen', 'patch', 'flourish'], + eureka: ['probe', 'inspect', 'report', 'hood'], + heimdall: ['badge', 'barrier', 'protect', 'revoke'], + kiko: ['tinker', 'confetti', 'sprint', 'debug'], + libby: ['stamp', 'research', 'shush', 'schema'], + nibu: ['console', 'glitch', 'lore', 'triumph'], + weksa: ['map', 'fieldwork', 'argue', 'interlingua'], +}; + +export const NEUTRAL = 'neutral'; + +// A sender is a known Mind when its Matrix localpart or the first word of its +// display name is the Mind's name, case-insensitively. +export const mindOfSender = (localpart?: string, displayName?: string): string | undefined => { + const candidates = [localpart, displayName?.split(/\s+/)[0]]; + return candidates + .map((c) => c?.toLowerCase()) + .find( + (c): c is string => c !== undefined && Object.prototype.hasOwnProperty.call(MIND_POSES, c) + ); +}; + +// Unknown, missing or non-string labels, and another Mind's signature, fall back to neutral. +export const resolvePose = (mind: string, label: unknown): string => { + if (typeof label !== 'string') return NEUTRAL; + const l = label.trim().toLowerCase(); + const known = (SHARED_POSES as readonly string[]).includes(l) || MIND_POSES[mind]?.includes(l); + return known ? l : NEUTRAL; +}; + +export const poseSrc = (mind: string, pose: string, base = '/'): string => + `${base}public/poses/${mind}/${pose}.webp`; diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index 2d87e9bd..42d74a6b 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -58,7 +58,10 @@ import { isRoomAlias, mxcUrlToHttp, } from '../../../utils/matrix'; -import { MessageLayout, MessageSpacing } from '../../../state/settings'; +import { MessageLayout, MessageSpacing, settingsAtom } from '../../../state/settings'; +import { useSetting } from '../../../state/hooks/settings'; +import { PoseAvatar } from '../../../components/message/pose/PoseAvatar'; +import { POSE_KEY, mindOfSender, resolvePose } from '../../../components/message/pose/poses'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useRecentEmoji } from '../../../hooks/useRecentEmoji'; import * as css from './styles.css'; @@ -788,6 +791,12 @@ export const Message = as<'div', MessageProps>( ); + const [poseBubbles] = useSetting(settingsAtom, 'poseBubbles'); + const mind = + poseBubbles && messageLayout === MessageLayout.Bubble + ? mindOfSender(getMxIdLocalPart(senderId), senderDisplayName) + : undefined; + const avatarJSX = !collapse && messageLayout !== MessageLayout.Compact && ( ( ); + const poseJSX = !collapse && mind && ( + + ); + const msgContentJSX = ( {reply} @@ -1135,7 +1153,10 @@ export const Message = as<'div', MessageProps>( )} {messageLayout === MessageLayout.Bubble && ( - + {msgContentJSX} )} diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx index b861a060..5b51cd8c 100644 --- a/src/app/features/settings/general/General.tsx +++ b/src/app/features/settings/general/General.tsx @@ -884,6 +884,7 @@ function Messages() { settingsAtom, 'legacyUsernameColor' ); + const [poseBubbles, setPoseBubbles] = useSetting(settingsAtom, 'poseBubbles'); const [hideMembershipEvents, setHideMembershipEvents] = useSetting( settingsAtom, 'hideMembershipEvents' @@ -918,6 +919,13 @@ function Messages() { } /> + + } + /> +