Skip to content

Commit de8d8eb

Browse files
committed
[Notify] Add Notify library
1 parent a02daec commit de8d8eb

24 files changed

+1106
-0
lines changed

.github/workflows/test.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@ jobs:
101101
run: php vendor/bin/simple-phpunit
102102
working-directory: src/LazyImage
103103

104+
- name: Notify Dependencies
105+
uses: ramsey/composer-install@v2
106+
with:
107+
working-directory: src/Notify
108+
dependency-versions: lowest
109+
- name: Notify Tests
110+
run: php vendor/bin/simple-phpunit
111+
working-directory: src/Notify
112+
104113
tests-php8-low-deps:
105114
runs-on: ubuntu-latest
106115
steps:
@@ -184,6 +193,14 @@ jobs:
184193
working-directory: src/LiveComponent
185194
run: php vendor/bin/simple-phpunit
186195

196+
- name: Notify Dependencies
197+
uses: ramsey/composer-install@v2
198+
with:
199+
working-directory: src/Notify
200+
- name: Notify Tests
201+
working-directory: src/Notify
202+
run: php vendor/bin/simple-phpunit
203+
187204
tests-js:
188205
runs-on: ubuntu-latest
189206
steps:

src/Notify/.gitattributes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/.gitattributes export-ignore
2+
/.gitignore export-ignore
3+
/phpunit.xml.dist export-ignore
4+
/Resources/assets/test export-ignore
5+
/Tests export-ignore

src/Notify/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/vendor/
2+
.phpunit.result.cache
3+
.php_cs.cache
4+
composer.lock

