Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(service-worker): add service worker utilities and logging support #18

Merged
merged 23 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9a7a104
feat(service-worker): add service worker utilities and logging support
arashagp Dec 14, 2024
d5281f8
feat(service-worker): refactor imports and add version-checker utility
arashagp Dec 14, 2024
b60c47b
refactor(service-worker): enhance PWA installation handling with type…
arashagp Dec 16, 2024
21d82da
feat(service-worker): add beforeinstallprompt option and integrate sn…
arashagp Dec 16, 2024
af07308
refactor(snackbar): optimize logger and signal instantiation with pur…
arashagp Dec 16, 2024
3585038
feat(snackbar): allow 'infinite' duration option for snackbar display
arashagp Dec 16, 2024
e90f9d7
doc(snackbar): update documentation to clarify 'infinite' duration op…
arashagp Dec 16, 2024
fa505b5
refactor(service-worker): update snackbar duration to 'infinite' for …
arashagp Dec 16, 2024
245dfe1
refactor(service-worker): improve AlwatrSignal instantiation with pur…
arashagp Dec 16, 2024
da949f2
refactor(service-worker): update version and repository directory in …
arashagp Dec 16, 2024
c80b516
feat(service-worker): add an empty test file for initial setup
arashagp Dec 16, 2024
30f85ab
refactor(snackbar): simplify default duration assignment in showSnack…
arashagp Dec 16, 2024
54d0d8d
doc(service-worker): remove unnecessary comment in main.ts
arashagp Dec 16, 2024
45e266b
refactor(service-worker): remove install-pwa.ts file and related func…
arashagp Dec 16, 2024
59c0359
refactor(logger): simplify logger initialization by removing package …
arashagp Dec 16, 2024
bac2251
refactor(service-worker): remove serviceWorkerNotifyHandler and relat…
arashagp Dec 16, 2024
e63fd7e
refactor(version-checker): remove isVersionLarger function and relate…
arashagp Dec 16, 2024
fa24ba7
refactor(service-worker): remove unused exports from main.ts
arashagp Dec 16, 2024
944c6f7
refactor(service-worker): clean up package.json and yarn.lock by remo…
arashagp Dec 16, 2024
217786e
doc(service-worker): write installation
arashagp Dec 17, 2024
fa1aad9
chore: set correct owner homepage
njfamirm Dec 17, 2024
466ca5b
refactor(service-worker): review and enhance
njfamirm Dec 17, 2024
9a9e4dc
lint: make happy
njfamirm Dec 17, 2024
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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"argb",
"backorder",
"backordering",
"beforeinstallprompt",
"colspan",
"cssnano",
"endmacro",
Expand Down
661 changes: 661 additions & 0 deletions packages/service-worker/LICENSE

Large diffs are not rendered by default.

121 changes: 121 additions & 0 deletions packages/service-worker/README.md
arashagp marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# @nexim/alpine

