Skip to content
Open
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: 4 additions & 0 deletions config/wpn_ucp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ phpbb_webpushnotifications_ucp_push_subscribe_controller:
phpbb_webpushnotifications_ucp_push_unsubscribe_controller:
path: /push/unsubscribe
defaults: { _controller: phpbb.wpn.ucp.controller.webpush:unsubscribe }

phpbb_webpushnotifications_ucp_push_toggle_popup_controller:
path: /push/toggle-popup
defaults: { _controller: phpbb.wpn.ucp.controller.webpush:toggle_popup }
2 changes: 1 addition & 1 deletion controller/manifest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function handle(): JsonResponse
{
// Get the board URL and extract the path component
$board_url = generate_board_url();
$board_path = $this->config['force_server_vars'] ? $this->config['script_path'] : (parse_url($board_url)['path'] ?? '');
$board_path = $this->config['force_server_vars'] ? $this->config['script_path'] : (parse_url($board_url, PHP_URL_PATH) ?? '');

// Ensure path ends with '/' for PWA scope
$scope = rtrim($board_path, '/\\') . '/';
Expand Down
4 changes: 3 additions & 1 deletion language/en/webpushnotifications_module_ucp.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@
'NOTIFY_WEBPUSH_DROPDOWN_TITLE' => 'Visit notifications settings to set your preferred push notifications.',
'NOTIFY_WEBPUSH_DENIED' => 'You have denied notifications from this site. To enable push notifications, allow notifications from this site in your browser settings.',
'NOTIFY_WEBPUSH_NOT_SUPPORTED' => 'Push notifications not supported',
'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Allow browser notifications?',
'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Allow browser notifications',
'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'We would like to send you browser notifications for replies, private messages, and relevant forum activity. Optional — you can manage these settings at any time.',
'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Allow',
'NOTIFY_WEBPUSH_POPUP_DENY' => 'Deny',
'NOTIFY_WEBPUSH_POPUP_DISABLE' => 'Disable “Allow Browser Notifications” prompts',
'NOTIFY_WEBPUSH_POPUP_DISABLE_EXPLAIN' => 'Toggle this on to stop us from asking you to enable web push notifications on any of your devices. Note that we won’t be able to alert you if your web push notifications ever become disabled.',
]);
4 changes: 3 additions & 1 deletion language/ru/webpushnotifications_module_ucp.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@
'NOTIFY_WEBPUSH_DROPDOWN_TITLE' => 'Посетите настройки уведомлений, чтобы установить предпочтительные типы браузерных уведомлений.',
'NOTIFY_WEBPUSH_DENIED' => 'Вы запретили браузерные уведомления для даного сайта. Для того, чтобы подписаться, необходимо их разрешить в настройках браузера.',
'NOTIFY_WEBPUSH_NOT_SUPPORTED' => 'Не поддерживается',
'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Включить браузерные уведомления?',
'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Включить браузерные уведомления',
'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'Браузерные уведомления позволяют быстро получать информацию о новых ответах, личных сообщениях и других активностях на данной конференции. Функцию можно отключить или включить в любое время в настройках уведомлений в Личном разделе.',
'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Включить',
'NOTIFY_WEBPUSH_POPUP_DENY' => 'Отклонить',
'NOTIFY_WEBPUSH_POPUP_DISABLE' => 'Отключить запросы «Включить браузерные уведомления»',
'NOTIFY_WEBPUSH_POPUP_DISABLE_EXPLAIN' => 'Включите этот параметр, чтобы мы больше не спрашивали вас о включении веб-push-уведомлений на любом из ваших устройств. Обратите внимание, что в этом случае мы не сможем предупредить вас, если веб-push-уведомления будут отключены.',
]);
48 changes: 48 additions & 0 deletions migrations/add_user_popup_preference.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
*
* phpBB Browser Push Notifications. An extension for the phpBB Forum Software package.
*
* @copyright (c) 2025, phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
*/

namespace phpbb\webpushnotifications\migrations;

use phpbb\db\migration\migration;