src/Notify/.symfony-bundle.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
branches: ["2.x"]
2+
maintained_branches: ["2.x"]
3+
doc_dir: "Resources/doc"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Notify\DependencyInjection;
13+
14+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
15+
use Symfony\Component\Config\Definition\ConfigurationInterface;
16+
17+
/**
18+
* @author Mathias Arlaud <[email protected]>
19+
*
20+
* @internal
21+
*/
22+
final class Configuration implements ConfigurationInterface
23+
{
24+
public function getConfigTreeBuilder(): TreeBuilder
25+
{
26+
$treeBuilder = new TreeBuilder('notify');
27+
$rootNode = $treeBuilder->getRootNode();
28+
$rootNode
29+
->children()
30+
->scalarNode('mercure_hub')
31+
->info('Mercube hub service id')
32+
->defaultValue('mercure.hub.default')
33+
->end()
34+
->end()
35+
;
36+
37+
return $treeBuilder;
38+
}
39+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Notify\DependencyInjection;
13+
14+
use Symfony\Component\DependencyInjection\ContainerBuilder;
15+
use Symfony\Component\DependencyInjection\Reference;
16+
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
17+
use Symfony\UX\Notify\Twig\NotifyExtension as TwigNotifyExtension;
18+
use Symfony\WebpackEncoreBundle\Twig\StimulusTwigExtension;
19+
use Twig\Environment;
20+
21+
/**
22+
* @author Mathias Arlaud <[email protected]>
23+
*
24+
* @internal
25+
*/
26+
final class NotifyExtension extends ConfigurableExtension
27+
{
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function loadInternal(array $config, ContainerBuilder $container)
32+
{
33+
if (class_exists(Environment::class) && class_exists(StimulusTwigExtension::class)) {
34+
$container->register('notify.twig_extension', TwigNotifyExtension::class)
35+
->setArguments([
36+
new Reference($config['mercure_hub']),
37+
new Reference('webpack_encore.twig_stimulus_extension'),
38+
])
39+
->addTag('twig.extension')
40+
;
41+
}
42+
}
43+
}

src/Notify/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2020-2021 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/Notify/NotifyBundle.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Notify;
13+
14+
use Symfony\Component\HttpKernel\Bundle\Bundle;
15+
16+
/**
17+
* @author Mathias Arlaud <[email protected]>
18+
*
19+
* @final
20+
* @experimental
21+
*/
22+
class NotifyBundle extends Bundle
23+
{
24+
}

src/Notify/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Symfony UX Notify
2+
3+
Symfony UX Notify is a Symfony bundle integrating realtime native notifications
4+
in Symfony applications using [Mercure](https://mercure.rocks/).
5+
It is part of [the Symfony UX initiative](https://symfony.com/ux).
6+
7+
**This repository is a READ-ONLY sub-tree split**. See
8+
https://github.com/symfony/ux to create issues or submit pull requests.
9+
10+
## Resources
11+
12+
- [Documentation](https://symfony.com/bundles/ux-notify/current/index.html)
13+
- [Report issues](https://github.com/symfony/ux/issues) and
14+
[send Pull Requests](https://github.com/symfony/ux/pulls)
15+
in the [main Symfony UX repository](https://github.com/symfony/ux)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
'use strict';
10+
11+
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
12+
13+
Object.defineProperty(exports, "__esModule", {
14+
value: true
15+
});
16+
exports["default"] = void 0;
17+
18+
var _stimulus = require("@hotwired/stimulus");
19+
20+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
21+
22+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
23+
24+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
25+
26+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } Object.defineProperty(subClass, "prototype", { value: Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }), writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
27+
28+
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
29+
30+
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
31+
32+
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
33+
34+
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
35+
36+
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
37+
38+
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
39+
40+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
41+
42+
/**
43+
* @author Mathias Arlaud <[email protected]>
44+
*/
45+
var _default = /*#__PURE__*/function (_Controller) {
46+
_inherits(_default, _Controller);
47+
48+
var _super = _createSuper(_default);
49+
50+
function _default() {
51+
var _this;
52+
53+
_classCallCheck(this, _default);
54+
55+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
56+
args[_key] = arguments[_key];
57+
}
58+
59+
_this = _super.call.apply(_super, [this].concat(args));
60+
61+
_defineProperty(_assertThisInitialized(_this), "eventSources", []);
62+
63+
return _this;
64+
}
65+
66+
_createClass(_default, [{
67+
key: "initialize",
68+
value: function initialize() {
69+
var _this2 = this;
70+
71+
var errorMessages = [];
72+
if (!this.hasHubValue) errorMessages.push('A "hub" value pointing to the Mercure hub must be provided.');
73+
if (!this.hasTopicsValue) errorMessages.push('A "topics" value must be provided.');
74+
if (errorMessages.length) throw new Error(errorMessages.join(' '));
75+
this.eventSources = this.topicsValue.map(function (topic) {
76+
var u = new URL(_this2.hubValue);
77+
u.searchParams.append('topic', topic);
78+
return new EventSource(u);
79+
});
80+
}
81+
}, {
82+
key: "connect",
83+
value: function connect() {
84+
var _this3 = this;
85+
86+
if (!('Notification' in window)) {
87+
console.warn('This browser does not support desktop notifications.');
88+
return;
89+
}
90+
91+
;
92+
this.eventSources.forEach(function (eventSource) {
93+
eventSource.addEventListener('message', function (event) {
94+
return _this3._notify(JSON.parse(event.data).summary);
95+
});
96+
});
97+
98+
this._dispatchEvent('notify:connect', {
99+
eventSources: this.eventSources
100+
});
101+
}
102+
}, {
103+
key: "disconnect",
104+
value: function disconnect() {
105+
var _this4 = this;
106+
107+
this.eventSources.forEach(function (eventSource) {
108+
eventSource.removeEventListener('message', _this4._notify);
109+
eventSource.close();
110+
});
111+
this.eventSources = [];
112+
}
113+
}, {
114+
key: "_notify",
115+
value: function _notify(content) {
116+
if (!content) return;
117+
118+
if ('granted' === Notification.permission) {
119+
new Notification(content);
120+
return;
121+
}
122+
123+
if ('denied' !== Notification.permission) {
124+
Notification.requestPermission().then(function (permission) {
125+
if ('granted' === permission) {
126+
new Notification(content);
127+
}
128+
});
129+
}
130+
}
131+
}, {
132+
key: "_dispatchEvent",
133+
value: function _dispatchEvent(name) {
134+
var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
135+
var canBubble = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
136+
var cancelable = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
137+
var userEvent = document.createEvent('CustomEvent');
138+
userEvent.initCustomEvent(name, canBubble, cancelable, payload);
139+
this.element.dispatchEvent(userEvent);
140+
}
141+
}]);
142+
143+
return _default;
144+
}(_stimulus.Controller);
145+
146+
exports["default"] = _default;
147+
148+
_defineProperty(_default, "values", {
149+
hub: String,
150+
topics: Array
151+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('../../../../jest.config.js');
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "@symfony/ux-notify",
3+
"description": "Native notification integration for Symfony using Mercure",
4+
"license": "MIT",
5+
"version": "1.0.0",
6+
"symfony": {
7+
"controllers": {
8+
"notify": {
9+
"main": "dist/controller.js",
10+
"webpackMode": "eager",
11+
"fetch": "eager",
12+
"enabled": true
13+
}
14+
}
15+
},
16+
"scripts": {
17+
"build": "babel src -d dist",
18+
"test": "babel src -d dist && jest",
19+
"lint": "eslint src test"
20+
},
21+
"peerDependencies": {
22+
"@hotwired/stimulus": "^3.0.0"
23+
},
24+
"devDependencies": {
25+
"@hotwired/stimulus": "^3.0.0"
26+
}
27+
}

0 commit comments

Comments
 (0)