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
14 changes: 11 additions & 3 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,17 @@ AdtJsComponents.init('portal-components-grids-traits-signInAsIdentity', '~UI/Com
// AdtJsComponents.init('companySitePlanDetail', 'UI/Portal/Presenters/CompanySitePlans');
// AdtJsComponents.init('dashboard', 'UI/Portal/Presenters/Dashboard');
// AdtJsComponents.init('dashboard', 'assets/js/dashboard');
// AdtJsComponents.init('messaging', 'assets/js/messaging');
// AdtJsComponents.init('notifications', 'assets/js/notifications');
// AdtJsComponents.init('translate', 'assets/js/translate');
AdtJsComponents.init('messaging', (config) =>
import('./messaging/index.js').then(m => m.default.run(config))
);

AdtJsComponents.init('notifications', (config) =>
import('./notifications/index.js').then(m => m.default.run(config))
);

AdtJsComponents.init('translate', (config) =>
import('./translate/index.js').then(m => m.default.run(config))
);
// AdtJsComponents.init('print-dashboard', 'assets/js/printDashboard');
// AdtJsComponents.init('safari-support', 'assets/js/safariSupport');
//
Expand Down
21 changes: 21 additions & 0 deletions assets/js/messaging/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { initializeApp } from "firebase/app";
import { getMessaging, isSupported } from "firebase/messaging";

const run = async (config) => {
const app = initializeApp(config.initializeConfig);

const supported = await isSupported();
const messaging = supported ? getMessaging(app) : null;

navigator.serviceWorker.addEventListener('message', (event) => {
const payload = event.data;
$(document).trigger(`messaging.${payload.data.action}`, {
action: payload.data.action,
body: payload.data.body
});
});

window.messaging = messaging;
}

export default {run};
33 changes: 33 additions & 0 deletions assets/js/notifications/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { getToken } from "firebase/messaging";

const run = (config) => {
$('[data-adt-notifications]').on('click', function () {
if (window.messaging) {
Notification.requestPermission().then(function (permission) {
if (permission !== 'granted') {
alert(_('appJs.firebase.error.notificationsPermissionError'));
return;
}

getToken(window.messaging, { vapidKey: config.vapidKey })
.then(function (currentToken) {
if (currentToken) {
$.nette.ajax({
url: config.setFirebaseTokenLink.replace('__firebaseToken__', currentToken)
});
} else {
alert(_('appJs.firebase.error.notificationsPermissionError'));
}
})
.catch(function (err) {
console.error(err);
alert(_('appJs.firebase.error.notificationsPermissionError'));
});
});
} else {
alert(_('appJs.firebase.error.notificationsNotSupported'));
}
});
}

export default { run };
33 changes: 33 additions & 0 deletions assets/js/translate/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const run = (config) => {

const _ = (message, number, params) => {
let s = config.all[message];

if (s === undefined) {
return null;
}

if (params) {
for (const key in params) {
s = s.replaceAll(`%${key}%`, params[key]);
}
}

if (s.includes("|")) {
const options = s.split("|");
if (Math.abs(number) === 1) {
s = options[0];
} else if (Math.abs(number) >= 2 && Math.abs(number) <= 4) {
s = options[1];
} else {
s = options[2];
}
}

return s;
}

window._ = _;
}

export default {run};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"ua-parser-js": "^1.0.39",
"webpack": "^5.95.0",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^5.1.4"
"webpack-cli": "^5.1.4",
"kreait/firebase-php": "^7.0"
},
"packageManager": "yarn@3.2.0",
"devDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions src/DI/FancyAdminExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
use ADT\FancyAdmin\Model\FancyAdmin;
use ADT\FancyAdmin\Model\Security\Authenticator;
use ADT\FancyAdmin\Model\Security\SecurityUser;
use ADT\FancyAdmin\Model\Services\JsComponents;
use ADT\FancyAdmin\UI\Components\Controls\SidePanel\SidePanelControl;
use ADT\FancyAdmin\UI\Components\Controls\SidePanel\SidePanelControlFactory;
use Contributte\Translation\DI\TranslationProviderInterface;
use Nette\DI\CompilerExtension;
use Nette\DI\Config\Loader;
use Nette\Loaders\RobotLoader;
use Nette\Schema\Expect;
use Nette\Schema\Processor;
Expand Down Expand Up @@ -114,6 +116,9 @@ public function loadConfiguration(): void
'colors' => (array) $this->config->colors,
]);

