web: merge event dispatcher hooks

This commit is contained in:
Tulir Asokan 2024-10-28 00:12:00 +02:00
parent 11a8aac398
commit a114b23b88
6 changed files with 15 additions and 26 deletions

View file

@ -22,14 +22,14 @@ import MainScreen from "./ui/MainScreen.tsx"
import { LoginScreen, VerificationScreen } from "./ui/login" import { LoginScreen, VerificationScreen } from "./ui/login"
import { LightboxWrapper } from "./ui/modal/Lightbox.tsx" import { LightboxWrapper } from "./ui/modal/Lightbox.tsx"
import { ModalWrapper } from "./ui/modal/Modal.tsx" import { ModalWrapper } from "./ui/modal/Modal.tsx"
import { useCachedEventAsState } from "./util/eventdispatcher.ts" import { useEventAsState } from "./util/eventdispatcher.ts"
const client = new Client(new WSClient("_gomuks/websocket")) const client = new Client(new WSClient("_gomuks/websocket"))
window.client = client window.client = client
function App() { function App() {
const connState = useCachedEventAsState(client.rpc.connect) const connState = useEventAsState(client.rpc.connect)
const clientState = useCachedEventAsState(client.state) const clientState = useEventAsState(client.state)
useEffect(() => { useEffect(() => {
Notification.requestPermission() Notification.requestPermission()
.then(permission => console.log("Notification permission:", permission)) .then(permission => console.log("Notification permission:", permission))

View file

@ -16,7 +16,7 @@
import { use, useRef } from "react" import { use, useRef } from "react"
import { getAvatarURL } from "@/api/media.ts" import { getAvatarURL } from "@/api/media.ts"
import { RoomStateStore } from "@/api/statestore" import { RoomStateStore } from "@/api/statestore"
import { useNonNullEventAsState } from "@/util/eventdispatcher.ts" import { useEventAsState } from "@/util/eventdispatcher.ts"
import MessageComposer from "./composer/MessageComposer.tsx" import MessageComposer from "./composer/MessageComposer.tsx"
import { LightboxContext } from "./modal/Lightbox.tsx" import { LightboxContext } from "./modal/Lightbox.tsx"
import { RoomContext, RoomContextData } from "./roomcontext.ts" import { RoomContext, RoomContextData } from "./roomcontext.ts"
@ -30,7 +30,7 @@ interface RoomViewProps {
} }
const RoomHeader = ({ room, clearActiveRoom }: RoomViewProps) => { const RoomHeader = ({ room, clearActiveRoom }: RoomViewProps) => {
const roomMeta = useNonNullEventAsState(room.meta) const roomMeta = useEventAsState(room.meta)
const avatarSourceID = roomMeta.lazy_load_summary?.heroes?.length === 1 const avatarSourceID = roomMeta.lazy_load_summary?.heroes?.length === 1
? roomMeta.lazy_load_summary.heroes[0] : room.roomID ? roomMeta.lazy_load_summary.heroes[0] : room.roomID
return <div className="room-header"> return <div className="room-header">

View file

@ -15,7 +15,7 @@
// 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 React, { use, useCallback, useRef, useState } from "react" import React, { use, useCallback, useRef, useState } from "react"
import type { RoomID } from "@/api/types" import type { RoomID } from "@/api/types"
import { useNonNullEventAsState } from "@/util/eventdispatcher.ts" import { useEventAsState } from "@/util/eventdispatcher.ts"
import toSearchableString from "@/util/searchablestring.ts" import toSearchableString from "@/util/searchablestring.ts"
import ClientContext from "../ClientContext.ts" import ClientContext from "../ClientContext.ts"
import Entry from "./Entry.tsx" import Entry from "./Entry.tsx"
@ -27,7 +27,7 @@ interface RoomListProps {
} }
const RoomList = ({ setActiveRoom, activeRoomID }: RoomListProps) => { const RoomList = ({ setActiveRoom, activeRoomID }: RoomListProps) => {
const roomList = useNonNullEventAsState(use(ClientContext)!.store.roomList) const roomList = useEventAsState(use(ClientContext)!.store.roomList)
const roomFilterRef = useRef<HTMLInputElement>(null) const roomFilterRef = useRef<HTMLInputElement>(null)
const [roomFilter, setRoomFilter] = useState("") const [roomFilter, setRoomFilter] = useState("")
const [realRoomFilter, setRealRoomFilter] = useState("") const [realRoomFilter, setRealRoomFilter] = useState("")

View file

@ -16,7 +16,7 @@
import { CSSProperties, use, useCallback, useRef } from "react" import { CSSProperties, use, useCallback, useRef } from "react"
import { MemDBEvent } from "@/api/types" import { MemDBEvent } from "@/api/types"
import { emojiToReactionContent } from "@/util/emoji" import { emojiToReactionContent } from "@/util/emoji"
import { useNonNullEventAsState } from "@/util/eventdispatcher.ts" import { useEventAsState } from "@/util/eventdispatcher.ts"
import ClientContext from "../../ClientContext.ts" import ClientContext from "../../ClientContext.ts"
import EmojiPicker from "../../emojipicker/EmojiPicker.tsx" import EmojiPicker from "../../emojipicker/EmojiPicker.tsx"
import { ModalContext } from "../../modal/Modal.tsx" import { ModalContext } from "../../modal/Modal.tsx"
@ -83,7 +83,7 @@ const EventMenu = ({ evt, setForceOpen }: EventHoverMenuProps) => {
onClose: () => setForceOpen(false), onClose: () => setForceOpen(false),
}) })
}, [evt, roomCtx, setForceOpen, openModal]) }, [evt, roomCtx, setForceOpen, openModal])
const isEditing = useNonNullEventAsState(roomCtx.isEditing) const isEditing = useEventAsState(roomCtx.isEditing)
return <div className="event-hover-menu" ref={contextMenuRef}> return <div className="event-hover-menu" ref={contextMenuRef}>
<button onClick={onClickReact}><ReactIcon/></button> <button onClick={onClickReact}><ReactIcon/></button>
<button <button

View file

@ -13,22 +13,11 @@
// //
// 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 { useEffect, useState, useSyncExternalStore } from "react" import { useSyncExternalStore } from "react"
export function useEventAsState<T>(dispatcher?: EventDispatcher<T>): T | null { export function useEventAsState<T>(dispatcher: NonNullCachedEventDispatcher<T>): T
const [state, setState] = useState<T | null>(null) export function useEventAsState<T>(dispatcher: CachedEventDispatcher<T>): T | null
useEffect(() => dispatcher && dispatcher.listen(setState), [dispatcher]) export function useEventAsState<T>(dispatcher: CachedEventDispatcher<T>): T | null {
return state
}
export function useCachedEventAsState<T>(dispatcher: CachedEventDispatcher<T>): T | null {
return useSyncExternalStore(
dispatcher.listenChange,
() => dispatcher.current,
)
}
export function useNonNullEventAsState<T>(dispatcher: NonNullCachedEventDispatcher<T>): T {
return useSyncExternalStore( return useSyncExternalStore(
dispatcher.listenChange, dispatcher.listenChange,
() => dispatcher.current, () => dispatcher.current,

View file

@ -13,12 +13,12 @@
// //
// 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 { NonNullCachedEventDispatcher, useNonNullEventAsState } from "@/util/eventdispatcher.ts" import { NonNullCachedEventDispatcher, useEventAsState } from "@/util/eventdispatcher.ts"
export const focused = new NonNullCachedEventDispatcher(document.hasFocus()) export const focused = new NonNullCachedEventDispatcher(document.hasFocus())
window.addEventListener("focus", () => focused.emit(true)) window.addEventListener("focus", () => focused.emit(true))
window.addEventListener("blur", () => focused.emit(false)) window.addEventListener("blur", () => focused.emit(false))
export default function useFocus() { export default function useFocus() {
return useNonNullEventAsState(focused) return useEventAsState(focused)
} }