Profile settings: add global Notification toggle

This commit is contained in:
10G Meow 2023-12-07 20:53:24 +02:00
parent 1ddedac0ef
commit 3ad766fe12
8 changed files with 128 additions and 79 deletions

View file

@ -33,6 +33,7 @@
import stickers from "./plugins/stickers"; import stickers from "./plugins/stickers";
import { registerServiceWorker, notificationCount, windowNotificationPermission } from "./plugins/notificationAndServiceWorker.js" import { registerServiceWorker, notificationCount, windowNotificationPermission } from "./plugins/notificationAndServiceWorker.js"
import logoMixin from "./components/logoMixin"; import logoMixin from "./components/logoMixin";
import { mapState } from 'vuex'
export default { export default {
name: "App", name: "App",
@ -175,13 +176,16 @@ export default {
} }
return favicon; return favicon;
}, },
...mapState([
'globalNotification'
])
}, },
watch: { watch: {
notificationCount: { notificationCount: {
handler(nCount) { handler(nCount) {
// windowNotificationPermission // windowNotificationPermission
// return value: 'granted', 'default', 'denied' // return value: 'granted', 'default', 'denied'
if (nCount > 0 && this.windowNotificationPermission() === "granted") { if (this.globalNotification && nCount > 0 && this.windowNotificationPermission() === "granted") {
this.showNotification() this.showNotification()
} }
} }

View file

@ -218,7 +218,8 @@
"password_new": "New password", "password_new": "New password",
"password_repeat": "Repeat new password", "password_repeat": "Repeat new password",
"display_name": "Display name", "display_name": "Display name",
"display_name_required": "Display name is required" "display_name_required": "Display name is required",
"notification_label": "Notification"
}, },
"profile_info_popup": { "profile_info_popup": {
"you_are": "You are", "you_are": "You are",
@ -382,7 +383,8 @@
"body": "Never miss a message or important conversation again! Be notified whenever someone sends you a message or replies to your chat.", "body": "Never miss a message or important conversation again! Be notified whenever someone sends you a message or replies to your chat.",
"enable": "Enable" "enable": "Enable"
}, },
"blocked_message": "Notifications blocked. Please reset the permissions" "blocked_message": "Notification is blocked. Go to your device or browser settings to enable Notification",
"not_supported": "Notification is not yet supported in Mobile"
}, },
"emoji": { "emoji": {
"search": "Search...", "search": "Search...",

View file

@ -4,18 +4,16 @@
no-gutters no-gutters
align-content="center" align-content="center"
v-on="$listeners" v-on="$listeners"
v-show="icon === 'notifications_active' ? this.windowNotificationPermission() !== 'granted' : true"
> >
<v-col cols="auto" class="me-2"> <v-col cols="auto" class="me-2 align-self-center">
<v-icon :size="iconSize">{{ icon }}</v-icon> <v-icon :size="iconSize">{{ icon }}</v-icon>
</v-col> </v-col>
<v-col>{{ text }}</v-col> <v-col class="align-self-center">{{ text }}</v-col>
<v-col cols="auto" v-if="$slots.default"><slot/></v-col>
</v-row> </v-row>
</template> </template>
<script> <script>
import { windowNotificationPermission } from "../plugins/notificationAndServiceWorker.js"
export default { export default {
name: "ActionRow", name: "ActionRow",
props: { props: {
@ -37,9 +35,6 @@ export default {
return ""; return "";
}, },
}, },
},
methods: {
windowNotificationPermission
} }
}; };
</script> </script>

View file

