Skip to content

Commit

Permalink
Add new controllers and enhance functionality of controllers
Browse files Browse the repository at this point in the history
Introduce `device-motion` and `touch` controllers, enhance existing controllers with improved event dispatching and throttling, and add support for new configuration options like monochrome icons. Refactor code for better scalability, providing utilities like `expandIcons` and abstracting specific controller features.
  • Loading branch information
Spomky committed Dec 16, 2024
1 parent 308120a commit e2b8ed8
Show file tree
Hide file tree
Showing 23 changed files with 280 additions and 75 deletions.
14 changes: 14 additions & 0 deletions assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@
"fetch": "eager",
"enabled": true
},
"device-motion": {
"main": "src/device-motion_controller.js",
"name": "pwa/device-motion",
"webpackMode": "eager",
"fetch": "eager",
"enabled": true
},
"device-orientation": {
"main": "src/device-orientation_controller.js",
"name": "pwa/device-orientation",
Expand Down Expand Up @@ -96,6 +103,13 @@
"fetch": "eager",
"enabled": true
},
"touch": {
"main": "src/touch_controller.js",
"name": "pwa/touch",
"webpackMode": "eager",
"fetch": "eager",
"enabled": true
},
"vibration": {
"main": "src/vibration_controller.js",
"name": "pwa/vibration",
Expand Down
18 changes: 16 additions & 2 deletions assets/src/abstract_controller.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
'use strict';

import { getComponent } from '@symfony/ux-live-component';
import { Controller } from '@hotwired/stimulus';

/* stimulusFetch: 'lazy' */
export default class extends Controller {
component = null;
async initialize() {
try {
this.component = await getComponent(this.element);
} catch (e) {
}
}

dispatchEvent = (name, payload) => {
this.dispatch(name, { detail: payload });
if (payload === undefined) {
payload = {};
}
this.dispatch('pwa:'+name, { detail: payload, bubbles: true });
if (this.component) {
this.component.emit('pwa:'+name, payload);
}
}
}
4 changes: 2 additions & 2 deletions assets/src/backgroundsync-form_controller.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

import { Controller } from '@hotwired/stimulus';
import AbstractController from './abstract_controller.js';

/* stimulusFetch: 'lazy' */
export default class extends Controller {
export default class extends AbstractController {
static values = {
params: {
type: Object,
Expand Down
2 changes: 1 addition & 1 deletion assets/src/battery_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class extends AbstractController {
}
update = async ({counter}) => {
await navigator.setAppBadge(counter);
this.dispatchEvent('badge:updated', { counter });
this.dispatchEvent('battery:updated', { counter });
}

updateChargeInfo = async (battery) => {
Expand Down
4 changes: 2 additions & 2 deletions assets/src/connection-status_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default class extends AbstractController {
};

connect = () => {
this.dispatchEvent('connect', {});
this.dispatchEvent('connection-status:connect', {});
if (navigator.onLine) {
this.statusChanged({
status: 'ONLINE',
Expand Down Expand Up @@ -45,6 +45,6 @@ export default class extends AbstractController {
this.attributeTargets.forEach((element) => {
element.setAttribute('data-connection-status', data.status);
});
this.dispatchEvent('status-changed', { detail: data });
this.dispatchEvent('connection-status:changed', { detail: data });
}
}
36 changes: 36 additions & 0 deletions assets/src/device-motion_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

import AbstractController from './abstract_controller.js';

/* stimulusFetch: 'lazy' */
export default class extends AbstractController {
static values = {
throttle: { type: Number, default: 1000 },
};

connect() {
const throttle = (func, limit) => {
let inThrottle;
return function() {
const context = this;
if (!inThrottle) {
func.apply(context, arguments);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
};

const dispatchMotionEvent = (event) => {
this.dispatchEvent('device:motion', {
acceleration: event.acceleration,
accelerationIncludingGravity: event.accelerationIncludingGravity,
rotationRate: event.rotationRate,
interval: event.interval,
});
};

const throttledDispatch = throttle(dispatchMotionEvent.bind(this), this.throttleValue);
window.addEventListener('devicemotion', throttledDispatch, true);
}
}
39 changes: 27 additions & 12 deletions assets/src/device-orientation_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@ import AbstractController from './abstract_controller.js';

/* stimulusFetch: 'lazy' */
export default class extends AbstractController {
static values = {
throttle: { type: Number, default: 1000 },
};

connect() {
window.addEventListener(
'deviceorientation',
(event) => {
this.dispatchEvent({
absolute: event.absolute,
alpha: event.alpha,
beta: event.beta,
gamma: event.gamma
})
},
true
);
const throttle = (func, limit) => {
let inThrottle;
return function() {
const context = this;
if (!inThrottle) {
func.apply(context, arguments);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
};
};

const dispatchOrientationEvent = (event) => {
this.dispatchEvent('device:orientation', {
absolute: event.absolute,
alpha: event.alpha,
beta: event.beta,
gamma: event.gamma,
});
};

const throttledDispatch = throttle(dispatchOrientationEvent.bind(this), this.throttleValue);
window.addEventListener('deviceorientation', throttledDispatch, true);
}
}
6 changes: 3 additions & 3 deletions assets/src/geolocation_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class extends AbstractController {
}

navigator.geolocation.getCurrentPosition(
(position) => {this.dispatchEvent('geolocation:position', {latitude: position.coords.latitude, longitude: position.coords.longitude});},
(position) => {this.dispatchEvent('geolocation:position', {position});},
(error) => {this.dispatchEvent('geolocation:error', {error: error});},
params
);
Expand All @@ -29,8 +29,8 @@ export default class extends AbstractController {
}

this.watchId = navigator.geolocation.watchPosition(
(position) => {this.dispatchEvent('geolocation:position', {latitude: position.coords.latitude, longitude: position.coords.longitude});},
(error) => {this.dispatchEvent('geolocation:error', {error: error});},
(position) => {this.dispatchEvent('geolocation:position', {position});},
(error) => {this.dispatchEvent('geolocation:error', {error});},
params
);
}
Expand Down
2 changes: 1 addition & 1 deletion assets/src/prefetch-on-demand_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default class extends AbstractController {
}
});
this.dispatchEvent(
result === true ?'prefetched': 'error',
result === true ?'prefetch:prefetched': 'prefetch:error',
{params}
);
}
Expand Down
3 changes: 1 addition & 2 deletions assets/src/presentation_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ export default class extends AbstractController {
if (!this.connection) {
return;
}
const {message} = params;
this.connection.send(message);
this.connection.send(JSON.stringify(params));
}

terminate = () => {
Expand Down
32 changes: 19 additions & 13 deletions assets/src/receiver_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ import AbstractController from './abstract_controller.js';
export default class extends AbstractController {
async connect() {
if (!navigator.presentation.receiver) {
return;
}
const connections = await navigator.presentation.receiver.connections;
connections.connections.map(connection => addConnection(connection));
connections.addEventListener(
'connectionavailable',
const list = await navigator.presentation.receiver.connectionList;
list.connections.map((connection) => this.addConnection(connection));
}

addConnection(connection) {
connection.addEventListener(
'message',
(event) => {
const connection = event.connection;
connection.addEventListener(
'message',
(event) => this.dispatchEvent('message', {message: event.data})
);
connection.addEventListener(
'close',
() => this.dispatchEvent('close')
);
const data = JSON.parse(event.data);
this.dispatchEvent('receiver:message', {data});
}
);

connection.addEventListener(
'close',
(event) => this.dispatchEvent('receiver:close', {
connectionId: connection.connectionId,
reason: event.reason,
message: event.message
})
);
}
}
2 changes: 1 addition & 1 deletion assets/src/sync-broadcast_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ export default class extends AbstractController {
this.remainingTargets.forEach((element) => {
element.innerHTML = data.remaining;
});
this.dispatchEvent('status-changed', { detail: data });
this.dispatchEvent('sync-broadcast:status-changed', { detail: data });
}
}
76 changes: 76 additions & 0 deletions assets/src/touch_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use strict';

import AbstractController from './abstract_controller.js';

export default class extends AbstractController {
ongoingTouches = [];

connect() {
this.element.addEventListener("touchstart", this.onTouchStart);
this.element.addEventListener("touchmove", this.onTouchMove);
this.element.addEventListener("touchcancel", this.onTouchCancel);
this.element.addEventListener("touchend", this.onTouchEnd);
}

copyTouch = ({ identifier, clientX, clientY, pageX, pageY, radiusX, radiusY, screenX, screenY, force, rotationAngle})=> {
return {
identifier, clientX, clientY, pageX, pageY, radiusX, radiusY, screenX, screenY, force, rotationAngle,
top: this.element.offsetTop,
left: this.element.offsetLeft,
};
}

onTouchEnd = (event) => {
event.preventDefault();
const {changedTouches} = event;
for (let i = 0; i < changedTouches.length; i++) {
const idx = this.ongoingTouchIndexById(changedTouches[i].identifier);
this.ongoingTouches.splice(idx, 1);
this.dispatchEvent('touch:ended', { touch: changedTouches[i], bubbles: true });
}
this.dispatchEvent('touch:updated', { touches: this.ongoingTouches, bubbles: true });
}

onTouchCancel = (event) => {
event.preventDefault();
const {changedTouches} = event;
for (let i = 0; i < changedTouches.length; i++) {
const idx = this.ongoingTouchIndexById(changedTouches[i].identifier);
this.ongoingTouches.splice(idx, 1);
this.dispatchEvent('touch:cancelled', { touch: changedTouches[i], bubbles: true });
}
this.dispatchEvent('touch:updated', { touches: this.ongoingTouches, bubbles: true });
}

onTouchMove = (event) => {
event.preventDefault();
const {changedTouches} = event;
for (let i = 0; i < changedTouches.length; i++) {
const idx = this.ongoingTouchIndexById(changedTouches[i].identifier);
this.ongoingTouches.splice(idx, 1, this.copyTouch(changedTouches[i]))
this.dispatchEvent('touch:moved', { touch: changedTouches[i], bubbles: true });
}
this.dispatchEvent('touch:updated', { touches: this.ongoingTouches, bubbles: true });
}

onTouchStart = (event) => {
event.preventDefault();
const {changedTouches} = event;
for (let i = 0; i < changedTouches.length; i++) {
this.ongoingTouches.push(this.copyTouch(changedTouches[i]));
this.dispatchEvent('touch:started', { touch: changedTouches[i], bubbles: true });
}
this.dispatchEvent('touch:updated', { touches: this.ongoingTouches, bubbles: true });
}

ongoingTouchIndexById = (idToFind) => {
for (let i = 0; i < this.ongoingTouches.length; i++) {
const id = this.ongoingTouches[i].identifier;

if (id === idToFind) {
return i;
}
}
return -1;
}
}
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,10 @@
"phpstan/phpstan-strict-rules": "^1.0|^2.0",
"phpstan/phpstan-symfony": "^1.4|^2.0",
"phpunit/phpunit": "^10.1|^11.0",
"rector/rector": "^1.0|^2.0",
"staabm/phpstan-todo-by": "^0.1.27|^0.2",
"struggle-for-php/sfp-phpstan-psr-log": "^0.21.0|^0.22|^0.23",
"rector/rector": "^1.0|^2.0-rc2",
"shipmonk/dead-code-detector": "^0.5.1|^0.6",
"staabm/phpstan-todo-by": "^0.1|^0.2",
"struggle-for-php/sfp-phpstan-psr-log": "^0.20|^0.21|^0.22|^0.23",
"symfony/filesystem": "^6.4|^7.0",
"symfony/framework-bundle": "^6.4|^7.0",
"symfony/mime": "^6.4|^7.0",
Expand Down
2 changes: 2 additions & 0 deletions src/Dto/Favicons.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ final class Favicons
public null|bool $useSilhouette = null;

public null|string $potrace = null;

public bool $monochrome = false;
}
9 changes: 6 additions & 3 deletions src/ImageProcessor/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function __construct(
public null|string $backgroundColor = null,
public null|int $borderRadius = null,
public null|int $imageScale = null,
public bool $monochrome = false,
) {
if ($this->borderRadius !== null && $this->backgroundColor === null) {
throw new InvalidArgumentException('The background color must be set when the border radius is set');
Expand All @@ -26,13 +27,14 @@ public function __construct(
public function __toString(): string
{
return sprintf(
'%d%d%s%s%s%s',
'%d%d%s%s%s%s%s',
$this->width,
$this->height,
$this->format,
$this->backgroundColor ?? '',
$this->borderRadius ?? '',
$this->imageScale ?? ''
$this->imageScale ?? '',
$this->monochrome ? '1' : '0',
);
}

Expand All @@ -43,7 +45,8 @@ public static function create(
null|string $backgroundColor = null,
null|int $borderRadius = null,
null|int $imageScale = null,
bool $monochrome = false,
): self {
return new self($width, $height, $format, $backgroundColor, $borderRadius, $imageScale);
return new self($width, $height, $format, $backgroundColor, $borderRadius, $imageScale, $monochrome);
}
}
Loading

0 comments on commit e2b8ed8

Please sign in to comment.