$builder->addDefinition($this->prefix('jsComponents'))
->setFactory(JsComponents::class);

//$this->validateTraitInterfaceCompliance();

// command registration
Expand Down
12 changes: 12 additions & 0 deletions src/DI/Injects/JsComponentsInject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace ADT\FancyAdmin\DI\Injects;

use ADT\FancyAdmin\Model\Services\JsComponents;
use Kdyby\Autowired\Attributes\Autowire;

trait JsComponentsInject
{
#[Autowire]
protected JsComponents $_jsComponents;
}
60 changes: 60 additions & 0 deletions src/Model/Services/FirebaseService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace ADT\FancyAdmin\Model\Services;

use ADT\DoctrineComponents\EntityManager;
use ADT\FancyAdmin\Model\Queries\Factories\IdentityQueryFactory;
use Doctrine\ORM\NonUniqueResultException;
use Kreait\Firebase\Exception\FirebaseException;
use Kreait\Firebase\Exception\Messaging\NotFound;
use Kreait\Firebase\Exception\MessagingException;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
use Nette\Localization\Translator;
use Tracy\Debugger;

abstract class FirebaseService
{
public function __construct(
protected string $serviceAccount,
readonly protected EntityManager $entityManager,
readonly protected IdentityQueryFactory $identityQueryFactory,
readonly protected Translator $translator,
) {
}

/**
* @throws MessagingException
* @throws FirebaseException
* @throws \ReflectionException
* @throws NonUniqueResultException
*/
public function sendMessage(string $token, array $body): void
{
try {
(new Factory())
->withServiceAccount($this->serviceAccount)
->createMessaging()
->send(
CloudMessage::new()
->withToken($token)
->withData($body)
->withHighestPossiblePriority()
);
} catch (NotFound) {
$validTokens = [];
foreach ($this->identityQueryFactory->create()->byFirebaseToken($token)->fetch() as $_user) {
Debugger::log($_user->getId() . ' ' . $token, 'firebase-not-found');
foreach ($_user->getFirebaseTokens() as $userToken) {
if ($userToken === $token) {
continue;
}

$validTokens[] = $userToken;
}
$_user->setFirebaseTokens($validTokens);
}
$this->entityManager->flush();
}
}
}
31 changes: 31 additions & 0 deletions src/Model/Services/JsComponents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php


namespace ADT\FancyAdmin\Model\Services;

class JsComponents extends \ADT\Utils\JsComponents
{
public function setFirebaseLink(string $key, string $value): static
{
$this->components['notifications'][$key] = $value;
return $this;
}

public function setFirebaseConfig(array $firebaseConfig): void
{
$this->components['notifications'] = [
'initializeConfig' => $firebaseConfig,
];

$this->components['messaging'] = [
'initializeConfig' => $firebaseConfig,
];
}

public function setTranslateConfig(\App\Model\Translator $translator): void
{
$this->components['translate'] = [
'all' => $translator->getCatalogue()->all('appJs')
];
}
}
3 changes: 3 additions & 0 deletions src/UI/Presenters/@layout.latte
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
data-summernote-upload="{$summernoteUpload ?? ''}"
>
<div n:snippet="body">
<div data-adt-translate></div>