@ -3,12 +3,10 @@
<ChatHeaderPrivate class="chat-header flex-grow-0 flex-shrink-0" <ChatHeaderPrivate class="chat-header flex-grow-0 flex-shrink-0"
v-on:header-click="onHeaderClick" v-on:header-click="onHeaderClick"
v-on:view-room-details="viewRoomDetails" v-on:view-room-details="viewRoomDetails"
v-on:notify="onNotificationDialog"
v-if="!useFileModeNonAdmin && $matrix.isDirectRoom(room)" /> v-if="!useFileModeNonAdmin && $matrix.isDirectRoom(room)" />
<ChatHeader class="chat-header flex-grow-0 flex-shrink-0" <ChatHeader class="chat-header flex-grow-0 flex-shrink-0"
v-on:header-click="onHeaderClick" v-on:header-click="onHeaderClick"
v-on:view-room-details="viewRoomDetails" v-on:view-room-details="viewRoomDetails"
v-on:notify="onNotificationDialog"
v-else-if="!useFileModeNonAdmin" /> v-else-if="!useFileModeNonAdmin" />
<AudioLayout ref="chatContainer" class="auto-audio-player-root" v-if="useVoiceMode" :room="room" <AudioLayout ref="chatContainer" class="auto-audio-player-root" v-if="useVoiceMode" :room="room"
:events="events" :autoplay="!showRecorder" :events="events" :autoplay="!showRecorder"
@ -78,9 +76,9 @@
<component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="filteredEvents[index + 1]" <component :is="componentForEvent(event)" :room="room" :originalEvent="event" :nextEvent="filteredEvents[index + 1]"
:timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction" :timelineSet="timelineSet" v-on:send-quick-reaction.stop="sendQuickReaction"
:componentFn="componentForEvent" :componentFn="componentForEvent"
v-on:context-menu="showContextMenuForEvent({event: event, anchor: $event.anchor})" v-on:context-menu="showContextMenuForEvent({event: event, anchor: $event.anchor})"
v-on:own-avatar-clicked="viewProfile" v-on:own-avatar-clicked="viewProfile"
v-on:other-avatar-clicked="showAvatarMenuForEvent({event: event, anchor: $event.anchor})" v-on:other-avatar-clicked="showAvatarMenuForEvent({event: event, anchor: $event.anchor})"
v-on:download="download(event)" v-on:download="download(event)"
v-on:poll-closed="pollWasClosed(event)" v-on:poll-closed="pollWasClosed(event)"
v-on:more=" v-on:more="
@ -324,45 +322,6 @@
<CreatePollDialog :show="showCreatePollDialog" @close="showCreatePollDialog = false" /> <CreatePollDialog :show="showCreatePollDialog" @close="showCreatePollDialog = false" />
<!-- Dialog for request Notification and register service worker-->
<v-dialog
v-model="notificationDialog"
persistent
class="ma-0 pa-0"
:width="$vuetify.breakpoint.smAndUp ? '688px' : '95%'"
>
<div class="dialog-content text-center">
<v-icon size="30">notifications_active</v-icon>
<h2 class="dialog-title">
{{ $t("notification.dialog.title") }}
</h2>
<div class="dialog-text">{{ $t("notification.dialog.body") }}</div>
<v-container fluid>
<v-row cols="12">
<v-col cols="6">
<v-btn
depressed
text
block
class="text-button"
@click="notificationDialog = false"
>{{ $t("global.close") }}</v-btn
>
</v-col>
<v-col cols="6" align="center">
<v-btn
color="primary"
depressed
block
class="filled-button"
@click.stop="onNotifyRequest"
>{{ $t("notification.dialog.enable") }}</v-btn
>
</v-col>
</v-row>
</v-container>
</div>
</v-dialog>
</div> </div>
</template> </template>
@ -388,7 +347,6 @@ import chatMixin from "./chatMixin";
import sendAttachmentsMixin from "./sendAttachmentsMixin"; import sendAttachmentsMixin from "./sendAttachmentsMixin";
import AudioLayout from "./AudioLayout.vue"; import AudioLayout from "./AudioLayout.vue";
import FileDropLayout from "./file_mode/FileDropLayout"; import FileDropLayout from "./file_mode/FileDropLayout";
import { requestNotificationPermission, windowNotificationPermission } from "../plugins/notificationAndServiceWorker.js"
import roomTypeMixin from "./roomTypeMixin"; import roomTypeMixin from "./roomTypeMixin";
import roomMembersMixin from "./roomMembersMixin"; import roomMembersMixin from "./roomMembersMixin";
@ -525,7 +483,6 @@ export default {
Places: this.$t("emoji.categories.places") Places: this.$t("emoji.categories.places")
} }
}, },
notificationDialog: false
}; };
}, },
@ -914,18 +871,6 @@ export default {
this.initialLoadDone = true; this.initialLoadDone = true;
console.log("Loading finished!"); console.log("Loading finished!");
}, },
windowNotificationPermission,
onNotificationDialog() {
if(this.windowNotificationPermission() === 'denied') {
alert(this.$t("notification.blocked_message"));
} else if(this.windowNotificationPermission() === 'default') {
this.notificationDialog = true;
}
},
onNotifyRequest() {
requestNotificationPermission()
this.notificationDialog = false;
},
onRoomJoined(initialEventId) { onRoomJoined(initialEventId) {
// Listen to events // Listen to events
this.$matrix.on("Room.timeline", this.onEvent); this.$matrix.on("Room.timeline", this.onEvent);
@ -1157,7 +1102,7 @@ export default {
this.onLayoutChange(fn, element); this.onLayoutChange(fn, element);
} else { } else {
fn(); fn();
} }
} else { } else {
fn(); fn();
} }

