Fix unread reset and notification settings (#1824)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Tue, 23 Jul 2024 05:14:32 +0000 (10:44 +0530)
committerGitHub <noreply@github.com>
Tue, 23 Jul 2024 05:14:32 +0000 (15:14 +1000)
* reset unread with client sync state change

* fix notification toggle setting not working

* revert formatOnSave vscode setting

.eslintrc.cjs
src/app/hooks/usePermission.js [deleted file]
src/app/hooks/usePermission.ts [new file with mode: 0644]
src/app/organisms/settings/Settings.jsx
src/app/pages/client/ClientNonUIFeatures.tsx
src/app/state/room/roomToUnread.ts
src/client/action/settings.js
src/client/state/cons.js
src/client/state/settings.js

index 36101fbe503ac89e8afcba82b5e6c7a0ea11c1bd..9d5a27d90b0387b97f7dffe6e506c5cba79adc12 100644 (file)
@@ -61,4 +61,12 @@ module.exports = {
     "@typescript-eslint/no-unused-vars": "error",
     "@typescript-eslint/no-shadow": "error"
   },
+  overrides: [
+    {
+      files: ['*.ts'],
+      rules: {
+        'no-undef': 'off',
+      },
+    },
+  ],
 };
diff --git a/src/app/hooks/usePermission.js b/src/app/hooks/usePermission.js
deleted file mode 100644 (file)
index 5dc7607..0000000
+++ /dev/null
@@ -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];
-}
diff --git a/src/app/hooks/usePermission.ts b/src/app/hooks/usePermission.ts
new file mode 100644 (file)
index 0000000..5a3ec9f
--- /dev/null
@@ -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;
+}
\ No newline at end of file
index f2951b812972a0bd23451fde3a202996e824c3db..779931df3f7463833a853499601b70f43c43804d 100644 (file)
@@ -7,9 +7,8 @@ import settings from '../../../client/state/settings';
 import navigation from '../../../client/state/navigation';
 import {
   toggleSystemTheme,
-  toggleNotifications, toggleNotificationSounds,
 } from '../../../client/action/settings';
-import { usePermission } from '../../hooks/usePermission';
+import { usePermissionState } from '../../hooks/usePermission';
 
 import Text from '../../atoms/text/Text';
 import IconButton from '../../atoms/button/IconButton';
