This commit is contained in:
WhkSoft committed 2023-03-10 12:49:55 +08:00
1 parent 3b7f71b573
commit a41440857d
22 files changed
+477 -35

No files matched your search

+1
View File
@@ -400,6 +400,7 @@
& pre {
width: fit-content;
max-width: 100%;
direction: initial;
@include scrollbar.scroll;
@include scrollbar.scroll__h;
@include scrollbar.scroll--auto-hide;
+1 -2
View File
@@ -80,8 +80,7 @@ function RoomEmojis({ roomId }) {
const { usablePacks, createPack, deletePack } = useRoomPacks(room);
const myPowerlevel = room.getMember(mx.getUserId())?.powerLevel || 0;
const canChange = room.currentState.hasSufficientPowerLevelFor('state_default', myPowerlevel);
const canChange = room.currentState.maySendStateEvent('im.ponies.room_emote', mx.getUserId());
const handlePackCreate = (e) => {
e.preventDefault();
@@ -0,0 +1,66 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import initMatrix from '../../../client/initMatrix';
import Button from '../../atoms/button/Button';
import Text from '../../atoms/text/Text';
import './RoomTimezone.scss';
function RoomTimezone({ roomId }) {
const mx = initMatrix.matrixClient;
const room = mx.getRoom(roomId);
const userId = mx.getUserId();
const timezoneEventId = room.currentState.getStateEvents('in.cinny.shared_timezone', userId)?.event?.content?.user_timezone_event;
const timezoneEvent = room.findEventById(timezoneEventId);
const timezoneContent = timezoneEvent?.getContent();
const [timezone, setTimezone] = useState(timezoneContent?.user_timezone);
const clearTimezone = () => {
const eventKey = room.currentState.getStateEvents('in.cinny.shared_timezone', userId)?.event?.content?.user_timezone_event;
mx.redactEvent(roomId, eventKey);
mx.sendStateEvent(roomId, 'in.cinny.shared_timezone', { }, userId);
setTimezone(null);
};
const shareTimezone = () => {
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
setTimezone(userTimezone);
const content = {
user_timezone: userTimezone,
msgtype: 'in.cinny.share_timezone',
};
mx.sendEvent(roomId, 'in.cinny.share_timezone', content).then((event) => {
// Append the shared timezone event to the room state
mx.sendStateEvent(roomId, 'in.cinny.shared_timezone', { user_timezone_event: event.event_id }, userId);
});
};
return (
<div className="room-timezone__content">
<Text className="room-timezone__message">Share your timezone</Text>
<Text className="room-timezone__message" variant="b3">
Sharing your timezone will allow other users in this room to see your local time.
Keep in mind that sharing your timezone will share your approximate location.
</Text>
<Text className="room-timezone__message" variant="b2">
{timezone ? `Currently shared timezone: ${timezone}` : 'You are not currently sharing a timezone'}
</Text>
<Button onClick={() => shareTimezone()} variant={timezone ? 'surface' : 'danger'}>
{timezone ? 'Update Timezone' : 'Share Timezone'}
</Button>
<Button onClick={() => clearTimezone()} disabled={!timezone}>
Clear Shared Timezone
</Button>
</div>
);
}
RoomTimezone.propTypes = {
roomId: PropTypes.string.isRequired,
};
export default RoomTimezone;
@@ -0,0 +1,84 @@
@use '../../partials/flex';
@use '../../partials/dir';
@use '../../partials/text';
.room-timezone {
&__message,
& .setting-tile {
margin: var(--sp-tight) var(--sp-normal);
}
& .setting-tile {
margin-bottom: var(--sp-loose);
}
&__alias-item {
padding: var(--sp-extra-tight) var(--sp-normal);
@extend .cp-fx__row--s-c;
&.checkbox {
@include dir.side(margin, 0 , var(--sp-tight));
}
& .text {
@extend .cp-fx__item-one;
@extend .cp-txt__ellipsis;
color: var(--tc-surface-high);
span {
margin: 0 var(--sp-extra-tight);
padding: 0 var(--sp-ultra-tight);
color: var(--bg-surface);
background-color: var(--tc-surface-low);
border-radius: 4px;
}
}
}
&__item-btns {
@include dir.side(margin, 48px, 0);
& button {
padding: var(--sp-ultra-tight) var(--sp-tight);
margin-bottom: var(--sp-tight);
@include dir.side(margin, 0, var(--sp-tight));
}
}
&__content {
margin-bottom: var(--sp-normal);
& .checkbox {
@include dir.side(margin, 0, var(--sp-tight));
min-width: 20px;
}
& > button {
margin: 0 var(--sp-normal);
}
}
&__form {
padding: var(--sp-normal);
padding-top: 0;
display: flex;
&-label {
padding: var(--sp-normal) var(--sp-normal) var(--sp-ultra-tight);
}
}
&__input-wrapper {
display: flex;
@extend .cp-fx__item-one;
@include dir.side(margin, 0, var(--sp-tight));
& .input-container {
@extend .cp-fx__item-one;
}
}
&__input-status {
padding: 0 var(--sp-normal);
}
&__valid {
color: var(--tc-positive-high);
padding-bottom: var(--sp-normal);
}
&__invalid {
color: var(--tc-danger-high);
padding-bottom: var(--sp-normal);
}
}
+5 -4
View File
@@ -25,8 +25,9 @@ function Selector({
const noti = initMatrix.notifications;
const room = mx.getRoom(roomId);
let imageSrc = room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
if (imageSrc === null) imageSrc = room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop') || null;
const imageSrc = (isDM
? room.getAvatarFallbackMember()?.getAvatarUrl(mx.baseUrl, 24, 24, 'crop')
: room.getAvatarUrl(mx.baseUrl, 24, 24, 'crop')) || null;
const isMuted = noti.getNotiType(roomId) === cons.notifs.MUTE;
@@ -57,8 +58,8 @@ function Selector({
key={roomId}
name={room.name}
roomId={roomId}
imageSrc={isDM ? imageSrc : null}
iconSrc={isDM ? null : joinRuleToIconSrc(room.getJoinRule(), room.isSpaceRoom())}
imageSrc={imageSrc}
iconSrc={imageSrc ? null : joinRuleToIconSrc(room.getJoinRule(), room.isSpaceRoom())}
isSelected={navigation.selectedRoomId === roomId}
isMuted={isMuted}
isUnread={!isMuted && noti.hasNoti(roomId)}
+89
View File
@@ -0,0 +1,89 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import initMatrix from '../../../client/initMatrix';
import Text from '../../atoms/text/Text';
import './RoomBanner.scss';
import RawIcon from '../../atoms/system-icons/RawIcon';
import RecentClockIC from '../../../../public/res/ic/outlined/recent-clock.svg';
import { twemojify } from '../../../util/twemojify';
import { getUsernameOfRoomMember } from '../../../util/matrixUtil';
function RoomBanner({ roomId }) {
const mx = initMatrix.matrixClient;
const isDM = initMatrix.roomList.directs.has(roomId);
const room = mx.getRoom(roomId);
const [time, setTime] = useState(null);
let partner = null;
if (isDM) {
partner = room.getAvatarFallbackMember();
}
const timezoneEventId = room.currentState.getStateEvents('in.cinny.shared_timezone', partner?.userId)?.event?.content?.user_timezone_event;
const timezoneEvent = room.findEventById(timezoneEventId);
let timezoneContent = null;
const updateTime = () => {
if (isDM) {
const timezone = timezoneContent?.user_timezone;
const date = new Date();
try {
const partnerLocalTime = date.toLocaleTimeString([], { timeZone: timezone, hour: '2-digit', minute: '2-digit' });
const userTime = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
// if the partner time and user time are the same, we wont show the time
if (userTime !== partnerLocalTime) { setTime(`${getUsernameOfRoomMember(partner)}'s local time is: ${partnerLocalTime}`); }
} catch {
setTime(null);
}
}
};
if (timezoneEvent) {
if (!timezoneEvent.getClearContent()) {
timezoneEvent.attemptDecryption(mx.crypto);
}
timezoneContent = timezoneEvent.getContent();
timezoneEvent.getDecryptionPromise()?.then(() => {
timezoneContent = timezoneEvent.getContent();
updateTime();
});
}
useEffect(() => {
updateTime();
const interval = setInterval(() => updateTime(), 20000);
return () => {
setTime(null);
clearInterval(interval);
};
}, [room]);
return (
<div>
{ time ? (
<div className="room-view__banner">
<RawIcon src={RecentClockIC} size="small" />
<Text>
{ twemojify(time) }
</Text>
</div>
)
: null }
</div>
);
}
RoomBanner.propTypes = {
roomId: PropTypes.string.isRequired,
};
export default RoomBanner;
+28
View File
@@ -0,0 +1,28 @@
@use '../../partials/flex';
@use '../../partials/text';
@use '../../partials/dir';
.room-view {
&__banner {
background-color: var(--bg-surface-low);
width: calc(100% - var(--sp-extra-loose));
padding: var(--sp-ultra-tight);
border-radius: var(--bo-radius);
align-items: center;
position: relative;
@include dir.prop(left, var(--sp-normal), unset);
@include dir.prop(right, unset, var(--sp-normal));
display: flex;
z-index: 999;
transform: translate(0%, 10%);
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 20%);
& .ic-raw {
opacity: 0.4;
margin: 0 var(--sp-extra-tight);
}
}
}
+5
View File
@@ -39,6 +39,7 @@ import ChevronTopIC from '../../../../public/res/ic/outlined/chevron-top.svg';
import { useForceUpdate } from '../../hooks/useForceUpdate';
import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import RoomTimezone from '../../molecules/room-timezone/RoomTimeZone';
const tabText = {
GENERAL: 'General',
@@ -120,6 +121,10 @@ function GeneralSettings({ roomId }) {
<MenuHeader>Room addresses</MenuHeader>
<RoomAliases roomId={roomId} />
</div>
<div className="room-settings__card">
<MenuHeader>Timezones</MenuHeader>
<RoomTimezone roomId={roomId} />
</div>
</>
);
}
+2
View File
@@ -12,6 +12,7 @@ import RoomViewContent from './RoomViewContent';
import RoomViewFloating from './RoomViewFloating';
import RoomViewInput from './RoomViewInput';
import RoomViewCmdBar from './RoomViewCmdBar';
import RoomBanner from './RoomBanner';
const viewEvent = new EventEmitter();
@@ -42,6 +43,7 @@ function RoomView({ roomTimeline, eventId }) {
return (
<div className="room-view" ref={roomViewRef}>
<RoomViewHeader roomId={roomId} />
<RoomBanner roomId={roomId} />
<div className="room-view__content-wrapper">
<div className="room-view__scrollable">
<RoomViewContent
+9 -3
View File
@@ -8,8 +8,7 @@ import { blurOnBubbling } from '../../atoms/button/script';
import initMatrix from '../../../client/initMatrix';
import cons from '../../../client/state/cons';
import navigation from '../../../client/state/navigation';
import { toggleRoomSettings, openReusableContextMenu, openNavigation } from '../../../client/action/navigation';
import { togglePeopleDrawer } from '../../../client/action/settings';
import { toggleNavigation, toggleRoomSettings, openReusableContextMenu, openNavigation } from '../../../client/action/navigation';import { togglePeopleDrawer } from '../../../client/action/settings';
import colorMXID from '../../../util/colorMXID';
import { getEventCords } from '../../../util/common';
@@ -24,6 +23,7 @@ import RoomOptions from '../../molecules/room-options/RoomOptions';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import UserIC from '../../../../public/res/ic/outlined/user.svg';
import HomeIC from '../../../../public/res/ic/outlined/home.svg';
import VerticalMenuIC from '../../../../public/res/ic/outlined/vertical-menu.svg';
import BackArrowIC from '../../../../public/res/ic/outlined/chevron-left.svg';
@@ -76,11 +76,17 @@ function RoomViewHeader({ roomId }) {
return (
<Header>
<IconButton
src={BackArrowIC}
src={HomeIC}
className="room-header__back-btn"
tooltip="Return to navigation"
onClick={() => openNavigation()}
/>
<IconButton
src={BackArrowIC}
className="room-header__hide-navigation-btn"
tooltip="Hide Navigation"
onClick={() => toggleNavigation()}
/>
<button
ref={roomHeaderBtnRef}
className="room-header__btn"
@@ -45,3 +45,10 @@
display: none;
}
}
.room-header__hide-navigation-btn {
@include dir.side(margin, 0, var(--sp-tight));
@include screen.smallerThan(mobileBreakpoint) {
display: none;
}
}
+58 -6
View File
@@ -8,7 +8,7 @@ import settings from '../../../client/state/settings';
import navigation from '../../../client/state/navigation';
import {
toggleSystemTheme, toggleMarkdown, toggleMembershipEvents, toggleNickAvatarEvents,
toggleNotifications, toggleNotificationSounds, toggleSelfTranslate,
toggleNotifications, toggleNotificationSounds, toggleSelfTranslate, toggleReadReceipts, toggleHideNavigation
} from '../../../client/action/settings';
import { usePermission } from '../../hooks/usePermission';
@@ -48,7 +48,9 @@ import { confirmDialog } from '../../molecules/confirm-dialog/ConfirmDialog';
import '../../i18n.jsx'
let capabilities = {
privateReadReceipts: false,
}
function AppearanceSection() {
@@ -78,7 +80,7 @@ function AppearanceSection() {
return (
<div className="settings-appearance">
<div className="settings-appearance__card">
<MenuHeader>{t("translation.Organisms.Settings.theme.title")}</MenuHeader>
<MenuHeader>{t("Theme")}</MenuHeader>
<SettingTile
title="Follow system theme"
options={(
@@ -131,6 +133,16 @@ function AppearanceSection() {
)}
content={<Text variant="b3">Hide membership change messages from room timeline. (Join, Leave, Invite, Kick and Ban)</Text>}
/>
<SettingTile
title="Hide navigation by default"
options={(
<Toggle
isActive={settings.hideNavigation}
onToggle={() => { toggleHideNavigation(); updateState({}); }}
/>
)}
content={<Text variant="b3">Hide the navigation bar by default, when cinny is opened.</Text>}
/>
<SettingTile
title="Hide nick/avatar events"
options={(
@@ -272,6 +284,7 @@ function EmojiSection() {
}
function SecuritySection() {
const [, updateState] = useState({});
return (
<div className="settings-security">
<div className="settings-security__card">
@@ -301,6 +314,33 @@ function SecuritySection() {
)}
/>
</div>
<div className="settings-security__card">
<MenuHeader>Presence</MenuHeader>
<SettingTile
title="Send read receipts"
options={(
<Toggle
/** Always allow to switch receipts on. */
disabled={!capabilities.privateReadReceipts && settings.sendReadReceipts}
isActive={settings.sendReadReceipts}
onToggle={() => { toggleReadReceipts(); updateState({}); }}
/>
)}
content={(
<>
<Text variant="b3">
Let other people know what messages you read.
</Text>
{!capabilities.privateReadReceipts
&& (
<Text variant="b3">
Making your read receipts private requires a compatible homeserver.
</Text>
)}
</>
)}
/>
</div>
</div>
);
}
@@ -314,10 +354,11 @@ function AboutSection() {
<img width="60" height="60" src={CinnySVG} alt="Cinny logo" />
<div>
<Text variant="h2" weight="medium">
Cinny
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}`}</span>
Cinny Mod
<span className="text text-b3" style={{ margin: '0 var(--sp-extra-tight)' }}>{`v${cons.version}.52`}</span>
</Text>
<Text>Yet another matrix client</Text>
<Text>Modded By WHK with magic & love</Text>
<div className="settings-about__btns">
<Button onClick={() => window.open('https://github.com/ajbura/cinny')}>Source code</Button>
@@ -404,9 +445,20 @@ function useWindowToggle(setSelectedTab) {
return [isOpen, requestClose];
}
async function getCapabilities() {
const mx = initMatrix.matrixClient;
capabilities = {
privateReadReceipts: (await (Promise.all([
mx.doesServerSupportUnstableFeature('org.matrix.msc2285.stable'),
mx.isVersionSupported('v1.4')])
)).some((res) => res === true),
};
}
function Settings() {
const [selectedTab, setSelectedTab] = useState(tabItems[0]);
const [isOpen, requestClose] = useWindowToggle(setSelectedTab);
useEffect(getCapabilities, []);
const { t } = useTranslation();
const handleTabChange = (tabItem) => setSelectedTab(tabItem);
@@ -420,7 +472,7 @@ function Settings() {
<PopupWindow
isOpen={isOpen}
className="settings-window"
title={<Text variant="s1" weight="medium" primary>{t("translation.Organisms.Settings.title")}</Text>}
title={<Text variant="s1" weight="medium" primary>{t("Settings")}</Text>}
contentOptions={(
<>
<Button variant="danger" iconSrc={PowerIC} onClick={handleLogout}>
+14 -1
View File
@@ -15,6 +15,7 @@ import Windows from '../../organisms/pw/Windows';
import Dialogs from '../../organisms/pw/Dialogs';
import EmojiBoardOpener from '../../organisms/emoji-board/EmojiBoardOpener';
import settings from '../../../client/state/settings';
import initMatrix from '../../../client/initMatrix';
import navigation from '../../../client/state/navigation';
import cons from '../../../client/state/cons';
@@ -24,9 +25,11 @@ import VerticalMenuIC from '../../../../public/res/ic/outlined/vertical-menu.svg
function Client() {
const [isLoading, changeLoading] = useState(true);
const [roomSelected, selectRoom] = useState(false);
const [loadingMsg, setLoadingMsg] = useState('Heating up');
const [dragCounter, setDragCounter] = useState(0);
const classNameHidden = 'client__item-hidden';
const toggleHidden = 'toggle-hidden';
const navWrapperRef = useRef(null);
const roomWrapperRef = useRef(null);
@@ -34,19 +37,29 @@ function Client() {
function onRoomSelected() {
navWrapperRef.current?.classList.add(classNameHidden);
roomWrapperRef.current?.classList.remove(classNameHidden);
if (settings.hideNavigation) {
selectRoom(navigation.selectedRoomId);
navWrapperRef.current?.classList.add(toggleHidden);
}
}
function onNavigationSelected() {
navWrapperRef.current?.classList.remove(classNameHidden);
navWrapperRef.current?.classList.remove(toggleHidden);
roomWrapperRef.current?.classList.add(classNameHidden);
}
function onNavigationToggled() {
navWrapperRef.current?.classList.toggle(toggleHidden);
}
useEffect(() => {
navigation.on(cons.events.navigation.ROOM_SELECTED, onRoomSelected);
navigation.on(cons.events.navigation.NAVIGATION_OPENED, onNavigationSelected);
navigation.on(cons.events.navigation.NAVIGATION_TOGGLED, onNavigationToggled);
return (() => {
navigation.removeListener(cons.events.navigation.ROOM_SELECTED, onRoomSelected);
navigation.removeListener(cons.events.navigation.NAVIGATION_OPENED, onNavigationSelected);
navigation.removeListener(cons.events.navigation.NAVIGATION_TOGGLED, onNavigationToggled);
});
}, []);
@@ -165,7 +178,7 @@ function Client() {
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
<div className="navigation__wrapper" ref={navWrapperRef}>
<div className={`navigation__wrapper ${settings.hideNavigation && roomSelected ? toggleHidden : ''}`} ref={navWrapperRef}>
<Navigation />
</div>
<div className={`room__wrapper ${classNameHidden}`} ref={roomWrapperRef}>
+4
View File
@@ -13,6 +13,10 @@
}
}
.toggle-hidden {
display: none;
}
.room__wrapper {
flex: 1;
min-width: 0;
+6
View File
@@ -30,6 +30,12 @@ export function openNavigation() {
});
}
export function toggleNavigation() {
appDispatcher.dispatch({
type: cons.actions.navigation.TOGGLE_NAVIGATION,
});
}
export function openSpaceSettings(roomId, tabText) {
appDispatcher.dispatch({
type: cons.actions.navigation.OPEN_SPACE_SETTINGS,
+8 -1
View File
@@ -1,4 +1,10 @@
import initMatrix from '../initMatrix';
import settings from '../state/settings';
const ReceiptType = {
Read: 'm.read',
ReadPrivate: 'm.read.private',
};
// eslint-disable-next-line import/prefer-default-export
export async function markAsRead(roomId) {
@@ -22,5 +28,6 @@ export async function markAsRead(roomId) {
const latestEvent = getLatestValidEvent();
if (latestEvent === null) return;
await mx.sendReadReceipt(latestEvent);
const receiptType = settings.sendReadReceipts ? ReceiptType.Read : ReceiptType.ReadPrivate;
await mx.sendReadReceipt(latestEvent, receiptType);
}
+12
View File
@@ -25,6 +25,12 @@ export function toggleMembershipEvents() {
});
}
export function toggleHideNavigation() {
appDispatcher.dispatch({
type: cons.actions.settings.TOGGLE_HIDE_NAVIGATION,
});
}
export function toggleNickAvatarEvents() {
appDispatcher.dispatch({
type: cons.actions.settings.TOGGLE_NICKAVATAR_EVENT,
@@ -43,6 +49,12 @@ export function toggleNotifications() {
});
}
export function toggleReadReceipts() {
appDispatcher.dispatch({
type: cons.actions.settings.TOGGLE_READ_RECEIPTS,
});
}
export function toggleNotificationSounds() {
appDispatcher.dispatch({
type: cons.actions.settings.TOGGLE_NOTIFICATION_SOUNDS,
+12 -10
View File
@@ -383,17 +383,19 @@ class Notifications extends EventEmitter {
});
this.matrixClient.on('Room.receipt', (mEvent, room) => {
if (mEvent.getType() === 'm.receipt') {
if (room.isSpaceRoom()) return;
const content = mEvent.getContent();
const readedEventId = Object.keys(content)[0];
const readerUserId = Object.keys(content[readedEventId]['m.read'])[0];
if (readerUserId !== this.matrixClient.getUserId()) return;
if (mEvent.getType() !== 'm.receipt' || room.isSpaceRoom()) return;
const content = mEvent.getContent();
const userId = this.matrixClient.getUserId();
this.deleteNoti(room.roomId);
this._deletePopupRoomNotis(room.roomId);
}
Object.keys(content).forEach((eventId) => {
Object.entries(content[eventId]).forEach(([receiptType, receipt]) => {
if (!cons.supportReceiptTypes.includes(receiptType)) return;
if (Object.keys(receipt || {}).includes(userId)) {
this.deleteNoti(room.roomId);
this._deletePopupRoomNotis(room.roomId);
}
});
});
});
this.matrixClient.on('Room.myMembership', (room, membership) => {
+9
View File
@@ -19,6 +19,10 @@ const cons = {
'm.room.member',
'm.sticker',
],
supportReceiptTypes: [
'm.read',
'm.read.private',
],
notifs: {
DEFAULT: 'default',
ALL_MESSAGES: 'all_messages',
@@ -55,6 +59,7 @@ const cons = {
CLICK_REPLY_TO: 'CLICK_REPLY_TO',
OPEN_SEARCH: 'OPEN_SEARCH',
OPEN_REUSABLE_CONTEXT_MENU: 'OPEN_REUSABLE_CONTEXT_MENU',
TOGGLE_NAVIGATION: 'TOGGLE_NAVIGATION',
OPEN_NAVIGATION: 'OPEN_NAVIGATION',
OPEN_REUSABLE_DIALOG: 'OPEN_REUSABLE_DIALOG',
OPEN_EMOJI_VERIFICATION: 'OPEN_EMOJI_VERIFICATION',
@@ -76,10 +81,12 @@ const cons = {
TOGGLE_MARKDOWN: 'TOGGLE_MARKDOWN',
TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER',
TOGGLE_MEMBERSHIP_EVENT: 'TOGGLE_MEMBERSHIP_EVENT',
TOGGLE_HIDE_NAVIGATION: 'TOGGLE_HIDE_NAVIGATION',
TOGGLE_NICKAVATAR_EVENT: 'TOGGLE_NICKAVATAR_EVENT',
TOGGLE_SELF_TRANSLATE: 'TOGGLE_SELF_TRANSLATE',
TOGGLE_NOTIFICATIONS: 'TOGGLE_NOTIFICATIONS',
TOGGLE_NOTIFICATION_SOUNDS: 'TOGGLE_NOTIFICATION_SOUNDS',
TOGGLE_READ_RECEIPTS: 'TOGGLE_READ_RECEIPTS',
},
},
events: {
@@ -91,6 +98,7 @@ const cons = {
SPACE_MANAGE_OPENED: 'SPACE_MANAGE_OPENED',
SPACE_ADDEXISTING_OPENED: 'SPACE_ADDEXISTING_OPENED',
ROOM_SETTINGS_TOGGLED: 'ROOM_SETTINGS_TOGGLED',
NAVIGATION_TOGGLED: 'NAVIGATIONS_TOGGLED',
SHORTCUT_SPACES_OPENED: 'SHORTCUT_SPACES_OPENED',
INVITE_LIST_OPENED: 'INVITE_LIST_OPENED',
PUBLIC_ROOMS_OPENED: 'PUBLIC_ROOMS_OPENED',
@@ -153,6 +161,7 @@ const cons = {
NICKAVATAR_EVENTS_TOGGLED: 'NICKAVATAR_EVENTS_TOGGLED',
NOTIFICATIONS_TOGGLED: 'NOTIFICATIONS_TOGGLED',
NOTIFICATION_SOUNDS_TOGGLED: 'NOTIFICATION_SOUNDS_TOGGLED',
READ_RECEIPTS_TOGGLED: 'READ_RECEIPTS_TOGGLED',
},
},
};
+3
View File
@@ -349,6 +349,9 @@ class Navigation extends EventEmitter {
[cons.actions.navigation.OPEN_NAVIGATION]: () => {
this.emit(cons.events.navigation.NAVIGATION_OPENED);
},
[cons.actions.navigation.TOGGLE_NAVIGATION]: () => {
this.emit(cons.events.navigation.NAVIGATION_TOGGLED, this.navigationVisible);
},
[cons.actions.navigation.OPEN_EMOJIBOARD]: () => {
this.emit(
cons.events.navigation.EMOJIBOARD_OPENED,
+30
View File
@@ -28,12 +28,14 @@ class Settings extends EventEmitter {
this.useSystemTheme = this.getUseSystemTheme();
this.isMarkdown = this.getIsMarkdown();
this.hideNavigation = this.getHideNavigation();
this.isPeopleDrawer = this.getIsPeopleDrawer();
this.hideMembershipEvents = this.getHideMembershipEvents();
this.hideNickAvatarEvents = this.getHideNickAvatarEvents();
this.enableSelfTranslate = this.getEnableSelfTranslate();
this._showNotifications = this.getShowNotifications();
this.isNotificationSounds = this.getIsNotificationSounds();
this.sendReadReceipts = this.getSendReadReceipts();
this.isTouchScreenDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0);
}
@@ -135,6 +137,15 @@ class Settings extends EventEmitter {
return settings.useSystemTheme;
}
getHideNavigation() {
if (typeof this.hideNavigation === 'boolean') return this.hideNavigation;
const settings = getSettings();
if (settings === null) return false;
if (typeof settings.hideNavigation === 'undefined') return false;
return settings.hideNavigation;
}
getIsMarkdown() {
if (typeof this.isMarkdown === 'boolean') return this.isMarkdown;
@@ -203,6 +214,15 @@ class Settings extends EventEmitter {
return settings.isNotificationSounds;
}
getSendReadReceipts() {
if (typeof this.sendReadReceipts === 'boolean') return this.sendReadReceipts;
const settings = getSettings();
if (settings === null) return true;
if (typeof settings.sendReadReceipts === 'undefined') return true;
return settings.sendReadReceipts;
}
setter(action) {
const actions = {
[cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
@@ -223,6 +243,11 @@ class Settings extends EventEmitter {
setSettings('hideMembershipEvents', this.hideMembershipEvents);
this.emit(cons.events.settings.MEMBERSHIP_EVENTS_TOGGLED, this.hideMembershipEvents);
},
[cons.actions.settings.TOGGLE_HIDE_NAVIGATION]: () => {
this.hideNavigation = !this.hideNavigation;
setSettings('hideNavigation', this.hideNavigation);
this.emit(cons.events.settings.HIDE_NAVIGATION_TOGGLED, this.hideNavigation);
},
[cons.actions.settings.TOGGLE_NICKAVATAR_EVENT]: () => {
this.hideNickAvatarEvents = !this.hideNickAvatarEvents;
setSettings('hideNickAvatarEvents', this.hideNickAvatarEvents);
@@ -247,6 +272,11 @@ class Settings extends EventEmitter {
setSettings('isNotificationSounds', this.isNotificationSounds);
this.emit(cons.events.settings.NOTIFICATION_SOUNDS_TOGGLED, this.isNotificationSounds);
},
[cons.actions.settings.TOGGLE_READ_RECEIPTS]: () => {
this.sendReadReceipts = !this.sendReadReceipts;
setSettings('sendReadReceipts', this.sendReadReceipts);
this.emit(cons.events.settings.READ_RECEIPTS_TOGGLED, this.sendReadReceipts);
},
};
actions[action.type]?.();
+24 -8
View File
@@ -82,12 +82,22 @@ const plainRules = {
},
newline: {
...defaultRules.newline,
match: blockRegex(/^ *\n/),
plain: () => '\n',
html: () => '<br>',
},
paragraph: {
...defaultRules.paragraph,
plain: (node, output, state) => `${output(node.content, state)}\n\n`,
html: (node, output, state) => htmlTag('p', output(node.content, state)),
match: (source, state) => {
const endMatch = blockRegex(/^([^\n]*)\n*$/);
if (endMatch) {
state.end = true;
return endMatch(source, state);
}
return blockRegex(/^([^\n]*)\n?/)(source, state);
},
plain: (node, output, state) => `${output(node.content, state)}${state.end ? '' : '\n'}`,
html: (node, output, state) => `${output(node.content, state)}${state.end ? '' : '<br>'}`,
},
escape: {
...defaultRules.escape,
@@ -101,9 +111,7 @@ const plainRules = {
text: {
...defaultRules.text,
match: anyScopeRegex(/^[\s\S]+?(?=[^0-9A-Za-z\s\u00c0-\uffff]| *\n|\w+:\S|$)/),
plain: (node, _, state) => (state.kind === 'edit'
? node.content.replace(/(\*|_|!\[|\[|\|\||\$\$?)/g, '\\$1')
: node.content),
plain: (node) => node.content,
},
};
@@ -138,7 +146,15 @@ const markdownRules = {
},
blockQuote: {
...defaultRules.blockQuote,
plain: (node, output, state) => `> ${output(node.content, state).trim().replace(/\n/g, '\n> ')}\n\n`,
match: blockRegex(/^( *>[^\n]+(\n *>+[^\n]+)*\n?)+/),
parse: (capture, parse, state) => {
const content = capture[0].replace(/^ *> ?/gm, '');
return {
content: parse(`${content}`, state),
};
},
plain: (node, output, state) => `> ${output(node.content, state).trim().replace(/\n/g, '\n> ')}\n`,
html: (node, output, state) => htmlTag('blockquote', output(node.content, state)),
},
list: {
...defaultRules.list,
@@ -492,11 +508,11 @@ function render(content, state, plainOut, htmlOut) {
};
}
const plainParser = parserFor(plainRules);
const plainParser = parserFor(plainRules, { disableAutoBlockNewlines: true });
const plainPlainOut = outputFor(plainRules, 'plain');
const plainHtmlOut = outputFor(plainRules, 'html');
const mdParser = parserFor(markdownRules);
const mdParser = parserFor(markdownRules, { disableAutoBlockNewlines: true });
const mdPlainOut = outputFor(markdownRules, 'plain');
const mdHtmlOut = outputFor(markdownRules, 'html');