View file

@ -177,11 +177,6 @@ export default {
this.$emit("view-room-details", { event: this.event }); this.$emit("view-room-details", { event: this.event });
} }
}); });
items.push({
icon: 'notifications_active', text: this.$t('global.notify'), handler: () => {
this.$emit("notify");
}
});
items.push({ items.push({
icon: '$vuetify.icons.ic_member-leave', text: this.$t('leave.leave'), handler: () => { icon: '$vuetify.icons.ic_member-leave', text: this.$t('leave.leave'), handler: () => {
this.leaveRoom(); this.leaveRoom();

View file

@ -89,6 +89,16 @@
:icon="'$vuetify.icons.globe'" :icon="'$vuetify.icons.globe'"
:text="$t('profile.select_language')" :text="$t('profile.select_language')"
/> />
<ActionRow
@click="onUpdateGlobalNotification"
:icon="notificationIcon"
:text="$t('profile.notification_label')"
>
<v-switch
v-model="globalNotification"
readonly
></v-switch>
</ActionRow>
</v-container> </v-container>
<!-- edit password dialog --> <!-- edit password dialog -->
@ -197,6 +207,45 @@
v-model="showSelectLanguageDialog" v-model="showSelectLanguageDialog"
v-on:close="showSelectLanguageDialog = false" v-on:close="showSelectLanguageDialog = false"
/> />
<!-- Dialog for request Notification -->
<v-dialog
v-model="notificationDialog"
persistent
class="ma-0 pa-0"
:width="$vuetify.breakpoint.smAndUp ? '688px' : '95%'"
>
<div class="dialog-content text-center">
<v-icon size="30">notifications_active</v-icon>
<h2 class="dialog-title">
{{ $t("notification.dialog.title") }}
</h2>
<div class="dialog-text">{{ $t("notification.dialog.body") }}</div>
<v-container fluid>
<v-row cols="12">
<v-col cols="6">
<v-btn
depressed
text
block
class="text-button"
@click="onNotifyDialogClosed"
>{{ $t("global.close") }}</v-btn
>
</v-col>
<v-col cols="6" align="center">
<v-btn
color="primary"
depressed
block
class="filled-button"
@click.stop="onNotifyDialog"
>{{ $t("notification.dialog.enable") }}</v-btn
>
</v-col>
</v-row>
</v-container>
</div>
</v-dialog>
</div> </div>
</template> </template>
@ -207,6 +256,8 @@ import util from "../plugins/utils";
import profileInfoMixin from "./profileInfoMixin"; import profileInfoMixin from "./profileInfoMixin";
import LogoutRoomDialog from './LogoutRoomDialog.vue'; import LogoutRoomDialog from './LogoutRoomDialog.vue';
import CopyLink from "./CopyLink.vue" import CopyLink from "./CopyLink.vue"
import { requestNotificationPermission, windowNotificationPermission } from "../plugins/notificationAndServiceWorker.js"
import { mapState } from 'vuex'
export default { export default {
name: "Profile", name: "Profile",
@ -234,7 +285,8 @@ export default {
passwordErrorMessage: null, passwordErrorMessage: null,
isAvatarLoaded: true, isAvatarLoaded: true,
loadValue: 0, loadValue: 0,
newPasswordHasError: false newPasswordHasError: false,
notificationDialog: false
}; };
}, },
@ -252,7 +304,13 @@ export default {
this.newPassword2 && this.newPassword2 &&
this.newPassword1 == this.newPassword2 this.newPassword1 == this.newPassword2
); );
} },
notificationIcon() {
return this.globalNotification ? 'notifications_active' : 'notifications_off';
},
...mapState([
'globalNotification'
])
}, },
methods: { methods: {
@ -306,7 +364,53 @@ export default {
console.log("Progress: " + JSON.stringify(progress)); console.log("Progress: " + JSON.stringify(progress));
}); });
}, },
updateGlobalNotificationStore(flag) {
this.$store.commit('setGlobalNotification', flag);
},
windowNotificationPermission,
onUpdateGlobalNotification(showAlertOrDialog = true) {
const permission = this.windowNotificationPermission();
switch (permission) {
case 'denied':
this.updateGlobalNotificationStore(false);
if (showAlertOrDialog) {
alert(this.$t("notification.blocked_message"));
}
break;
case 'granted':
this.updateGlobalNotificationStore(!this.globalNotification);
break;
case 'default':
if (showAlertOrDialog) {
this.notificationDialog = true;
}
this.updateGlobalNotificationStore(!this.globalNotification);
break;
default:
alert(this.$t("notification.not_supported"));
}
},
async onNotifyDialog() {
const permission = await requestNotificationPermission()
if(permission === 'denied') {
this.updateGlobalNotificationStore(false);
alert(this.$t("notification.blocked_message"));
} else {
this.updateGlobalNotificationStore(true);
}
this.notificationDialog = false;
},
onNotifyDialogClosed() {
this.updateGlobalNotificationStore(false);
this.notificationDialog = false;
}
}, },
mounted() {
if(this.globalNotification && this.windowNotificationPermission() !== 'granted') {
this.onUpdateGlobalNotification(false);
}
}
}; };
</script> </script>