@@ -230,23 +229,25 @@ function AppearanceSection() {
 }
 
 function NotificationsSection() {
-  const [permission, setPermission] = usePermission('notifications', window.Notification?.permission);
-
-  const [, updateState] = useState({});
+  const notifPermission = usePermissionState('notifications', window.Notification?.permission ?? "denied");
+  const [showNotifications, setShowNotifications] = useSetting(settingsAtom, 'showNotifications')
+  const [isNotificationSounds, setIsNotificationSounds] = useSetting(settingsAtom, 'isNotificationSounds')
 
   const renderOptions = () => {
     if (window.Notification === undefined) {
       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 (
         <Toggle
-          isActive={settings._showNotifications}
+          isActive={showNotifications}
           onToggle={() => {
-            toggleNotifications();
-            setPermission(window.Notification?.permission);
-            updateState({});
+            setShowNotifications(!showNotifications);
           }}
         />
       );
@@ -255,7 +256,9 @@ function NotificationsSection() {
     return (
       <Button
         variant="primary"
-        onClick={() => window.Notification.requestPermission().then(setPermission)}
+        onClick={() => window.Notification.requestPermission().then(() => {
+          setShowNotifications(window.Notification?.permission === 'granted');
+        })}
       >
         Request permission
       </Button>
@@ -275,8 +278,8 @@ function NotificationsSection() {
           title="Notification Sound"
           options={(
             <Toggle
-              isActive={settings.isNotificationSounds}
-              onToggle={() => { toggleNotificationSounds(); updateState({}); }}
+              isActive={isNotificationSounds}
+              onToggle={() => setIsNotificationSounds(!isNotificationSounds)}
             />
             )}
           content={<Text variant="b3">Play sound when new messages arrive.</Text>}
index 947764ca4c76ad5f465c4dea7c3110e993ed4ad7..5f557aa72393566b56ae9d820c0287afcd003fbd 100644 (file)
@@ -58,6 +58,7 @@ function InviteNotifications() {
   const mx = useMatrixClient();
 
   const navigate = useNavigate();
+  const [showNotifications] = useSetting(settingsAtom, 'showNotifications');
   const [notificationSound] = useSetting(settingsAtom, 'isNotificationSounds');
 
   const notify = useCallback(
@@ -84,7 +85,7 @@ function InviteNotifications() {
 
   useEffect(() => {
     if (invites.length > perviousInviteLen && mx.getSyncState() === 'SYNCING') {
-      if (Notification.permission === 'granted') {
+      if (showNotifications && Notification.permission === 'granted') {
         notify(invites.length - perviousInviteLen);
       }
 
@@ -92,7 +93,7 @@ function InviteNotifications() {
         playSound();
       }
     }
-  }, [mx, invites, perviousInviteLen, notificationSound, notify, playSound]);
+  }, [mx, invites, perviousInviteLen, showNotifications, notificationSound, notify, playSound]);
 
   return (
     // eslint-disable-next-line jsx-a11y/media-has-caption
index 5a009405eee79dcefaeef45440b4d7a3606eeb4c..8cb9d958476d8840a0781b361d75f112ae5319fb 100644 (file)
@@ -185,8 +185,11 @@ export const useBindRoomToUnreadAtom = (
   useSyncState(
     mx,
     useCallback(
-      (state) => {
-        if (state === SyncState.Prepared) {
+      (state, prevState) => {
+        if (
+          (state === SyncState.Prepared && prevState === null) ||
+          (state === SyncState.Syncing && prevState !== SyncState.Syncing)
+        ) {
           setUnreadAtom({
             type: 'RESET',
             unreadInfos: getUnreadInfos(mx),
index 7b539c8d741f95933ac5c1483c05c256b15be887..e849702b8e54766b382f0199cdb792bcb59a91a3 100644 (file)
@@ -30,15 +30,3 @@ export function toggleNickAvatarEvents() {
     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,
-  });
-}
index 523e871a14f92e7c8d824609517cfb56298231dc..353ce47d974a1b500555be9c7512f9b56d24f478 100644 (file)
@@ -52,8 +52,6 @@ const cons = {
       TOGGLE_PEOPLE_DRAWER: 'TOGGLE_PEOPLE_DRAWER',
       TOGGLE_MEMBERSHIP_EVENT: 'TOGGLE_MEMBERSHIP_EVENT',
       TOGGLE_NICKAVATAR_EVENT: 'TOGGLE_NICKAVATAR_EVENT',
-      TOGGLE_NOTIFICATIONS: 'TOGGLE_NOTIFICATIONS',
-      TOGGLE_NOTIFICATION_SOUNDS: 'TOGGLE_NOTIFICATION_SOUNDS',
     },
   },
   events: {
@@ -81,8 +79,6 @@ const cons = {
       PEOPLE_DRAWER_TOGGLED: 'PEOPLE_DRAWER_TOGGLED',
       MEMBERSHIP_EVENTS_TOGGLED: 'MEMBERSHIP_EVENTS_TOGGLED',
       NICKAVATAR_EVENTS_TOGGLED: 'NICKAVATAR_EVENTS_TOGGLED',
-      NOTIFICATIONS_TOGGLED: 'NOTIFICATIONS_TOGGLED',
-      NOTIFICATION_SOUNDS_TOGGLED: 'NOTIFICATION_SOUNDS_TOGGLED',
     },
   },
 };
index d39b2ca10f84afb57aea574a7fbdc793d2225de5..bf9562cc7dd5c78ab28bd72032b2c1c73f08bd50 100644 (file)
@@ -33,8 +33,6 @@ class Settings extends EventEmitter {
     this.isPeopleDrawer = this.getIsPeopleDrawer();
     this.hideMembershipEvents = this.getHideMembershipEvents();
     this.hideNickAvatarEvents = this.getHideNickAvatarEvents();
-    this._showNotifications = this.getShowNotifications();
-    this.isNotificationSounds = this.getIsNotificationSounds();
 
     this.darkModeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
 
@@ -137,29 +135,6 @@ class Settings extends EventEmitter {
     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) {
     const actions = {
       [cons.actions.settings.TOGGLE_SYSTEM_THEME]: () => {
@@ -185,20 +160,6 @@ class Settings extends EventEmitter {
         setSettings('hideNickAvatarEvents', 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]?.();