Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions js/notifications-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/notifications-main.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions js/notifications-src_NotificationsApp_vue.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/notifications-src_NotificationsApp_vue.js.map

Large diffs are not rendered by default.

91 changes: 91 additions & 0 deletions src/Components/IconNotification.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!--
- SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<span v-if="showDot || showWarning" class="notifications-button__icon">
<!-- Modified IconBell from material design icons -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
:width="size"
:height="size"
viewBox="0 0 24 24"
fill="currentColor">
<path d="M 19,11.79 C 18.5,11.92 18,12 17.5,12 14.47,12 12,9.53 12,6.5 12,5.03 12.58,3.7 13.5,2.71 13.15,2.28 12.61,2 12,2 10.9,2 10,2.9 10,4 V 4.29 C 7.03,5.17 5,7.9 5,11 v 6 l -2,2 v 1 H 21 V 19 L 19,17 V 11.79 M 12,23 c 1.11,0 2,-0.89 2,-2 h -4 c 0,1.11 0.9,2 2,2 z" />
<path class="notification__dot"
:class="{
'notification__dot--warning': showWarning && !isOrangeThemed,
'notification__dot--white': isRedThemed || (showWarning && isOrangeThemed),
}"
d="M 21,6.5 C 21,8.43 19.43,10 17.5,10 15.57,10 14,8.43 14,6.5 14,4.57 15.57,3 17.5,3 19.43,3 21,4.57 21,6.5" />
</svg>
</span>
<IconBell v-else class="notifications-button__icon" :size="size" />
</template>

<script setup>
import IconBell from 'vue-material-design-icons/Bell.vue'

import { computed } from 'vue'
import { getCapabilities } from '@nextcloud/capabilities'

const theming = getCapabilities()?.theming

defineProps({
showDot: {
type: Boolean,
default: false,
},
showWarning: {
type: Boolean,
default: false,
},
size: {
type: Number,
default: 20,
},
})

const hexRegex = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/
const hexToHSL = (hexColor) => {
const r = parseInt(hexColor.substring(1, 3), 16) / 255
const g = parseInt(hexColor.substring(3, 5), 16) / 255
const b = parseInt(hexColor.substring(5, 7), 16) / 255

const max = Math.max(r, g, b)
const min = Math.min(r, g, b)
let hue = 0
let sat = 0
const lum = (max + min) / 2

if (max !== min) {
const d = max - min
sat = lum > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r: hue = (g - b) / d + (g < b ? 6 : 0); break
case g: hue = (b - r) / d + 2; break
case b: hue = (r - g) / d + 4; break
}
hue *= 60
}

return [hue, sat, lum]
}

const isRedThemed = computed(() => {
if (!theming?.color || !hexRegex.test(theming?.color)) {
return false
}
const [hue, sat, lum] = hexToHSL(theming.color)
return (hue >= 330 || hue <= 15) && sat > 0.4 && (lum > 0.1 || lum < 0.6)
})

const isOrangeThemed = computed(() => {
if (!theming?.color || !hexRegex.test(theming?.color)) {
return false
}
const [hue, sat, lum] = hexToHSL(theming.color)
return (hue >= 305 || hue <= 64) && sat > 0.7 && (lum > 0.1 || lum < 0.6)
})
</script>
2 changes: 0 additions & 2 deletions src/Components/Notification.vue
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@
},

mounted() {
this._$el = $(this.$el)

Check warning on line 246 in src/Components/Notification.vue

View workflow job for this annotation

GitHub Actions / NPM lint

The global property or function $ was deprecated in Nextcloud 19.0.0
},

