Skip to content

Commit

Permalink
Merge pull request #1265 from nextcloud/feature/noid/warn-about-undel…
Browse files Browse the repository at this point in the history
…ivered-notifications

Warn about undelivered notifications
  • Loading branch information
nickvergessen committed Aug 31, 2022
2 parents f5afbee + 8cb0e9b commit f1e5830
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 31 deletions.
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.

16 changes: 15 additions & 1 deletion lib/Listener/BeforeTemplateRenderedListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,23 @@
use OCP\IConfig;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Notification\IManager;
use OCP\Util;

class BeforeTemplateRenderedListener implements IEventListener {
protected IConfig $config;
protected IUserSession $userSession;
protected IInitialState $initialState;
protected IManager $notificationManager;

public function __construct(IConfig $config,
IUserSession $userSession,
IInitialState $initialState) {
IInitialState $initialState,
IManager $notificationManager) {
$this->config = $config;
$this->userSession = $userSession;
$this->initialState = $initialState;
$this->notificationManager = $notificationManager;
}

public function handle(Event $event): void {
Expand Down Expand Up @@ -84,6 +88,16 @@ public function handle(Event $event): void {
) === 'yes'
);

/**
* We want to keep offering our push notification service for free, but large
* users overload our infrastructure. For this reason we have to rate-limit the
* use of push notifications. If you need this feature, consider using Nextcloud Enterprise.
*/
$this->initialState->provideInitialState(
'throttled_push_notifications',
!$this->notificationManager->isFairUseOfFreePushService()
);

Util::addScript('notifications', 'notifications-main');
}
}
60 changes: 41 additions & 19 deletions src/Components/Notification.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<template>
<li class="notification" :data-id="notificationId" :data-timestamp="timestamp">
<div class="notification-heading">
<span v-tooltip.bottom="absoluteDate"
<span v-if="timestamp"
v-tooltip.bottom="absoluteDate"
class="notification-time live-relative-timestamp"
:data-timestamp="timestamp">{{ relativeDate }}</span>
<NcButton class="notification-dismiss-button"
<NcButton v-if="timestamp"
class="notification-dismiss-button"
type="tertiary"
:aria-label="t('notifications', 'Dismiss')"
@click="onDismissNotification">
Expand All @@ -14,7 +16,15 @@
</NcButton>
</div>

<a v-if="useLink" :href="link" class="notification-subject full-subject-link">
<a v-if="externalLink"
:href="externalLink"
class="notification-subject full-subject-link external"
target="_blank"
rel="noreferrer noopener">
<span class="image"><img :src="icon" class="notification-icon" alt=""></span>
<span class="subject">{{ subject }} ↗</span>
</a>
<a v-else-if="useLink" :href="link" class="notification-subject full-subject-link">
<span v-if="icon" class="image"><img :src="icon" class="notification-icon" alt=""></span>
<RichText v-if="subjectRich"
:text="subjectRich"
Expand Down Expand Up @@ -43,6 +53,18 @@
<div v-if="actions.length" class="notification-actions">
<Action v-for="(a, i) in actions" :key="i" v-bind="a" />
</div>
<div v-else-if="externalLink" class="notification-actions">
<NcButton type="primary"
href="https://nextcloud.com/pushnotifications"
class="action-button pull-right"
target="_blank"
rel="noreferrer noopener">
<template #icon>
<Message :size="20" />
</template>
{{ t('notifications', 'Contact Nextcloud GmbH') }} ↗
</NcButton>
</div>
</li>
</template>

