Add timezones and pronouns (closes #569)

This commit is contained in:
nexy7574 2024-12-29 21:25:56 +00:00
parent 0b424e59bf
commit 80c3da29aa
3 changed files with 87 additions and 1 deletions

View file

@ -91,6 +91,17 @@ div.right-panel-content.user {
word-break: break-word; word-break: break-word;
} }
div.extended-profile {
display: flex;
flex-direction: column;
gap: 0.25rem;
div.profile-row {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
hr { hr {
width: 100%; width: 100%;
opacity: .2; opacity: .2;

View file

@ -0,0 +1,74 @@
import { useEffect, useState } from "react"
import { UserProfile } from "@/api/types"
import { ensureArray } from "@/util/validation.ts"
interface PronounSet {
subject: string
object: string
possessive_determiner: string
possessive_pronoun: string
reflexive: string
summary: string
}
interface ExtendedProfileAttributes {
"us.cloke.msc4175.tz"?: string
"io.fsky.nyx.pronouns"?: PronounSet[]
}
interface ExtendedProfileProps {
profile: UserProfile & ExtendedProfileAttributes
}
const currentTimeAdjusted = (tz: string) => {
const lang = navigator.language || "en-US"
const now = new Date()
return new Intl.DateTimeFormat(lang, { timeStyle: "long", timeZone: tz }).format(now)
}
function ClockElement({ tz }: { tz: string }) {
const [time, setTime] = useState(currentTimeAdjusted(tz))
useEffect(() => {
const interval = setInterval(() => {
setTime(currentTimeAdjusted(tz))
}, (1000 - Date.now() % 1000))
return () => clearInterval(interval)
}, [tz])
return <div>{time}</div>
}
export default function UserExtendedProfile({ profile }: ExtendedProfileProps) {
if (!profile) return null
const pronouns: PronounSet[] = ensureArray(profile["io.fsky.nyx.pronouns"]) as PronounSet[]
return (
<div className={"extended-profile"}>
{
profile["us.cloke.msc4175.tz"] && (
<div className={"profile-row"}>
<div title={profile["us.cloke.msc4175.tz"]}>Time:</div>
<ClockElement tz={profile["us.cloke.msc4175.tz"]} />
</div>
)
}
{
pronouns.length >= 1 && (
<div className={"profile-row"}>
<div>Pronouns:</div>
<div>
{
pronouns.map(
(pronounSet: PronounSet) => (
pronounSet.summary || `${pronounSet.subject}/${pronounSet.object}`
),
).join("/")
}
</div>
</div>
)
}
</div>
)
}

View file

@ -18,6 +18,7 @@ import { PuffLoader } from "react-spinners"
import { getAvatarURL } from "@/api/media.ts" import { getAvatarURL } from "@/api/media.ts"
import { useRoomMember } from "@/api/statestore" import { useRoomMember } from "@/api/statestore"
import { MemberEventContent, UserID, UserProfile } from "@/api/types" import { MemberEventContent, UserID, UserProfile } from "@/api/types"
import UserExtendedProfile from "@/ui/rightpanel/UserExtendedProfile.tsx"
import { getLocalpart } from "@/util/validation.ts" import { getLocalpart } from "@/util/validation.ts"
import ClientContext from "../ClientContext.ts" import ClientContext from "../ClientContext.ts"
import { LightboxContext } from "../modal" import { LightboxContext } from "../modal"
@ -46,7 +47,6 @@ const UserInfo = ({ userID }: UserInfoProps) => {
err => setErrors([`${err}`]), err => setErrors([`${err}`]),
) )
}, [roomCtx, userID, client]) }, [roomCtx, userID, client])
const displayname = member?.displayname || globalProfile?.displayname || getLocalpart(userID) const displayname = member?.displayname || globalProfile?.displayname || getLocalpart(userID)
return <> return <>
<div className="avatar-container"> <div className="avatar-container">
@ -63,6 +63,7 @@ const UserInfo = ({ userID }: UserInfoProps) => {
</div> </div>
<div className="displayname" title={displayname}>{displayname}</div> <div className="displayname" title={displayname}>{displayname}</div>
<div className="userid" title={userID}>{userID}</div> <div className="userid" title={userID}>{userID}</div>
{globalProfile && <><hr/><UserExtendedProfile profile={globalProfile}/></>}
<hr/> <hr/>
{userID !== client.userID && <> {userID !== client.userID && <>
<MutualRooms client={client} userID={userID}/> <MutualRooms client={client} userID={userID}/>