methods: {
Expand Down Expand Up @@ -297,8 +297,6 @@

<style lang="scss" scoped>
.notification {
background-color: var(--color-main-background);

:deep(img.notification-icon) {
display: flex;
width: 32px;
Expand Down
176 changes: 61 additions & 115 deletions src/NotificationsApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,74 +5,43 @@
:exclude-click-outside-selectors="['.popover']"
:open.sync="open"
:aria-label="t('notifications', 'Notifications')"
:title="t('notifications', 'Notifications')"
@open="onOpen">
<template #trigger>
<Bell v-if="notifications.length === 0 && webNotificationsGranted !== null && !hasThrottledPushNotifications"
class="notifications-button__icon"
:size="20"
:title="t('notifications', 'Notifications')" />
<!-- From material design icons -->
<svg v-else
class="notifications-button__icon"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="20"
height="20"
viewBox="0 0 24 24"
fill="currentColor">
<path d="M 19,11.79 C 18.5,11.92 18,12 17.5,12 14.47,12 12,9.53 12,6.5 12,5.03 12.58,3.7 13.5,2.71 13.15,2.28 12.61,2 12,2 10.9,2 10,2.9 10,4 V 4.29 C 7.03,5.17 5,7.9 5,11 v 6 l -2,2 v 1 H 21 V 19 L 19,17 V 11.79 M 12,23 c 1.11,0 2,-0.89 2,-2 h -4 c 0,1.11 0.9,2 2,2 z" />
<path :class="isRedThemed ? 'notification__dot--white' : ''" class="notification__dot" d="M 21,6.5 C 21,8.43 19.43,10 17.5,10 15.57,10 14,8.43 14,6.5 14,4.57 15.57,3 17.5,3 19.43,3 21,4.57 21,6.5" />
<path v-if="hasThrottledPushNotifications"
:class="isOrangeThemed ? 'notification__dot--white' : ''"
class="notification__dot notification__dot--warning"
d="M 21,6.5 C 21,8.43 19.43,10 17.5,10 15.57,10 14,8.43 14,6.5 14,4.57 15.57,3 17.5,3 19.43,3 21,4.57 21,6.5" />
</svg>
<IconNotification :size="20"
:show-dot="notifications.length !== 0 || webNotificationsGranted === null"
:show-warning="hasThrottledPushNotifications" />
</template>

<!-- Notifications list content -->
<div ref="container" class="notification-container">
<transition name="fade" mode="out-in">
<div v-if="notifications.length > 0">
<transition-group class="notification-wrapper"
name="list"
tag="ul">
<Notification v-if="hasThrottledPushNotifications"
:key="-2016"
datetime="warning"
app="core"
:icon="warningIcon"
external-link="https://nextcloud.com/fairusepolicy"
:message="emptyContentDescription"
:subject="emptyContentMessage"
:index="2016" />
<Notification v-for="(n, index) in notifications"
:key="n.notificationId"
v-bind="n"
:index="index"
@remove="onRemove" />
</transition-group>

<!-- Dismiss all -->
<span v-if="notifications.length > 0"
class="dismiss-all"
@click="onDismissAll">
<NcButton type="tertiary"
@click="onDismissAll">
<template #icon>
<Close :size="20" />
</template>
{{ t('notifications', 'Dismiss all notifications') }}
</NcButton>
</span>
</div>
<transition-group v-if="notifications.length > 0"
class="notification-wrapper"
name="list"
tag="ul">
<Notification v-if="hasThrottledPushNotifications"
:key="-2016"
datetime="warning"
app="core"
:icon="warningIcon"
external-link="https://nextcloud.com/fairusepolicy"
:message="emptyContentDescription"
:subject="emptyContentMessage"
:index="2016" />
<Notification v-for="(n, index) in notifications"
:key="n.notificationId"
v-bind="n"
:index="index"
@remove="onRemove" />
</transition-group>

<!-- No notifications -->
<NcEmptyContent v-else
:name="emptyContentMessage"
:description="emptyContentDescription">
<template #icon>
<Bell v-if="!hasThrottledPushNotifications" />
<IconBell v-if="!hasThrottledPushNotifications" />
<span v-else class="icon icon-alert-outline" />
</template>

Expand All @@ -82,21 +51,36 @@
target="_blank"
rel="noreferrer noopener">
<template #icon>
<Message :size="20" />
<IconMessage :size="20" />
</template>
{{ t('notifications', 'Contact Nextcloud GmbH') }} ↗
</NcButton>
</template>
</NcEmptyContent>
</transition>

<!-- Dismiss all -->
<div v-if="notifications.length > 0" class="dismiss-all">
<NcButton type="tertiary"
wide
@click="onDismissAll">
<template #icon>
<IconClose :size="20" />
</template>
{{ t('notifications', 'Dismiss all notifications') }}
</NcButton>
</div>
</div>
</NcHeaderMenu>
</template>

<script>
import IconBell from 'vue-material-design-icons/Bell.vue'
import IconClose from 'vue-material-design-icons/Close.vue'
import IconMessage from 'vue-material-design-icons/Message.vue'
import IconNotification from './Components/IconNotification.vue'
import Notification from './Components/Notification.vue'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import Close from 'vue-material-design-icons/Close.vue'
import axios from '@nextcloud/axios'
import { getCurrentUser } from '@nextcloud/auth'
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
Expand All @@ -108,21 +92,20 @@
} from '@nextcloud/router'
import { getNotificationsData } from './services/notificationsService.js'
import { listen } from '@nextcloud/notify_push'
import Bell from 'vue-material-design-icons/Bell.vue'
import Message from 'vue-material-design-icons/Message.vue'

import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
import { getCapabilities } from '@nextcloud/capabilities'
import NcHeaderMenu from '@nextcloud/vue/dist/Components/NcHeaderMenu.js'
import { createWebNotification } from './services/webNotificationsService.js'

export default {
name: 'NotificationsApp',

components: {
IconBell,
IconClose,
IconMessage,
IconNotification,
NcButton,
Close,
Bell,
Message,
NcEmptyContent,
NcHeaderMenu,
Notification,
Expand All @@ -134,7 +117,6 @@
backgroundFetching: false,
hasNotifyPush: false,
shutdown: false,
theming: getCapabilities()?.theming || {},
hasThrottledPushNotifications: loadState('notifications', 'throttled_push_notifications'),
notifications: [],
lastETag: null,
Expand Down Expand Up @@ -175,27 +157,6 @@
_$icon: null,

computed: {
isRedThemed() {
if (this.theming?.color) {
const hsl = this.rgbToHsl(this.theming.color.substring(1, 3),
this.theming.color.substring(3, 5),
this.theming.color.substring(5, 7))
const h = hsl[0] * 360
return (h >= 330 || h <= 15) && hsl[1] > 0.4 && (hsl[2] > 0.1 || hsl[2] < 0.6)
}
return false
},
isOrangeThemed() {
if (this.theming?.color) {
const hsl = this.rgbToHsl(this.theming.color.substring(1, 3),
this.theming.color.substring(3, 5),
this.theming.color.substring(5, 7))
const h = hsl[0] * 360
return (h >= 305 || h <= 64) && hsl[1] > 0.7 && (hsl[2] > 0.1 || hsl[2] < 0.6)
}
return false
},

showBrowserNotifications() {
return this.backgroundFetching
&& this.webNotificationsGranted
Expand Down Expand Up @@ -230,12 +191,12 @@

mounted() {
this.tabId = OC.requestToken || ('' + Math.random())
this._$icon = $(this.$refs.icon)

Check warning on line 194 in src/NotificationsApp.vue

View workflow job for this annotation

GitHub Actions / NPM lint

The global property or function $ was deprecated in Nextcloud 19.0.0
this._oldcount = 0

// Bind the button click event
console.debug('Registering notifications container as a menu')
OC.registerMenu($(this.$refs.button), $(this.$refs.container), undefined, true)

Check warning on line 199 in src/NotificationsApp.vue

View workflow job for this annotation

GitHub Actions / NPM lint

The global property or function $ was deprecated in Nextcloud 19.0.0

Check warning on line 199 in src/NotificationsApp.vue

View workflow job for this annotation

GitHub Actions / NPM lint

The global property or function $ was deprecated in Nextcloud 19.0.0

this.checkWebNotificationPermissions()

Expand Down Expand Up @@ -311,27 +272,6 @@
this.notifications.splice(index, 1)
},

rgbToHsl(r, g, b) {
r = parseInt(r, 16) / 255; g = parseInt(g, 16) / 255; b = parseInt(b, 16) / 255
const max = Math.max(r, g, b); const min = Math.min(r, g, b)
let h; let s; const l = (max + min) / 2

if (max === min) {
h = s = 0
} else {
const d = max - min
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break
case g: h = (b - r) / d + 2; break
case b: h = (r - g) / d + 4; break
}
h /= 6
}

return [h, s, l]
},

/**
* Update the title to show * if there are new notifications
*
Expand Down Expand Up @@ -535,18 +475,24 @@
.notification-container {
/* Prevent slide animation to go out of the div */
overflow: hidden;
}

.notification-wrapper {
max-height: calc(100vh - 50px * 4);
overflow: auto;
}
&,
& :deep(*),
& :deep(*::before),
& :deep(*::after) {
box-sizing: border-box;
}

::v-deep .empty-content {
margin: 12vh 10px;
.notification-wrapper {
display: flex;
flex-direction: column;
max-height: calc(100vh - 50px * 5);
overflow: auto;
}

p {
color: var(--color-text-maxcontrast);
.dismiss-all {
padding: calc(2 * var(--default-grid-baseline));
border-top: 1px solid var(--color-border);
}
}

Expand Down
Loading
Loading