forked from mirror/cinny
Fix unread reset and notification settings (#1824)
* reset unread with client sync state change * fix notification toggle setting not working * revert formatOnSave vscode setting
This commit is contained in:
parent
e2228a18c1
commit
e6d6b0349e
9 changed files with 62 additions and 100 deletions
|
@ -61,4 +61,12 @@ module.exports = {
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
"@typescript-eslint/no-shadow": "error"
|
"@typescript-eslint/no-shadow": "error"
|
||||||
},
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.ts'],
|
||||||
|
rules: {
|
||||||
|
'no-undef': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
/* eslint-disable import/prefer-default-export */
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
export function usePermission(name, initial) {
|
|
||||||
const [state, setState] = useState(initial);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
let descriptor;
|
|
||||||
|
|
||||||
const update = () => setState(descriptor.state);
|
|
||||||
|
|
||||||
if (navigator.permissions?.query) {
|
|
||||||
navigator.permissions.query({ name }).then((_descriptor) => {
|
|
||||||
descriptor = _descriptor;
|
|
||||||
|
|
||||||
update();
|
|
||||||
descriptor.addEventListener('change', update);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (descriptor) descriptor.removeEventListener('change', update);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return [state, setState];
|
|
||||||
}
|
|
30
src/app/hooks/usePermission.ts
Normal file
30
src/app/hooks/usePermission.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export function usePermissionState(name: PermissionName, initialValue: PermissionState = 'prompt') {
|
||||||
|
const [permissionState, setPermissionState] = useState<PermissionState>(initialValue);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let permissionStatus: PermissionStatus;
|
||||||
|
|
||||||
|
function handlePermissionChange(this: PermissionStatus) {
|
||||||
|
setPermissionState(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.permissions
|
||||||
|
.query({ name })
|
||||||
|
.then((permStatus: PermissionStatus) => {
|
||||||
|
permissionStatus = permStatus;
|
||||||
|
handlePermissionChange.apply(permStatus);
|
||||||
|
permStatus.addEventListener("change", handlePermissionChange);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Silence error since FF doesn't support microphone permission
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
permissionStatus?.removeEventListener("change", handlePermissionChange);
|
||||||
|
};
|
||||||
|
}, [name]);
|
||||||
|
|
||||||
|
return permissionState;
|
||||||
|
}
|
|
@ -7,9 +7,8 @@ import settings from '../../../client/state/settings';
|
||||||
import navigation from '../../../client/state/navigation';
|
import navigation from '../../../client/state/navigation';
|
||||||
import {
|
import {
|
||||||
toggleSystemTheme,
|
toggleSystemTheme,
|
||||||
toggleNotifications, toggleNotificationSounds,
|
|
||||||
} from '../../../client/action/settings';
|
} from '../../../client/action/settings';
|
||||||
import { usePermission } from '../../hooks/usePermission';
|
import { usePermissionState } from '../../hooks/usePermission';
|
||||||
|
|
||||||
import Text from '../../atoms/text/Text';
|
import Text from '../../atoms/text/Text';
|
||||||
import IconButton from '../../atoms/button/IconButton';
|
import IconButton from '../../atoms/button/IconButton';
|
||||||
|
@ -230,23 +229,25 @@ function AppearanceSection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function NotificationsSection() {
|
function NotificationsSection() {
|
||||||
const [permission, setPermission] = usePermission('notifications', window.Notification?.permission);
|
const notifPermission = usePermissionState('notifications', window.Notification?.permission ?? "denied");
|
||||||
|
const [showNotifications, setShowNotifications] = useSetting(settingsAtom, 'showNotifications')
|
||||||
const [, updateState] = useState({});
|
const [isNotificationSounds, setIsNotificationSounds] = useSetting(settingsAtom, 'isNotificationSounds')
|
||||||
|
|
||||||
const renderOptions = () => {
|
const renderOptions = () => {
|
||||||
if (window.Notification === undefined) {
|
if (window.Notification === undefined) {
|
||||||
return <Text className="settings-notifications__not-supported">Not supported in this browser.</Text>;
|
return <Text className="settings-notifications__not-supported">Not supported in this browser.</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permission === 'granted') {
|
if (notifPermission === 'denied') {
|
||||||
|
return <Text>Permission Denied</Text>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifPermission === 'granted') {
|
||||||
return (
|
return (
|
||||||
<Toggle
|
<Toggle
|
||||||
isActive={settings._showNotifications}
|
isActive={showNotifications}
|
||||||
onToggle={() => {
|
onToggle={() => {
|
||||||
toggleNotifications();
|
setShowNotifications(!showNotifications);
|
||||||
setPermission(window.Notification?.permission);
|
|
||||||
updateState({});
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -255,7 +256,9 @@ function NotificationsSection() {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={() => window.Notification.requestPermission().then(setPermission)}
|
onClick={() => window.Notification.requestPermission().then(() => {
|
||||||
|
setShowNotifications(window.Notification?.permission === 'granted');
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
Request permission
|
Request permission
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -275,8 +278,8 @@ function NotificationsSection() {
|
||||||
title="Notification Sound"
|
title="Notification Sound"
|
||||||
options={(
|
options={(
|
||||||
<Toggle
|
<Toggle
|
||||||
isActive={settings.isNotificationSounds}
|
isActive={isNotificationSounds}
|
||||||
onToggle={() => { toggleNotificationSounds(); updateState({}); }}
|
onToggle={() => setIsNotificationSounds(!isNotificationSounds)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
content={<Text variant="b3">Play sound when new messages arrive.</Text>}
|
content={<Text variant="b3">Play sound when new messages arrive.</Text>}
|
||||||
|
|
|
@ -58,6 +58,7 @@ function InviteNotifications() {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [showNotifications] = useSetting(settingsAtom, 'showNotifications');
|
||||||
const [notificationSound] = useSetting(settingsAtom, 'isNotificationSounds');
|
const [notificationSound] = useSetting(settingsAtom, 'isNotificationSounds');
|
||||||
|
|
||||||
const notify = useCallback(
|
const notify = useCallback(
|
||||||
|
@ -84,7 +85,7 @@ function InviteNotifications() {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (invites.length > perviousInviteLen && mx.getSyncState() === 'SYNCING') {
|
if (invites.length > perviousInviteLen && mx.getSyncState() === 'SYNCING') {
|
||||||
if (Notification.permission === 'granted') {
|
if (showNotifications && Notification.permission === 'granted') {
|
||||||
notify(invites.length - perviousInviteLen);
|
notify(invites.length - perviousInviteLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +93,7 @@ function InviteNotifications() {
|
||||||
playSound();
|
playSound();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [mx, invites, perviousInviteLen, notificationSound, notify, playSound]);
|
}, [mx, invites, perviousInviteLen, showNotifications, notificationSound, notify, playSound]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// eslint-disable-next-line jsx-a11y/media-has-caption
|
// eslint-disable-next-line jsx-a11y/media-has-caption
|
||||||
|
|
|
@ -185,8 +185,11 @@ export const useBindRoomToUnreadAtom = (
|
||||||
useSyncState(
|
useSyncState(
|
||||||
mx,
|
mx,
|
||||||
useCallback(
|
useCallback(
|
||||||
(state) => {
|
(state, prevState) => {
|
||||||
if (state === SyncState.Prepared) {
|
if (
|
||||||
|
(state === SyncState.Prepared && prevState === null) ||
|
||||||
|
(state === SyncState.Syncing && prevState !== SyncState.Syncing)
|
||||||
|
) {
|
||||||
setUnreadAtom({
|
setUnreadAtom({
|
||||||
type: 'RESET',
|
type: 'RESET',
|
||||||
unreadInfos: getUnreadInfos(mx),
|
unreadInfos: getUnreadInfos(mx),
|
||||||
|
|
|
@ -30,15 +30,3 @@ export function toggleNickAvatarEvents() {
|
||||||
type: cons.actions.settings.TOGGLE_NICKAVATAR_EVENT,
|
type: cons.actions.settings.TOGGLE_NICKAVATAR_EVENT,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toggleNotifications() {
|
|
||||||
appDispatcher.dispatch({
|
|
||||||
type: cons.actions.settings.TOGGLE_NOTIFICATIONS,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toggleNotificationSounds() {
|
|
||||||
appDispatcher.dispatch({
|
|
||||||
type: cons.actions.settings.TOGGLE_NOTIFICATION_SOUNDS,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -52,8 +52,6 @@ const cons = {
|
||||||
TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER',
|
TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER',
|
||||||
TOGGLE_MEMBERSHIP_EVENT: 'TOGGLE_MEMBERSHIP_EVENT',
|
TOGGLE_MEMBERSHIP_EVENT: 'TOGGLE_MEMBERSHIP_EVENT',
|
||||||
TOGGLE_NICKAVATAR_EVENT: 'TOGGLE_NICKAVATAR_EVENT',
|
TOGGLE_NICKAVATAR_EVENT: 'TOGGLE_NICKAVATAR_EVENT',
|
||||||
TOGGLE_NOTIFICATIONS: 'TOGGLE_NOTIFICATIONS',
|
|
||||||
TOGGLE_NOTIFICATION_SOUNDS: 'TOGGLE_NOTIFICATION_SOUNDS',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
|
@ -81,8 +79,6 @@ const cons = {
|
||||||
PEOPLE_DRAWER_TOGGLED: 'PEOPLE_DRAWER_TOGGLED',
|
PEOPLE_DRAWER_TOGGLED: 'PEOPLE_DRAWER_TOGGLED',
|
||||||
MEMBERSHIP_EVENTS_TOGGLED: 'MEMBERSHIP_EVENTS_TOGGLED',
|
MEMBERSHIP_EVENTS_TOGGLED: 'MEMBERSHIP_EVENTS_TOGGLED',
|
||||||
NICKAVATAR_EVENTS_TOGGLED: 'NICKAVATAR_EVENTS_TOGGLED',
|
NICKAVATAR_EVENTS_TOGGLED: 'NICKAVATAR_EVENTS_TOGGLED',
|
||||||
NOTIFICATIONS_TOGGLED: 'NOTIFICATIONS_TOGGLED',
|
|
||||||
NOTIFICATION_SOUNDS_TOGGLED: 'NOTIFICATION_SOUNDS_TOGGLED',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,8 +33,6 @@ class Settings extends EventEmitter {
|
||||||
this.isPeopleDrawer = this.getIsPeopleDrawer();
|
this.isPeopleDrawer = this.getIsPeopleDrawer();
|
||||||
this.hideMembershipEvents = this.getHideMembershipEvents();
|
this.hideMembershipEvents = this.getHideMembershipEvents();
|
||||||
this.hideNickAvatarEvents = this.getHideNickAvatarEvents();
|
this.hideNickAvatarEvents = this.getHideNickAvatarEvents();
|
||||||
this._showNotifications = this.getShowNotifications();
|
|
||||||
this.isNotificationSounds = this.getIsNotificationSounds();
|
|
||||||
|
|
||||||
this.darkModeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
|
this.darkModeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
|
||||||
|
@ -137,29 +135,6 @@ class Settings extends EventEmitter {
|
||||||
return settings.isPeopleDrawer;
|
return settings.isPeopleDrawer;
|
||||||
}
|
}
|
||||||
|
|
||||||
get showNotifications() {
|
|
||||||
if (window.Notification?.permission !== 'granted') return false;
|
|
||||||
return this._showNotifications;
|
|
||||||
}
|
|
||||||
|
|
||||||
getShowNotifications() {
|
|
||||||
if (typeof this._showNotifications === 'boolean') return this._showNotifications;
|
|
||||||
|
|
||||||
const settings = getSettings();
|
|
||||||
if (settings === null) return true;
|
|
||||||
if (typeof settings.showNotifications === 'undefined') return true;
|
|
||||||
return settings.showNotifications;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsNotificationSounds() {
|
|
||||||
if (typeof this.isNotificationSounds === 'boolean') return this.isNotificationSounds;
|
|
||||||
|
|
||||||
const settings = getSettings();
|
|
||||||
if (settings === null) return true;
|
|
||||||
if (typeof settings.isNotificationSounds === 'undefined') return true;
|
|
||||||
return settings.isNotificationSounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
setter(action) {
|
setter(action) {
|
||||||
const actions = {
|
const actions = {
|
||||||
[cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
|
[cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
|
||||||
|
@ -185,20 +160,6 @@ class Settings extends EventEmitter {
|
||||||
setSettings('hideNickAvatarEvents', this.hideNickAvatarEvents);
|
setSettings('hideNickAvatarEvents', this.hideNickAvatarEvents);
|
||||||
this.emit(cons.events.settings.NICKAVATAR_EVENTS_TOGGLED, this.hideNickAvatarEvents);
|
this.emit(cons.events.settings.NICKAVATAR_EVENTS_TOGGLED, this.hideNickAvatarEvents);
|
||||||
},
|
},
|
||||||
[cons.actions.settings.TOGGLE_NOTIFICATIONS]: async () => {
|
|
||||||
if (window.Notification?.permission !== 'granted') {
|
|
||||||
this._showNotifications = false;
|
|
||||||
} else {
|
|
||||||
this._showNotifications = !this._showNotifications;
|
|
||||||
}
|
|
||||||
setSettings('showNotifications', this._showNotifications);
|
|
||||||
this.emit(cons.events.settings.NOTIFICATIONS_TOGGLED, this._showNotifications);
|
|
||||||
},
|
|
||||||
[cons.actions.settings.TOGGLE_NOTIFICATION_SOUNDS]: () => {
|
|
||||||
this.isNotificationSounds = !this.isNotificationSounds;
|
|
||||||
setSettings('isNotificationSounds', this.isNotificationSounds);
|
|
||||||
this.emit(cons.events.settings.NOTIFICATION_SOUNDS_TOGGLED, this.isNotificationSounds);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
actions[action.type]?.();
|
actions[action.type]?.();
|
||||||
|
|
Loading…
Reference in a new issue