mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
More changes
This commit is contained in:
parent
e1600b19ff
commit
f48a76e373
3 changed files with 50 additions and 46 deletions
|
@ -86,11 +86,6 @@ export interface PronounSet {
|
||||||
language: string
|
language: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExtendedUserProfile extends UserProfile {
|
|
||||||
"us.cloke.msc4175.tz"?: string
|
|
||||||
"io.fsky.nyx.pronouns"?: PronounSet[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Membership = "join" | "leave" | "ban" | "invite" | "knock"
|
export type Membership = "join" | "leave" | "ban" | "invite" | "knock"
|
||||||
|
|
||||||
export interface MemberEventContent extends UserProfile {
|
export interface MemberEventContent extends UserProfile {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import Client from "@/api/client.ts"
|
import Client from "@/api/client.ts"
|
||||||
import { ExtendedUserProfile, PronounSet } from "@/api/types"
|
import { PronounSet, UserProfile } from "@/api/types"
|
||||||
import { ensureArray, ensureString } from "@/util/validation.ts"
|
import { ensureArray, ensureString } from "@/util/validation.ts"
|
||||||
|
|
||||||
interface ExtendedProfileProps {
|
interface ExtendedProfileProps {
|
||||||
profile: ExtendedUserProfile
|
profile: UserProfile
|
||||||
|
refreshProfile: () => void
|
||||||
client: Client
|
client: Client
|
||||||
userID: string
|
userID: string
|
||||||
}
|
}
|
||||||
|
@ -12,6 +13,7 @@ interface ExtendedProfileProps {
|
||||||
interface SetTimezoneProps {
|
interface SetTimezoneProps {
|
||||||
tz?: string
|
tz?: string
|
||||||
client: Client
|
client: Client
|
||||||
|
refreshProfile: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCurrentTimezone = () => new Intl.DateTimeFormat().resolvedOptions().timeZone
|
const getCurrentTimezone = () => new Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
|
@ -30,7 +32,7 @@ const currentTimeAdjusted = (tz: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ClockElement({ tz }: { tz: string }) {
|
const ClockElement = ({ tz }: { tz: string }) => {
|
||||||
const [time, setTime] = useState(currentTimeAdjusted(tz))
|
const [time, setTime] = useState(currentTimeAdjusted(tz))
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let interval: number | undefined
|
let interval: number | undefined
|
||||||
|
@ -41,29 +43,37 @@ function ClockElement({ tz }: { tz: string }) {
|
||||||
}, (1001 - Date.now() % 1000))
|
}, (1001 - Date.now() % 1000))
|
||||||
return () => interval ? clearInterval(interval) : clearTimeout(timeout)
|
return () => interval ? clearInterval(interval) : clearTimeout(timeout)
|
||||||
}, [tz])
|
}, [tz])
|
||||||
return <div>{time}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function SetTimezoneElement({ tz, client }: SetTimezoneProps) {
|
|
||||||
const zones = Intl.supportedValuesOf("timeZone")
|
|
||||||
const setTz = (newTz: string) => {
|
|
||||||
if (zones.includes(newTz) && newTz !== tz) {
|
|
||||||
return client.rpc.setProfileField("us.cloke.msc4175.tz", newTz).then(
|
|
||||||
() => client.rpc.getProfile(client.userID),
|
|
||||||
(err) => console.error("Error setting timezone", err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: You are unable to set a timezone if you do not already have one set in your profile.
|
|
||||||
// The defaulting to the current timezone causes `newTz !== tz` to never be true when the user has
|
|
||||||
// no timezone set.
|
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
|
<div title={tz}>Time:</div>
|
||||||
|
<div title={tz}>{time}</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
const SetTimeZoneElement = ({ tz, client, refreshProfile }: SetTimezoneProps) => {
|
||||||
|
const zones = Intl.supportedValuesOf("timeZone")
|
||||||
|
const saveTz = (newTz: string) => {
|
||||||
|
if (!zones.includes(newTz)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.rpc.setProfileField("us.cloke.msc4175.tz", newTz).then(
|
||||||
|
() => refreshProfile(),
|
||||||
|
err => {
|
||||||
|
console.error("Failed to set time zone:", err)
|
||||||
|
window.alert(`Failed to set time zone: ${err}`)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<label htmlFor="userprofile-timezone-input">Set time zone:</label>
|
||||||
<input
|
<input
|
||||||
list="timezones"
|
list="timezones"
|
||||||
className="text-input"
|
className="text-input"
|
||||||
|
id="userprofile-timezone-input"
|
||||||
defaultValue={tz || getCurrentTimezone()}
|
defaultValue={tz || getCurrentTimezone()}
|
||||||
onChange={(e) => setTz(e.currentTarget.value)}
|
onKeyDown={evt => evt.key === "Enter" && saveTz(evt.currentTarget.value)}
|
||||||
|
onBlur={evt => evt.currentTarget.value !== tz && saveTz(evt.currentTarget.value)}
|
||||||
/>
|
/>
|
||||||
<datalist id="timezones">
|
<datalist id="timezones">
|
||||||
{zones.map((zone) => <option key={zone} value={zone} />)}
|
{zones.map((zone) => <option key={zone} value={zone} />)}
|
||||||
|
@ -72,7 +82,7 @@ function SetTimezoneElement({ tz, client }: SetTimezoneProps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function UserExtendedProfile({ profile, client, userID }: ExtendedProfileProps) {
|
const UserExtendedProfile = ({ profile, refreshProfile, client, userID }: ExtendedProfileProps)=> {
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -86,24 +96,19 @@ export default function UserExtendedProfile({ profile, client, userID }: Extende
|
||||||
// otherwise there's an ugly and pointless <hr/> for no real reason.
|
// otherwise there's an ugly and pointless <hr/> for no real reason.
|
||||||
|
|
||||||
const pronouns = ensureArray(profile["io.fsky.nyx.pronouns"]) as PronounSet[]
|
const pronouns = ensureArray(profile["io.fsky.nyx.pronouns"]) as PronounSet[]
|
||||||
const userTimezone = ensureString(profile["us.cloke.msc4175.tz"])
|
const userTimeZone = ensureString(profile["us.cloke.msc4175.tz"])
|
||||||
return <>
|
return <>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div className="extended-profile">
|
<div className="extended-profile">
|
||||||
{userTimezone && <>
|
{userTimeZone && <ClockElement tz={userTimeZone} />}
|
||||||
<div title={userTimezone}>Time:</div>
|
{userID === client.userID &&
|
||||||
<ClockElement tz={userTimezone} />
|
<SetTimeZoneElement tz={userTimeZone} client={client} refreshProfile={refreshProfile} />}
|
||||||
</>}
|
{pronouns.length > 0 && <>
|
||||||
{userID === client.userID && <>
|
|
||||||
<div>Set Timezone:</div>
|
|
||||||
<SetTimezoneElement tz={userTimezone} client={client} />
|
|
||||||
</>}
|
|
||||||
{pronouns.length >= 1 && <>
|
|
||||||
<div>Pronouns:</div>
|
<div>Pronouns:</div>
|
||||||
<div>
|
<div>{pronouns.map(pronounSet => ensureString(pronounSet.summary)).join(" / ")}</div>
|
||||||
{pronouns.map((pronounSet: PronounSet) => (pronounSet.summary)).join("/")}
|
|
||||||
</div>
|
|
||||||
</>}
|
</>}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default UserExtendedProfile
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import { use, useEffect, useState } from "react"
|
import { use, useCallback, useEffect, useState } from "react"
|
||||||
import { PuffLoader } from "react-spinners"
|
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"
|
||||||
|
@ -39,15 +39,17 @@ const UserInfo = ({ userID }: UserInfoProps) => {
|
||||||
const member = (memberEvt?.content ?? null) as MemberEventContent | null
|
const member = (memberEvt?.content ?? null) as MemberEventContent | null
|
||||||
const [globalProfile, setGlobalProfile] = useState<UserProfile | null>(null)
|
const [globalProfile, setGlobalProfile] = useState<UserProfile | null>(null)
|
||||||
const [errors, setErrors] = useState<string[] | null>(null)
|
const [errors, setErrors] = useState<string[] | null>(null)
|
||||||
useEffect(() => {
|
const refreshProfile = useCallback((clearState = false) => {
|
||||||
setErrors(null)
|
if (clearState) {
|
||||||
setGlobalProfile(null)
|
setErrors(null)
|
||||||
|
setGlobalProfile(null)
|
||||||
|
}
|
||||||
client.rpc.getProfile(userID).then(
|
client.rpc.getProfile(userID).then(
|
||||||
setGlobalProfile,
|
setGlobalProfile,
|
||||||
err => setErrors([`${err}`]),
|
err => setErrors([`${err}`]),
|
||||||
)
|
)
|
||||||
}, [roomCtx, userID, client])
|
}, [userID, client])
|
||||||
|
useEffect(() => refreshProfile(true), [refreshProfile])
|
||||||
|
|
||||||
const displayname = member?.displayname || globalProfile?.displayname || getLocalpart(userID)
|
const displayname = member?.displayname || globalProfile?.displayname || getLocalpart(userID)
|
||||||
return <>
|
return <>
|
||||||
|
@ -65,7 +67,9 @@ 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 && <UserExtendedProfile profile={globalProfile} client={client} userID={userID}/>}
|
{globalProfile && <UserExtendedProfile
|
||||||
|
profile={globalProfile} refreshProfile={refreshProfile} client={client} userID={userID}
|
||||||
|
/>}
|
||||||
<hr/>
|
<hr/>
|
||||||
{userID !== client.userID && <>
|
{userID !== client.userID && <>
|
||||||
<MutualRooms client={client} userID={userID}/>
|
<MutualRooms client={client} userID={userID}/>
|
||||||
|
|
Loading…
Add table
Reference in a new issue