{if $primaryTemplate}
{block menu}
{include @sideMenu.latte}
Expand Down Expand Up @@ -160,6 +162,7 @@
{else}
<script src="{$basePath}/dist/portal/js/portal.js{v}"></script>
{/if}
{block modals}{/block}
{block scripts}{/block}
</body>
</html>
14 changes: 14 additions & 0 deletions src/UI/Presenters/BasePresenterTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ADT\FancyAdmin\DI\Injects\EntityManagerInject;
use ADT\FancyAdmin\DI\Injects\FancyAdminInject;
use ADT\FancyAdmin\DI\Injects\JsComponentsInject;
use ADT\FancyAdmin\DI\Injects\TranslatorInject;
use ADT\FancyAdmin\Model\Security\SecurityUser;
use Exception;
Expand All @@ -17,6 +18,7 @@ trait BasePresenterTrait
use FancyAdminInject;
use EntityManagerInject;
use TranslatorInject;
use JsComponentsInject;

protected bool $primaryTemplate = false;

Expand All @@ -37,6 +39,8 @@ protected function beforeRender(): void
$this->getTemplate()->hmr = $this->_fancyAdmin->getHmr();
$this->getTemplate()->projectName = $this->_fancyAdmin->getProjectName();
$this->getTemplate()->colors = $this->_fancyAdmin->getColors();
$this->_jsComponents->setFirebaseLink('setFirebaseTokenLink', $this->getPresenter()->link('setFirebaseToken!', ['firebaseToken' => '__firebaseToken__']));
$this->getTemplate()->jsComponentsConfig = $this->_jsComponents->generateConfig();
}


Expand Down Expand Up @@ -108,4 +112,14 @@ public function formatLayoutTemplateFiles(): array
$list[] = __DIR__ . "/@layout.latte";
return $list;
}

public function handleSetFirebaseToken(string $firebaseToken): void
{
$this->getUser()->getIdentity()
->addFirebaseToken($firebaseToken);

$this->em->flush();

$this->flashMessageSuccess('fcadmin.firebase.notifications.flashes.success'); // TODO translate
}
}
34 changes: 34 additions & 0 deletions src/UI/Presenters/Js/JsPresenterTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace ADT\FancyAdmin\UI\Presenters\Js;

use Contributte\Application\Response\StringResponse;
use JetBrains\PhpStorm\NoReturn;
use Kdyby\Autowired\Attributes\Autowire;
use Nette\Bridges\ApplicationLatte\TemplateFactory;

trait JsPresenterTrait
{
#[Autowire]
protected TemplateFactory $templateFactory;

protected array $firebaseConfig;

public function setFirebaseConfig(array $firebaseConfig): void
{
$this->firebaseConfig = $firebaseConfig;
}

#[NoReturn]
public function actionFirebaseMessagingSw(): void
{
$template = $this->templateFactory->createTemplate();
$template->setFile(__DIR__ . '/firebase-messaging-sw.js.latte');
$template->firebaseConfig = $this->firebaseConfig;
$template->renderToString();

$this->sendResponse(
new StringResponse($template->renderToString(), 'firebase-messaging-sw.js', 'application/javascript;')
);
}
}
19 changes: 19 additions & 0 deletions src/UI/Presenters/Js/firebase-messaging-sw.js.latte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{varType array $firebaseConfig}

importScripts('https://www.gstatic.com/firebasejs/12.10.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/12.10.0/firebase-messaging-compat.js');

// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.

firebase.initializeApp({\Nette\Utils\Json::encode($firebaseConfig)|noescape});

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
self.clients.matchAll({ type: 'window' }).then(clients => {
clients.forEach(client => {
client.postMessage(payload);
});
});
});
5 changes: 5 additions & 0 deletions src/lang/fcadmin.cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ sidePanels:
formSaved: "Formulář byl úspěšně uložen."
closeConfirm: "Opravdu zavřít bez uložení?"

firebase:
notifications:
flashes:
success: Notifikace úspěšně povoleny.

modules:
web:
navbar:
Expand Down