Expand All @@ -51,6 +73,7 @@ import axios from '@nextcloud/axios'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'
import Close from 'vue-material-design-icons/Close.vue'
import Message from 'vue-material-design-icons/Message.vue'
import { showError } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
import { Howl } from 'howler'
Expand All @@ -69,6 +92,7 @@ export default {
Action,
NcButton,
Close,
Message,
RichText,
},
Expand All @@ -80,89 +104,77 @@ export default {
notificationId: {
type: Number,
default: -1,
required: true,
},
datetime: {
type: String,
default: '',
required: true,
},
app: {
type: String,
default: '',
required: true,
},
icon: {
type: String,
default: '',
required: true,
},
link: {
type: String,
default: '',
required: true,
},
externalLink: {
type: String,
default: '',
},
user: {
type: String,
default: '',
required: true,
},
message: {
type: String,
default: '',
required: true,
},
messageRich: {
type: String,
default: '',
required: true,
},
messageRichParameters: {
type: [Object, Array],
default() {
return {}
},
required: true,
},
subject: {
type: String,
default: '',
required: true,
},
subjectRich: {
type: String,
default: '',
required: true,
},
subjectRichParameters: {
type: [Object, Array],
default() {
return {}
},
required: true,
},
objectType: {
type: String,
default: '',
required: true,
},
objectId: {
type: String,
default: '',
required: true,
},
actions: {
type: Array,
default() {
return []
},
required: true,
},
index: {
type: Number,
default: -1,
required: true,
},
},
Expand All @@ -176,12 +188,22 @@ export default {
computed: {
timestamp() {
if (this.datetime === 'warning') {
return 0
}
return (new Date(this.datetime)).valueOf()
},
absoluteDate() {
if (this.datetime === 'warning') {
return ''
}
return moment(this.timestamp).format('LLL')
},
relativeDate() {
if (this.datetime === 'warning') {
return ''
}
const diff = moment().diff(moment(this.timestamp))
if (diff >= 0 && diff < 45000) {
return t('core', 'seconds ago')
Expand Down
92 changes: 84 additions & 8 deletions src/NotificationsApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
:aria-label="t('notifications', 'Notifications')"
@open="onOpen">
<template #trigger>
<Bell v-if="notifications.length === 0"
<Bell v-if="notifications.length === 0 && webNotificationsGranted !== null && !hasThrottledPushNotifications"
:size="20"
:title="t('notifications', 'Notifications')"
fill-color="var(--color-primary-text)" />
Expand All @@ -22,6 +22,10 @@
fill="var(--color-primary-text)">
<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>
</template>

Expand All @@ -32,6 +36,15 @@
<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/pushnotifications"
:message="emptyContentDescription"
:subject="emptyContentMessage"
:index="2016" />
<Notification v-for="(n, index) in notifications"
:key="n.notificationId"
v-bind="n"
Expand All @@ -55,11 +68,23 @@

<!-- No notifications -->
<NcEmptyContent v-else
:title="webNotificationsGranted === null
? t('notifications', 'Requesting browser permissions to show notifications')
: t('notifications', 'No notifications')">
:title="emptyContentMessage"
:description="emptyContentDescription">
<template #icon>
<Bell />
<Bell v-if="!hasThrottledPushNotifications" />
<span v-else class="icon icon-alert-outline" />
</template>

<template #action>
<NcButton type="primary"
href="https://nextcloud.com/pushnotifications"
target="_blank"
rel="noreferrer noopener">
<template #icon>
<Message :size="20" />
</template>
{{ t('notifications', 'Contact Nextcloud GmbH') }} ↗
</NcButton>
</template>
</NcEmptyContent>
</transition>
Expand All @@ -74,10 +99,15 @@ import Close from 'vue-material-design-icons/Close.vue'
import axios from '@nextcloud/axios'
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
import { showError } from '@nextcloud/dialogs'
import { generateOcsUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
import {
generateOcsUrl,
imagePath,
} 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 HeaderMenu from './Components/HeaderMenu.vue'
Expand All @@ -89,6 +119,7 @@ export default {
NcButton,
Close,
Bell,
Message,
NcEmptyContent,
HeaderMenu,
Notification,
Expand All @@ -101,6 +132,7 @@ export default {
hasNotifyPush: false,
shutdown: false,
theming: getCapabilities()?.theming || {},
hasThrottledPushNotifications: loadState('notifications', 'throttled_push_notifications'),
notifications: [],
lastETag: null,
lastTabId: null,
Expand Down Expand Up @@ -133,13 +165,47 @@ export default {
}
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
&& this.userStatus !== 'dnd'
&& this.tabId === this.lastTabId
},
emptyContentMessage() {
if (this.webNotificationsGranted === null) {
return t('notifications', 'Requesting browser permissions to show notifications')
}
if (this.hasThrottledPushNotifications) {
return t('notifications', 'Push notifications might be unreliable')
}
return t('notifications', 'No notifications')
},
emptyContentDescription() {
if (this.hasThrottledPushNotifications) {
return t('notifications', 'Nextcloud GmbH sponsors a free push notification gateway for private users. To ensure good service, the gateway limits the number of push notifications per server. For enterprise users, a more scalable gateway is available. Contact Nextcloud GmbH for more information.')
}
return ''
},
warningIcon() {
return imagePath('core', 'actions/alert-outline.svg')
},
},
mounted() {
Expand Down Expand Up @@ -418,8 +484,18 @@ export default {
overflow: hidden;
}
.empty-content {
margin: 12vh 0;
::v-deep .empty-content {
margin: 12vh 10px;
p {
color: var(--color-text-maxcontrast);
}
}
.icon-alert-outline {
background-size: 64px;
width: 64px;
height: 64px;
}
.fade-enter-active,
Expand Down
Loading

0 comments on commit f1e5830

Please sign in to comment.