Timeline-refactor-fixes (#1438)
* fix type * fix missing member from reaction * stop context menu event propagation in msg modal * prevent encode blur hash from freezing app * replace roboto font with inter and fix weight * add recent emoji when selecting emoji * fix room latest evt hook * add option to drop typing status
This commit is contained in:
parent
f9b895b32c
commit
1bdb7f4e3a
18 changed files with 138 additions and 64 deletions
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -10,7 +10,6 @@
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/inter": "4.5.14",
|
"@fontsource/inter": "4.5.14",
|
||||||
"@fontsource/roboto": "4.5.8",
|
|
||||||
"@khanacademy/simple-markdown": "0.8.6",
|
"@khanacademy/simple-markdown": "0.8.6",
|
||||||
"@matrix-org/olm": "3.2.14",
|
"@matrix-org/olm": "3.2.14",
|
||||||
"@tanstack/react-virtual": "3.0.0-beta.54",
|
"@tanstack/react-virtual": "3.0.0-beta.54",
|
||||||
|
@ -875,11 +874,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-4.5.14.tgz",
|
"resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-4.5.14.tgz",
|
||||||
"integrity": "sha512-JDC9AocdPLuGsASkvWw9hS5gtHE7K9dOwL98XLrk5yjYqxy4uVnScG58NUvFMJDVJRl/7c8Wnap6PEs+7Zvj1Q=="
|
"integrity": "sha512-JDC9AocdPLuGsASkvWw9hS5gtHE7K9dOwL98XLrk5yjYqxy4uVnScG58NUvFMJDVJRl/7c8Wnap6PEs+7Zvj1Q=="
|
||||||
},
|
},
|
||||||
"node_modules/@fontsource/roboto": {
|
|
||||||
"version": "4.5.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-4.5.8.tgz",
|
|
||||||
"integrity": "sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA=="
|
|
||||||
},
|
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.11.7",
|
"version": "0.11.7",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz",
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/inter": "4.5.14",
|
"@fontsource/inter": "4.5.14",
|
||||||
"@fontsource/roboto": "4.5.8",
|
|
||||||
"@khanacademy/simple-markdown": "0.8.6",
|
"@khanacademy/simple-markdown": "0.8.6",
|
||||||
"@matrix-org/olm": "3.2.14",
|
"@matrix-org/olm": "3.2.14",
|
||||||
"@tanstack/react-virtual": "3.0.0-beta.54",
|
"@tanstack/react-virtual": "3.0.0-beta.54",
|
||||||
|
|
|
@ -46,6 +46,7 @@ import { editableActiveElement, isIntersectingScrollView, targetFromEvent } from
|
||||||
import { useAsyncSearch, UseAsyncSearchOptions } from '../../hooks/useAsyncSearch';
|
import { useAsyncSearch, UseAsyncSearchOptions } from '../../hooks/useAsyncSearch';
|
||||||
import { useDebounce } from '../../hooks/useDebounce';
|
import { useDebounce } from '../../hooks/useDebounce';
|
||||||
import { useThrottle } from '../../hooks/useThrottle';
|
import { useThrottle } from '../../hooks/useThrottle';
|
||||||
|
import { addRecentEmoji } from '../../plugins/recent-emoji';
|
||||||
|
|
||||||
const RECENT_GROUP_ID = 'recent_group';
|
const RECENT_GROUP_ID = 'recent_group';
|
||||||
const SEARCH_GROUP_ID = 'search_group';
|
const SEARCH_GROUP_ID = 'search_group';
|
||||||
|
@ -697,7 +698,10 @@ export function EmojiBoard({
|
||||||
if (!emojiInfo) return;
|
if (!emojiInfo) return;
|
||||||
if (emojiInfo.type === EmojiType.Emoji) {
|
if (emojiInfo.type === EmojiType.Emoji) {
|
||||||
onEmojiSelect?.(emojiInfo.data, emojiInfo.shortcode);
|
onEmojiSelect?.(emojiInfo.data, emojiInfo.shortcode);
|
||||||
if (!evt.altKey && !evt.shiftKey) requestClose();
|
if (!evt.altKey && !evt.shiftKey) {
|
||||||
|
addRecentEmoji(mx, emojiInfo.data);
|
||||||
|
requestClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (emojiInfo.type === EmojiType.CustomEmoji) {
|
if (emojiInfo.type === EmojiType.CustomEmoji) {
|
||||||
onCustomEmojiSelect?.(emojiInfo.data, emojiInfo.shortcode);
|
onCustomEmojiSelect?.(emojiInfo.data, emojiInfo.shortcode);
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { MatrixEvent, Room, RoomEvent, RoomEventHandlerMap } from 'matrix-js-sdk';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
export const useRoomLatestEvent = (room: Room) => {
|
|
||||||
const [latestEvent, setLatestEvent] = useState<MatrixEvent>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const getLatestEvent = (): MatrixEvent | undefined => {
|
|
||||||
const liveEvents = room.getLiveTimeline().getEvents();
|
|
||||||
for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
|
|
||||||
const evt = liveEvents[i];
|
|
||||||
if (evt) return evt;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTimelineEvent: RoomEventHandlerMap[RoomEvent.Timeline] = () => {
|
|
||||||
setLatestEvent(getLatestEvent());
|
|
||||||
};
|
|
||||||
setLatestEvent(getLatestEvent());
|
|
||||||
|
|
||||||
room.on(RoomEvent.Timeline, handleTimelineEvent);
|
|
||||||
return () => {
|
|
||||||
room.removeListener(RoomEvent.Timeline, handleTimelineEvent);
|
|
||||||
};
|
|
||||||
}, [room]);
|
|
||||||
|
|
||||||
return latestEvent;
|
|
||||||
};
|
|
57
src/app/hooks/useRoomLatestRenderedEvent.ts
Normal file
57
src/app/hooks/useRoomLatestRenderedEvent.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/* eslint-disable no-continue */
|
||||||
|
import { MatrixEvent, Room, RoomEvent, RoomEventHandlerMap } from 'matrix-js-sdk';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { settingsAtom } from '../state/settings';
|
||||||
|
import { useSetting } from '../state/hooks/settings';
|
||||||
|
import { MessageEvent, StateEvent } from '../../types/matrix/room';
|
||||||
|
|
||||||
|
export const useRoomLatestRenderedEvent = (room: Room) => {
|
||||||
|
const [hideMembershipEvents] = useSetting(settingsAtom, 'hideMembershipEvents');
|
||||||
|
const [hideNickAvatarEvents] = useSetting(settingsAtom, 'hideNickAvatarEvents');
|
||||||
|
const [showHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
|
||||||
|
const [latestEvent, setLatestEvent] = useState<MatrixEvent>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const getLatestEvent = (): MatrixEvent | undefined => {
|
||||||
|
const liveEvents = room.getLiveTimeline().getEvents();
|
||||||
|
for (let i = liveEvents.length - 1; i >= 0; i -= 1) {
|
||||||
|
const evt = liveEvents[i];
|
||||||
|
|
||||||
|
if (!evt) continue;
|
||||||
|
if (evt.isRelation()) continue;
|
||||||
|
if (evt.getType() === StateEvent.RoomMember) {
|
||||||
|
const membershipChanged = evt.getContent().membership !== evt.getPrevContent().membership;
|
||||||
|
if (membershipChanged && hideMembershipEvents) continue;
|
||||||
|
if (!membershipChanged && hideNickAvatarEvents) continue;
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
evt.getType() === MessageEvent.RoomMessage ||
|
||||||
|
evt.getType() === MessageEvent.RoomMessageEncrypted ||
|
||||||
|
evt.getType() === MessageEvent.Sticker ||
|
||||||
|
evt.getType() === StateEvent.RoomName ||
|
||||||
|
evt.getType() === StateEvent.RoomTopic ||
|
||||||
|
evt.getType() === StateEvent.RoomAvatar
|
||||||
|
) {
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showHiddenEvents) return evt;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTimelineEvent: RoomEventHandlerMap[RoomEvent.Timeline] = () => {
|
||||||
|
setLatestEvent(getLatestEvent());
|
||||||
|
};
|
||||||
|
setLatestEvent(getLatestEvent());
|
||||||
|
|
||||||
|
room.on(RoomEvent.Timeline, handleTimelineEvent);
|
||||||
|
return () => {
|
||||||
|
room.removeListener(RoomEvent.Timeline, handleTimelineEvent);
|
||||||
|
};
|
||||||
|
}, [room, hideMembershipEvents, hideNickAvatarEvents, showHiddenEvents]);
|
||||||
|
|
||||||
|
return latestEvent;
|
||||||
|
};
|
|
@ -167,7 +167,7 @@ export const getFirstLinkedTimeline = (
|
||||||
|
|
||||||
export const getLinkedTimelines = (timeline: EventTimeline): EventTimeline[] => {
|
export const getLinkedTimelines = (timeline: EventTimeline): EventTimeline[] => {
|
||||||
const firstTimeline = getFirstLinkedTimeline(timeline, Direction.Backward);
|
const firstTimeline = getFirstLinkedTimeline(timeline, Direction.Backward);
|
||||||
const timelines = [];
|
const timelines: EventTimeline[] = [];
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let nextTimeline: EventTimeline | null = firstTimeline;
|
let nextTimeline: EventTimeline | null = firstTimeline;
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { getMemberDisplayName } from '../../utils/room';
|
||||||
import { getMxIdLocalPart } from '../../utils/matrix';
|
import { getMxIdLocalPart } from '../../utils/matrix';
|
||||||
import * as css from './RoomViewFollowing.css';
|
import * as css from './RoomViewFollowing.css';
|
||||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||||
import { useRoomLatestEvent } from '../../hooks/useRoomLatestEvent';
|
import { useRoomLatestRenderedEvent } from '../../hooks/useRoomLatestRenderedEvent';
|
||||||
import { useRoomEventReaders } from '../../hooks/useRoomEventReaders';
|
import { useRoomEventReaders } from '../../hooks/useRoomEventReaders';
|
||||||
import { EventReaders } from '../../components/event-readers';
|
import { EventReaders } from '../../components/event-readers';
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export const RoomViewFollowing = as<'div', RoomViewFollowingProps>(
|
||||||
({ className, room, ...props }, ref) => {
|
({ className, room, ...props }, ref) => {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const latestEvent = useRoomLatestEvent(room);
|
const latestEvent = useRoomLatestRenderedEvent(room);
|
||||||
const latestEventReaders = useRoomEventReaders(room, latestEvent?.getId());
|
const latestEventReaders = useRoomEventReaders(room, latestEvent?.getId());
|
||||||
const followingMembers = latestEventReaders
|
const followingMembers = latestEventReaders
|
||||||
.map((readerId) => room.getMember(readerId))
|
.map((readerId) => room.getMember(readerId))
|
||||||
|
|
|
@ -22,3 +22,6 @@ export const RoomViewTyping = style([
|
||||||
animation: `${SlideUpAnime} 100ms ease-in-out`,
|
animation: `${SlideUpAnime} 100ms ease-in-out`,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
export const TypingText = style({
|
||||||
|
flexGrow: 1,
|
||||||
|
});
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Box, Text, as } from 'folds';
|
import { Box, Icon, IconButton, Icons, Text, as } from 'folds';
|
||||||
import { Room } from 'matrix-js-sdk';
|
import { Room } from 'matrix-js-sdk';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
import { roomIdToTypingMembersAtom, selectRoomTypingMembersAtom } from '../../state/typingMembers';
|
import { roomIdToTypingMembersAtom, selectRoomTypingMembersAtom } from '../../state/typingMembers';
|
||||||
import { TypingIndicator } from '../../components/typing-indicator';
|
import { TypingIndicator } from '../../components/typing-indicator';
|
||||||
import { getMemberDisplayName } from '../../utils/room';
|
import { getMemberDisplayName } from '../../utils/room';
|
||||||
|
@ -15,6 +15,7 @@ export type RoomViewTypingProps = {
|
||||||
};
|
};
|
||||||
export const RoomViewTyping = as<'div', RoomViewTypingProps>(
|
export const RoomViewTyping = as<'div', RoomViewTypingProps>(
|
||||||
({ className, room, ...props }, ref) => {
|
({ className, room, ...props }, ref) => {
|
||||||
|
const setTypingMembers = useSetAtom(roomIdToTypingMembersAtom);
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const typingMembers = useAtomValue(
|
const typingMembers = useAtomValue(
|
||||||
useMemo(() => selectRoomTypingMembersAtom(room.roomId, roomIdToTypingMembersAtom), [room])
|
useMemo(() => selectRoomTypingMembersAtom(room.roomId, roomIdToTypingMembersAtom), [room])
|
||||||
|
@ -29,6 +30,18 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDropAll = () => {
|
||||||
|
// some homeserver does not timeout typing status
|
||||||
|
// we have given option so user can drop their typing status
|
||||||
|
typingMembers.forEach((member) =>
|
||||||
|
setTypingMembers({
|
||||||
|
type: 'DELETE',
|
||||||
|
roomId: room.roomId,
|
||||||
|
member,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
className={classNames(css.RoomViewTyping, className)}
|
className={classNames(css.RoomViewTyping, className)}
|
||||||
|
@ -38,7 +51,7 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>(
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<TypingIndicator />
|
<TypingIndicator />
|
||||||
<Text size="T300" truncate>
|
<Text className={css.TypingText} size="T300" truncate>
|
||||||
{typingNames.length === 1 && (
|
{typingNames.length === 1 && (
|
||||||
<>
|
<>
|
||||||
<b>{typingNames[0]}</b>
|
<b>{typingNames[0]}</b>
|
||||||
|
@ -96,6 +109,9 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>(
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
|
<IconButton title="Drop Typing Status" size="300" radii="Pill" onClick={handleDropAll}>
|
||||||
|
<Icon size="50" src={Icons.Cross} />
|
||||||
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ function ReadTextFile({ body, mimeType, url, encInfo }: Omit<FileContentProps, '
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal size="500">
|
<Modal size="500" onContextMenu={(evt: any) => evt.stopPropagation()}>
|
||||||
<TextViewer
|
<TextViewer
|
||||||
name={body}
|
name={body}
|
||||||
text={textState.data}
|
text={textState.data}
|
||||||
|
@ -159,7 +159,7 @@ function ReadPdfFile({ body, mimeType, url, encInfo }: Omit<FileContentProps, 'i
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal size="500">
|
<Modal size="500" onContextMenu={(evt: any) => evt.stopPropagation()}>
|
||||||
<PdfViewer
|
<PdfViewer
|
||||||
name={body}
|
name={body}
|
||||||
src={pdfState.data}
|
src={pdfState.data}
|
||||||
|
|
|
@ -81,7 +81,7 @@ export const ImageContent = as<'div', ImageContentProps>(
|
||||||
clickOutsideDeactivates: true,
|
clickOutsideDeactivates: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal size="500">
|
<Modal size="500" onContextMenu={(evt: any) => evt.stopPropagation()}>
|
||||||
<ImageViewer
|
<ImageViewer
|
||||||
src={srcState.data}
|
src={srcState.data}
|
||||||
alt={body}
|
alt={body}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import { encryptFile, getImageInfo, getThumbnailContent, getVideoInfo } from '../../utils/matrix';
|
import { encryptFile, getImageInfo, getThumbnailContent, getVideoInfo } from '../../utils/matrix';
|
||||||
import { TUploadItem } from '../../state/roomInputDrafts';
|
import { TUploadItem } from '../../state/roomInputDrafts';
|
||||||
import { encodeBlurHash } from '../../utils/blurHash';
|
import { encodeBlurHash } from '../../utils/blurHash';
|
||||||
|
import { scaleYDimension } from '../../utils/common';
|
||||||
|
|
||||||
const generateThumbnailContent = async (
|
const generateThumbnailContent = async (
|
||||||
mx: MatrixClient,
|
mx: MatrixClient,
|
||||||
|
@ -52,7 +53,7 @@ export const getImageMsgContent = async (
|
||||||
body: file.name,
|
body: file.name,
|
||||||
};
|
};
|
||||||
if (imgEl) {
|
if (imgEl) {
|
||||||
const blurHash = encodeBlurHash(imgEl);
|
const blurHash = encodeBlurHash(imgEl, 512, scaleYDimension(imgEl.width, 512, imgEl.height));
|
||||||
const [thumbError, thumbContent] = await to(
|
const [thumbError, thumbContent] = await to(
|
||||||
generateThumbnailContent(
|
generateThumbnailContent(
|
||||||
mx,
|
mx,
|
||||||
|
@ -107,7 +108,11 @@ export const getVideoMsgContent = async (
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (thumbContent && thumbContent.thumbnail_info) {
|
if (thumbContent && thumbContent.thumbnail_info) {
|
||||||
thumbContent.thumbnail_info[MATRIX_BLUR_HASH_PROPERTY_NAME] = encodeBlurHash(videoEl);
|
thumbContent.thumbnail_info[MATRIX_BLUR_HASH_PROPERTY_NAME] = encodeBlurHash(
|
||||||
|
videoEl,
|
||||||
|
512,
|
||||||
|
scaleYDimension(videoEl.videoWidth, 512, videoEl.videoHeight)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (thumbError) console.warn(thumbError);
|
if (thumbError) console.warn(thumbError);
|
||||||
content.info = {
|
content.info = {
|
||||||
|
|
|
@ -99,10 +99,9 @@ export const ReactionViewer = as<'div', ReactionViewerProps>(
|
||||||
const senderId = mEvent.getSender();
|
const senderId = mEvent.getSender();
|
||||||
if (!senderId) return null;
|
if (!senderId) return null;
|
||||||
const member = room.getMember(senderId);
|
const member = room.getMember(senderId);
|
||||||
if (!member) return null;
|
const name = (member ? getName(member) : getMxIdLocalPart(senderId)) ?? senderId;
|
||||||
const name = getName(member);
|
|
||||||
|
|
||||||
const avatarUrl = member.getAvatarUrl(
|
const avatarUrl = member?.getAvatarUrl(
|
||||||
mx.baseUrl,
|
mx.baseUrl,
|
||||||
100,
|
100,
|
||||||
100,
|
100,
|
||||||
|
@ -113,12 +112,12 @@ export const ReactionViewer = as<'div', ReactionViewerProps>(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={member.userId}
|
key={senderId}
|
||||||
style={{ padding: `0 ${config.space.S200}` }}
|
style={{ padding: `0 ${config.space.S200}` }}
|
||||||
radii="400"
|
radii="400"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
requestClose();
|
requestClose();
|
||||||
openProfileViewer(member.userId, room.roomId);
|
openProfileViewer(senderId, room.roomId);
|
||||||
}}
|
}}
|
||||||
before={
|
before={
|
||||||
<Avatar size="200">
|
<Avatar size="200">
|
||||||
|
@ -127,7 +126,7 @@ export const ReactionViewer = as<'div', ReactionViewerProps>(
|
||||||
) : (
|
) : (
|
||||||
<AvatarFallback
|
<AvatarFallback
|
||||||
style={{
|
style={{
|
||||||
background: colorMXID(member.userId),
|
background: colorMXID(senderId),
|
||||||
color: 'white',
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import appDispatcher from '../dispatcher';
|
||||||
|
|
||||||
import cons from './cons';
|
import cons from './cons';
|
||||||
import { darkTheme, butterTheme, silverTheme } from '../../colors.css';
|
import { darkTheme, butterTheme, silverTheme } from '../../colors.css';
|
||||||
|
import { onLightFontWeight, onDarkFontWeight } from '../../config.css';
|
||||||
|
|
||||||
function getSettings() {
|
function getSettings() {
|
||||||
const settings = localStorage.getItem('settings');
|
const settings = localStorage.getItem('settings');
|
||||||
|
@ -23,6 +24,7 @@ class Settings extends EventEmitter {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.themeClasses = [lightTheme, silverTheme, darkTheme, butterTheme];
|
this.themeClasses = [lightTheme, silverTheme, darkTheme, butterTheme];
|
||||||
|
this.fontWeightClasses = [onLightFontWeight, onLightFontWeight, onDarkFontWeight, onDarkFontWeight]
|
||||||
this.themes = ['', 'silver-theme', 'dark-theme', 'butter-theme'];
|
this.themes = ['', 'silver-theme', 'dark-theme', 'butter-theme'];
|
||||||
this.themeIndex = this.getThemeIndex();
|
this.themeIndex = this.getThemeIndex();
|
||||||
|
|
||||||
|
@ -59,6 +61,7 @@ class Settings extends EventEmitter {
|
||||||
this.themes.forEach((themeName, index) => {
|
this.themes.forEach((themeName, index) => {
|
||||||
if (themeName !== '') document.body.classList.remove(themeName);
|
if (themeName !== '') document.body.classList.remove(themeName);
|
||||||
document.body.classList.remove(this.themeClasses[index]);
|
document.body.classList.remove(this.themeClasses[index]);
|
||||||
|
document.body.classList.remove(this.fontWeightClasses[index]);
|
||||||
document.body.classList.remove('prism-light')
|
document.body.classList.remove('prism-light')
|
||||||
document.body.classList.remove('prism-dark')
|
document.body.classList.remove('prism-dark')
|
||||||
});
|
});
|
||||||
|
@ -71,6 +74,7 @@ class Settings extends EventEmitter {
|
||||||
if (this.themes[themeIndex] === undefined) return
|
if (this.themes[themeIndex] === undefined) return
|
||||||
if (this.themes[themeIndex]) document.body.classList.add(this.themes[themeIndex]);
|
if (this.themes[themeIndex]) document.body.classList.add(this.themes[themeIndex]);
|
||||||
document.body.classList.add(this.themeClasses[themeIndex]);
|
document.body.classList.add(this.themeClasses[themeIndex]);
|
||||||
|
document.body.classList.add(this.fontWeightClasses[themeIndex]);
|
||||||
document.body.classList.add(themeIndex < 2 ? 'prism-light' : 'prism-dark');
|
document.body.classList.add(themeIndex < 2 ? 'prism-light' : 'prism-dark');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
src/config.css.ts
Normal file
26
src/config.css.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { createTheme } from '@vanilla-extract/css';
|
||||||
|
import { config } from 'folds';
|
||||||
|
|
||||||
|
export const onLightFontWeight = createTheme(config.fontWeight, {
|
||||||
|
W100: '100',
|
||||||
|
W200: '200',
|
||||||
|
W300: '300',
|
||||||
|
W400: '420',
|
||||||
|
W500: '500',
|
||||||
|
W600: '600',
|
||||||
|
W700: '700',
|
||||||
|
W800: '800',
|
||||||
|
W900: '900',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const onDarkFontWeight = createTheme(config.fontWeight, {
|
||||||
|
W100: '100',
|
||||||
|
W200: '200',
|
||||||
|
W300: '300',
|
||||||
|
W400: '350',
|
||||||
|
W500: '450',
|
||||||
|
W600: '550',
|
||||||
|
W700: '650',
|
||||||
|
W800: '750',
|
||||||
|
W900: '850',
|
||||||
|
});
|
|
@ -1,5 +0,0 @@
|
||||||
import '@fontsource/roboto/300.css';
|
|
||||||
import '@fontsource/roboto/400.css';
|
|
||||||
import '@fontsource/roboto/500.css';
|
|
||||||
import '@fontsource/roboto/700.css';
|
|
||||||
import '@fontsource/inter/variable.css';
|
|
|
@ -8,7 +8,6 @@ import { configClass, varsClass } from 'folds';
|
||||||
|
|
||||||
enableMapSet();
|
enableMapSet();
|
||||||
|
|
||||||
import './font';
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
import settings from './client/state/settings';
|
import settings from './client/state/settings';
|
||||||
|
|
|
@ -158,7 +158,7 @@
|
||||||
|
|
||||||
/* font-weight */
|
/* font-weight */
|
||||||
--fw-light: 300;
|
--fw-light: 300;
|
||||||
--fw-normal: 400;
|
--fw-normal: 420;
|
||||||
--fw-medium: 500;
|
--fw-medium: 500;
|
||||||
--fw-bold: 700;
|
--fw-bold: 700;
|
||||||
|
|
||||||
|
@ -193,8 +193,8 @@
|
||||||
--fluid-slide-up: cubic-bezier(0.13, 0.56, 0.25, 0.99);
|
--fluid-slide-up: cubic-bezier(0.13, 0.56, 0.25, 0.99);
|
||||||
|
|
||||||
--font-emoji: 'Twemoji';
|
--font-emoji: 'Twemoji';
|
||||||
--font-primary: 'Roboto', var(--font-emoji), sans-serif;
|
--font-primary: 'InterVariable', var(--font-emoji), sans-serif;
|
||||||
--font-secondary: 'Roboto', var(--font-emoji), sans-serif;
|
--font-secondary: 'InterVariable', var(--font-emoji), sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.silver-theme {
|
.silver-theme {
|
||||||
|
@ -291,8 +291,10 @@
|
||||||
|
|
||||||
/* override normal font weight for dark mode */
|
/* override normal font weight for dark mode */
|
||||||
--fw-normal: 350;
|
--fw-normal: 350;
|
||||||
|
--fw-medium: 450;
|
||||||
|
--fw-bold: 550;
|
||||||
|
|
||||||
--font-secondary: 'InterVariable', 'Roboto', var(--font-emoji), sans-serif;
|
--font-secondary: 'InterVariable', var(--font-emoji), sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.butter-theme {
|
.butter-theme {
|
||||||
|
|
Loading…
Reference in a new issue