View file

@ -6,16 +6,16 @@ export function registerServiceWorker() {
} }
} }
export function requestNotificationPermission() { export async function requestNotificationPermission() {
if("PushManager" in window) { if("PushManager" in window) {
window.Notification.requestPermission(); return Notification?.requestPermission().then((permission) => permission);
} else { } else {
console.log("No Push API Support!"); console.log("No Push API Support!");
} }
} }
export function windowNotificationPermission() { export function windowNotificationPermission() {
return window.Notification.permission return window?.Notification?.permission ?? 'Not_supported'
} }
export function notificationCount() { export function notificationCount() {

View file

@ -41,6 +41,7 @@ const vuexPersistLocalStorage = new VuexPersist({
language: state.language, language: state.language,
currentRoomId: state.currentRoomId, currentRoomId: state.currentRoomId,
hasShownMissedItemsHint: state.hasShownMissedItemsHint, hasShownMissedItemsHint: state.hasShownMissedItemsHint,
globalNotification: state.globalNotification,
}; };
} else { } else {
return {}; return {};
@ -98,6 +99,9 @@ export default new Vuex.Store({
}, },
setHasShownMissedItemsHint(state, flag) { setHasShownMissedItemsHint(state, flag) {
state.hasShownMissedItemsHint = flag; state.hasShownMissedItemsHint = flag;
},
setGlobalNotification(state, flag) {
state.globalNotification = flag;
} }
}, },
actions: { actions: {