class add_user_popup_preference extends migration
{
public function effectively_installed()
{
return $this->db_tools->sql_column_exists($this->table_prefix . 'users', 'user_wpn_popup_disabled');
}

public static function depends_on()
{
return ['\phpbb\webpushnotifications\migrations\add_popup_prompt'];
}

public function update_schema()
{
return [
'add_columns' => [
$this->table_prefix . 'users' => [
'user_wpn_popup_disabled' => ['BOOL', 0],
],
],
];
}

public function revert_schema()
{
return [
'drop_columns' => [
$this->table_prefix . 'users' => [
'user_wpn_popup_disabled',
],
],
];
}
}
4 changes: 3 additions & 1 deletion notification/method/webpush.php
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,13 @@ public function get_ucp_template_data(helper $controller_helper, form_helper $fo
'NOTIFICATIONS_WEBPUSH_ENABLE' => ($this->config['load_notifications'] && $this->config['allow_board_notifications'] && $this->config['wpn_webpush_dropdown_subscribe']) || stripos($this->user->page['page'], 'notification_options'),
'U_WEBPUSH_SUBSCRIBE' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_subscribe_controller'),
'U_WEBPUSH_UNSUBSCRIBE' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_unsubscribe_controller'),
'U_WEBPUSH_TOGGLE_POPUP' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_toggle_popup_controller'),
'VAPID_PUBLIC_KEY' => $this->config['wpn_webpush_vapid_public'],
'U_WEBPUSH_WORKER_URL' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_worker_controller'),
'SUBSCRIPTIONS' => $subscriptions,
'WEBPUSH_FORM_TOKENS' => $form_helper->get_form_tokens(\phpbb\webpushnotifications\ucp\controller\webpush::FORM_TOKEN_UCP),
'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE,
'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE && !($this->user->data['user_wpn_popup_disabled'] ?? 0),
'S_WEBPUSH_POPUP_DISABLED' => $this->user->data['user_wpn_popup_disabled'] ?? 0,
];
}

Expand Down
1 change: 1 addition & 0 deletions styles/all/template/ucp_notifications_webpush.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
serviceWorkerUrl: '{{ U_WEBPUSH_WORKER_URL }}',
subscribeUrl: '{{ U_WEBPUSH_SUBSCRIBE }}',
unsubscribeUrl: '{{ U_WEBPUSH_UNSUBSCRIBE }}',
togglePopupUrl: '{{ U_WEBPUSH_TOGGLE_POPUP }}',
ajaxErrorTitle: '{{ lang_js('AJAX_ERROR_TITLE') }}',
vapidPublicKey: '{{ VAPID_PUBLIC_KEY }}',
formTokens: {
Expand Down
102 changes: 91 additions & 11 deletions styles/all/template/webpush.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ function PhpbbWebpush() {
/** @type {HTMLElement} Unsubscribe button */
let unsubscribeButton;

/** @type {HTMLElement} Toggle popup button */
let togglePopupButton;

/** @type {string} URL to toggle popup prompt preference */
let togglePopupUrl = '';

/** @type {function} Escape key handler for popup */
let popupEscapeHandler;

/**
* Init function for phpBB Web Push
* @type {array} options
Expand All @@ -41,13 +50,20 @@ function PhpbbWebpush() {
serviceWorkerUrl = options.serviceWorkerUrl;
subscribeUrl = options.subscribeUrl;
unsubscribeUrl = options.unsubscribeUrl;
togglePopupUrl = options.togglePopupUrl;
this.formTokens = options.formTokens;
subscriptions = options.subscriptions;
ajaxErrorTitle = options.ajaxErrorTitle;
vapidPublicKey = options.vapidPublicKey;

subscribeButton = document.querySelector('#subscribe_webpush');
unsubscribeButton = document.querySelector('#unsubscribe_webpush');
togglePopupButton = document.querySelector('#toggle_popup_prompt');

// Set up toggle popup button handler if it exists (on UCP settings page)
if (togglePopupButton) {
togglePopupButton.addEventListener('click', togglePopupHandler);
}

// Service workers are only supported in secure context
if (window.isSecureContext !== true) {
Expand Down Expand Up @@ -163,7 +179,7 @@ function PhpbbWebpush() {
if (allowBtn) {
allowBtn.addEventListener('click', (event) => {
event.stopPropagation();
popup.style.display = 'none';
hidePopup(popup);
subscribeButtonHandler(event).catch(error => {
console.error('Subscription handler error:', error);
});
Expand All @@ -173,19 +189,40 @@ function PhpbbWebpush() {
if (denyBtn) {
denyBtn.addEventListener('click', (event) => {
event.stopPropagation();
popup.style.display = 'none';
hidePopup(popup);
promptDenied.set();
});
}

if (overlay) {
overlay.addEventListener('click', (event) => {
if (event.target === overlay) {
popup.style.display = 'none';
hidePopup(popup);
promptDenied.set();
}
});

popupEscapeHandler = (event) => {
if (event.key === 'Escape') {
hidePopup(popup);
promptDenied.set();
}
};

document.addEventListener('keydown', popupEscapeHandler);
}
}

/**
* Hide popup
* @param popup
*/
function hidePopup(popup) {
if (popup) {
popup.style.display = 'none';
}
document.removeEventListener('keydown', popupEscapeHandler);
popupEscapeHandler = null;
}

/**
Expand Down Expand Up @@ -282,10 +319,7 @@ function PhpbbWebpush() {
});
} catch (error) {
promptDenied.set(); // deny the prompt on error to prevent repeated prompting
const popup = document.getElementById('wpn_popup_prompt');
if (popup) {
popup.style.display = 'none';
}
hidePopup(document.getElementById('wpn_popup_prompt'));
console.error('Push subscription error:', error);
phpbb.alert(subscribeButton.getAttribute('data-l-err'), error.message || subscribeButton.getAttribute('data-l-unsupported'));
} finally {
Expand Down Expand Up @@ -331,6 +365,55 @@ function PhpbbWebpush() {
});
}

/**
* Handler for toggle popup prompt button
*
* @param {Object} event Toggle button push event
*/
function togglePopupHandler(event) {
event.preventDefault();

const loadingIndicator = phpbb.loadingIndicator();
const formData = new FormData();
formData.append('form_token', phpbb.webpush.formTokens.formToken);
formData.append('creation_time', phpbb.webpush.formTokens.creationTime.toString());

fetch(togglePopupUrl, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
body: formData,
})
.then(response => response.json())
.then(data => {
loadingIndicator.fadeOut(phpbb.alertTime);
if (data.success) {
// Update toggle icon based on new state
const button = document.getElementById('toggle_popup_prompt');
if (button) {
const icon = button.querySelector('i');
if (icon) {
if (data.disabled) {
icon.classList.remove('fa-toggle-off');
icon.classList.add('fa-toggle-on');
} else {
icon.classList.remove('fa-toggle-on');
icon.classList.add('fa-toggle-off');
}
}
}
if ('form_tokens' in data) {
updateFormTokens(data.form_tokens);
}
}
})
.catch(error => {
loadingIndicator.fadeOut(phpbb.alertTime);
phpbb.alert(ajaxErrorTitle, error);
});
}

/**
* Handle subscribe response
*
Expand All @@ -343,10 +426,7 @@ function PhpbbWebpush() {
updateFormTokens(response.form_tokens);
}
promptDenied.remove();
const popup = document.getElementById('wpn_popup_prompt');
if (popup) {
popup.style.display = 'none';
}
hidePopup(document.getElementById('wpn_popup_prompt'));
}
}

Expand Down
8 changes: 4 additions & 4 deletions styles/all/template/webpush_popup.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{% if S_WEBPUSH_POPUP_PROMPT %}
<div id="wpn_popup_prompt" class="wpn-popup-overlay" style="display:none;">
<div id="wpn_popup_prompt" class="wpn-popup-overlay" style="display:none;" role="dialog" aria-modal="true" aria-labelledby="wpn_popup_title" aria-describedby="wpn_popup_message">
<div class="wpn-popup-container">
<div class="wpn-popup-content">
<h3 class="wpn-popup-title">
<h3 id="wpn_popup_title" class="wpn-popup-title">
<span>{{ lang('NOTIFY_WEBPUSH_POPUP_TITLE') }}</span>
<span class="fa-stack fa-lg">
<span class="fa-stack fa-lg" aria-hidden="true">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-bell fa-stack-1x fa-inverse"></i>
</span>
</h3>
<p class="wpn-popup-message">{{ lang('NOTIFY_WEBPUSH_POPUP_MESSAGE') }}</p>
<p id="wpn_popup_message" class="wpn-popup-message">{{ lang('NOTIFY_WEBPUSH_POPUP_MESSAGE') }}</p>
<div class="wpn-popup-buttons">
<button id="wpn_popup_allow" class="wpn-popup-btn wpn-popup-btn-allow">{{ lang('NOTIFY_WEBPUSH_POPUP_ALLOW') }}</button>
<button id="wpn_popup_deny" class="wpn-popup-btn wpn-popup-btn-deny">{{ lang('NOTIFY_WEBPUSH_POPUP_DENY') }}</button>
Expand Down
50 changes: 48 additions & 2 deletions styles/all/theme/phpbb_wpn.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,43 @@
margin: 0 0 8px;
}

.wpn-fieldset {
display: grid;
column-gap: 1rem;
grid-template-columns: 1fr 320px;
row-gap: 1.25rem;
}

.wpn-fieldset > dl {
display: contents;
}

/* Optional styling */
.wpn-fieldset dt {
float: none;
align-self: start;
width: auto;
}

.wpn-fieldset dd {
display: flex;
align-items: center;
margin: 0;
}

.wpn-fieldset dt label {
line-height: 1.4;
display: inline-block;
}

.wpn-fieldset .fa-toggle-on {
color: #105289;
}

.wpn-fieldset .fa-toggle-off {
color: #5d5d5d;
}

.wpn-notification-dropdown-footer {
font-size: 12px;
white-space: nowrap;
Expand Down Expand Up @@ -47,8 +84,17 @@
}

@media (max-width: 700px) {
.wpn.button {
margin-top: 8px;
.wpn-fieldset {
grid-template-columns: 1fr;
row-gap: 0.5rem;
}

.wpn-fieldset dt {
grid-column: 1;
}

.wpn-fieldset dd {
grid-column: 1;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
{% if NOTIFICATIONS_WEBPUSH_ENABLE and notification_types is defined %}
<div class="panel">
<div class="inner">
<fieldset>
<fieldset class="wpn-fieldset">
<dl>
<dt><label for="subscribe_webpush">{{ lang('NOTIFY_WEBPUSH_NOTIFICATIONS') ~ lang('COLON') }}</label></dt>
<dt><label for="subscribe_webpush">{{ lang('NOTIFY_WEBPUSH_NOTIFICATIONS') ~ lang('COLON') }}</label><br><span>{{ lang('NOTIFY_WEBPUSH_ENABLE_EXPLAIN') }}</span></dt>
<dd>
<input id="subscribe_webpush" type="submit" name="subscribe_webpush" value="{{ lang('NOTIFY_WEBPUSH_ENABLE') }}" class="wpn button1 button button-form" data-l-err="{{ lang('INFORMATION')|e('html_attr') }}" data-l-msg="{{ lang('NOTIFY_WEBPUSH_DENIED')|e('html_attr') }}" data-l-unsupported="{{ lang('NOTIFY_WEBPUSH_NOT_SUPPORTED')|e('html_attr') }}">
<input id="unsubscribe_webpush" type="submit" name="unsubscribe_webpush" value="{{ lang('NOTIFY_WEBPUSH_DISABLE') }}" class="wpn button1 button button-form hidden">
<br><span>{{ lang('NOTIFY_WEBPUSH_ENABLE_EXPLAIN') }}</span>
</dd>
</dl>
<dl>
<dt><label for="toggle_popup_prompt">{{ lang('NOTIFY_WEBPUSH_POPUP_DISABLE') ~ lang('COLON') }}</label><br><span>{{ lang('NOTIFY_WEBPUSH_POPUP_DISABLE_EXPLAIN') }}</span></dt>
<dd>
<button id="toggle_popup_prompt" type="button" name="toggle_popup_prompt" class="wpn-toggle-button" aria-label="{{ lang('NOTIFY_WEBPUSH_POPUP_DISABLE') }}">
<i class="icon icon-lg fa-fw fa-toggle-{% if S_WEBPUSH_POPUP_DISABLED %}on{% else %}off{% endif %}" aria-hidden="true"></i>
</button>
</dd>
</dl>
</fieldset>
Expand Down
Loading