![NPM Version](https://img.shields.io/npm/v/%40nexim%2Falpine)
![npm bundle size](https://img.shields.io/bundlephobia/min/%40nexim%2Falpine)
![Build & Lint & Test](https://github.com/the-nexim/nanolib/actions/workflows/build-lint-test.yaml/badge.svg)
![NPM Downloads](https://img.shields.io/npm/dm/%40nexim%2Falpine)
![NPM License](https://img.shields.io/npm/l/%40nexim%2Falpine)

## Overview

`@nexim/alpine` is a versatile library designed to enhance your Alpine.js experience with a suite of utility functions and mixins. It provides robust solutions for data management, including logging capabilities and backup functionalities with local storage support. This library aims to streamline the development of high-performance projects, ensuring efficiency and scalability.

## Installation

Install the package using npm or yarn:

```sh
npm install @nexim/alpine

# Or using yarn
yarn add @nexim/alpine
```

## API

### alpineStoreGenerator

Generates an Alpine.js store with a default value.

#### Example Usage

```ts
import {alpineStoreGenerator} from '@nexim/alpine';

const store = alpineStoreGenerator({
name: 'user',
defaultValue: {type: 'root'},
});

console.log(store.type); // Output: root
```

### AlpineStore

Provides a Alpine.js pure store implementation with logger.

#### Constructor

Creates an instance of `AlpineStore`.

- **config**: The configuration object for the store.
- **name**: The name of the store.
- **defaultValue**: The default value of the store.

### Properties

- **store**: alpine store proxy.

#### Example Usage

```ts
import {AlpineStore} from '@nexim/alpine';

const {store} = new AlpineStore({
name: 'myStore',
defaultValue: {data: 'root'},
});

console.log(store.data); // Output: { data: 'root' }
store.data = 'user';

console.log(store.data); // Output: { data: 'user' }
```

### AlpineStoreWithBackup

Extends `AlpineStore` to add backup and restore functionality with local storage support and expiration handling.

#### Constructor

Creates an instance of `AlpineStoreWithBackup`.

- **config**: The configuration object for the store.
- **name**: The name of the store.
- **version**: The version of the store.
- **defaultValue**: The default value of the store.
- **expireDuration**: Optional. The duration after which the store expires.

### Properties

- **store**: alpine store proxy.

#### Methods

- **save()**: Saves the current data to local storage. If the data is null, it clears the stored data. Also updates the expiration time.
- **clear()**: Clears the local storage and set default value to store.

#### Example Usage

```ts
import {AlpineStoreWithBackup} from '@nexim/alpine';

const storeWithBackup = new AlpineStoreWithBackup({
name: 'myStoreWithBackup',
version: 1,
defaultValue: {data: 'root'},
expireDuration: '1d',
});

storeWithBackup.store.data = 'user';

storeWithBackup.save();
console.log(storeWithBackup.store.data); // Output: { data: 'user' }

storeWithBackup.clear();
console.log(storeWithBackup.store.data); // Output: { data: 'root' }
```

### TODO

- Analyze [@alwatr/context](https://github.com/Alwatr/flux/tree/next/packages/context) for use here.
80 changes: 80 additions & 0 deletions packages/service-worker/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"name": "@nexim/service-worker",
"version": "0.0.0",
"description": "Utility functions to enhance Alpine.js usage with backup support.",
"keywords": [
"worker",
"service-worker",
"typescript",
"nexim"
],
"homepage": "https://github.com/the-nexim/nanolib/tree/next/packages/service-worker#readme",
"bugs": {
"url": "https://github.com/the-nexim/nanolib/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/the-nexim/nanolib",
"directory": "packages/service-worker"
},
"license": "AGPL-3.0-only",
"author": "S. Amir Mohammad Najafi <njfamirm@gmail.com> (www.njfamirm.ir)",
"contributors": [
"Arash Ghardashpoor <arash.qardashpoor@gmail.com> (https://www.agpagp.ir)"
],
"type": "module",
"exports": {
".": {
"types": "./dist/main.d.ts",
"import": "./dist/main.mjs",
"require": "./dist/main.cjs"
}
},
"main": "./dist/main.cjs",
"module": "./dist/main.mjs",
"types": "./dist/main.d.ts",
"files": [
"**/*.{js,mjs,cjs,map,d.ts,html,md,LEGAL.txt}",
"LICENSE",
"!**/*.test.js",
"!demo/**/*"
],
"scripts": {
"b": "yarn run build",
"build": "yarn run build:ts && yarn run build:es",
"build:es": "nano-build --preset=module",
"build:ts": "tsc --build",
"c": "yarn run clean",
"cb": "yarn run clean && yarn run build",
"clean": "rm -rfv dist *.tsbuildinfo",
"d": "yarn run build:es && yarn node --enable-source-maps --trace-warnings",
"t": "yarn run test",
"test": "NODE_OPTIONS=\"$NODE_OPTIONS --enable-source-maps --experimental-vm-modules\" ava",
"w": "yarn run watch",
"watch": "yarn run watch:ts & yarn run watch:es",
"watch:es": "yarn run build:es --watch",
"watch:ts": "yarn run build:ts --watch --preserveWatchOutput"
},
"dependencies": {
"@alwatr/flux": "^4.0.2",
"@alwatr/i18n": "^2.0.4",
"@alwatr/local-storage": "^5.0.0",
"@alwatr/logger": "^5.0.0",
"@alwatr/package-tracer": "^5.0.0",
"@alwatr/parse-duration": "^5.0.0",
"@alwatr/wait": "^1.1.16",
"@nexim/snackbar": "workspace:^",
arashagp marked this conversation as resolved.
Show resolved Hide resolved
"alpinejs": "^3.14.7"
},
"devDependencies": {
"@alwatr/nano-build": "^5.0.0",
"@alwatr/type-helper": "^5.0.0",
"@nexim/typescript-config": "workspace:^",
"@types/alpinejs": "^3.13.11",
"ava": "^6.2.0",
"typescript": "^5.6.3"
},
"publishConfig": {
"access": "public"
}
}
130 changes: 130 additions & 0 deletions packages/service-worker/src/lib/install-pwa.ts
arashagp marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import {parseDuration} from '@alwatr/parse-duration';
import {waitForTimeout} from '@alwatr/wait';
import {snackbarSignal} from '@nexim/snackbar';

import {logger} from './logger.js';

/**
* Type definition for BeforeInstallPromptEvent.
*/
interface BeforeInstallPromptEvent extends Event {
prompt: () => Promise<void>;
userChoice: Promise<void>;
}

/**
* Check if the app is running in installed PWA mode.
*/
function isOnInstalledPwa(): boolean {
return (
window.matchMedia('(display-mode: standalone)').matches ||
('standalone' in window.navigator && window.navigator.standalone != null)
);
}

/**
* Check browser compatibility for PWA installation and setup the installation prompt handler.
* If the browser supports the `BeforeInstallPromptEvent` event, show the install button and handle the installation process.
* Requires an element with the `install-pwa-prompt` id.
*/
export function setupInstallPwaPromptHandler(): void {
logger.logMethod?.('setupInstallPwaPromptHandler');

if (isOnInstalledPwa() === true) return;

if (('BeforeInstallPromptEvent' in window) === false) return;

const installPwaPromptButton = document.getElementById('install-pwa-prompt');
if (installPwaPromptButton === null) return;

// Show button prompt because supported.
installPwaPromptButton.classList.remove('!hidden');
installPwaPromptButton.classList.add('!flex');

try {
let deferredPrompt: BeforeInstallPromptEvent | null = null;

window.addEventListener('beforeinstallprompt', (event: Event) => {
const beforeInstallPromptEvent = event as BeforeInstallPromptEvent;
beforeInstallPromptEvent.preventDefault();
deferredPrompt = beforeInstallPromptEvent;
});

installPwaPromptButton.addEventListener('click', () => {
if (deferredPrompt == null) {
logger.error('setupInstallPwaPromptHandler', 'deferred_prompt_is_null');
snackbarSignal.notify({
content: 'مشکلی در نصب برنامه پیش آمده است! ممکن است برنامه را قبلا نصب کرده باشید.',
duration: '2s',
});
waitForTimeout(parseDuration('3s')).then(() => {
location.href = '/';
});
return;
}

deferredPrompt.prompt();
deferredPrompt.userChoice.then(() => {
deferredPrompt = null;
});
});

window.addEventListener('appinstalled', () => {
deferredPrompt = null;
snackbarSignal.notify({
content: 'مراحل نصب آغاز شد...',
});

installPwaPromptButton.classList.add('!hidden');
installPwaPromptButton.classList.remove('!flex');
});
}
catch (error) {
logger.error('setupInstallPwaPromptHandler', 'unknown_install_error', {error});
snackbarSignal.notify({
content: 'مشکلی در نصب برنامه پیش آمده است! ممکن است برنامه را قبلا نصب کرده باشید.',
});
}
}

/**
* Show a guide link to install the PWA.
* Requires an element with the `install-pwa-guide` id.
*/
export function showInstallPwaGuideElement(): void {
logger.logMethod?.('showInstallPwaGuideElement');

if (isOnInstalledPwa() === true) return;

const installPwaGuideLink = document.getElementById('install-pwa-guide');
if (installPwaGuideLink === null) return;

installPwaGuideLink.classList.remove('!hidden');
installPwaGuideLink.classList.add('!flex');
}

/**
* Show a message that the PWA is installed.
* Requires an element with the `installed-pwa` id.
*/
export function showInstalledPwaMessage(): void {
logger.logMethod?.('showInstalledPwaMessage');

if (isOnInstalledPwa() === false) return;

const installedMessage = document.getElementById('installed-pwa');
if (installedMessage === null) return;

snackbarSignal.notify({
content: 'برنامه نصب شده است!',
action: {
label: 'رفتن به صفحه‌ی اصلی',
handler: () => {
location.href = '/';
},
},
});

installedMessage.classList.remove('!hidden');
installedMessage.classList.add('!flex');
}
5 changes: 5 additions & 0 deletions packages/service-worker/src/lib/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {createLogger} from '@alwatr/logger';
import {packageTracer} from '@alwatr/package-tracer';

__dev_mode__: packageTracer.add(__package_name__, __package_version__);
arashagp marked this conversation as resolved.
Show resolved Hide resolved
export const logger = /* @__PURE__ */ createLogger(`${__package_name__}:service-worker`);
arashagp marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading