@proteinjs/app-version-ui 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.js ADDED
@@ -0,0 +1,20 @@
1
+ module.exports = {
2
+ extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'prettier'],
3
+ parser: '@typescript-eslint/parser',
4
+ plugins: ['@typescript-eslint', 'prettier'],
5
+ root: true,
6
+ ignorePatterns: ['**/dist/*', '**/node_modules/*', '*.md'],
7
+
8
+ rules: {
9
+ 'prettier/prettier': ['warn'],
10
+ curly: ['warn'],
11
+ 'eol-last': ['warn', 'always'],
12
+ 'keyword-spacing': ['warn', { before: true }],
13
+ 'no-undef': 'off',
14
+ '@typescript-eslint/no-unused-vars': 'off',
15
+ '@typescript-eslint/no-var-requires': 'off',
16
+ '@typescript-eslint/no-explicit-any': 'off',
17
+ '@typescript-eslint/prefer-as-const': 'off',
18
+ '@typescript-eslint/ban-types': 'off',
19
+ },
20
+ };
@@ -0,0 +1,4 @@
1
+ node_modules/
2
+ dist/
3
+ .DS_Store
4
+ *.md
package/.prettierrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "trailingComma": "es5",
3
+ "tabWidth": 2,
4
+ "semi": true,
5
+ "singleQuote": true,
6
+ "jsxSingleQuote": true,
7
+ "printWidth": 120
8
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## 1.0.1 (2024-08-02)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * initial implementation of app-version-server and app-version-ui ([6ed1f7f](https://github.com/proteinjs/notifications/commit/6ed1f7fbed212a034256ec643afc937f9b7ff549))
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Brent Bahry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ export * from './src/useVersionChecker';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./src/useVersionChecker"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC"}
@@ -0,0 +1,24 @@
1
+ import { Socket } from 'socket.io-client';
2
+ /**
3
+ * A hook that exposes `needToUpdate`, which indicates whether or not the client's
4
+ * bundle is stale and the new version has been marked as significant enough to
5
+ * notify the client that they should update. See `LatestVersionRequiringUpdate` in
6
+ * `@proteinjs/app-version-common` for more details on implementing the server-side logic.
7
+ *
8
+ * This hook will check with the server if the client should update when the socket
9
+ * receives a `connect` event.
10
+ *
11
+ * Reason:
12
+ *
13
+ * When a new version is deployed, the server process will terminate. The socket will always
14
+ * need to reconnect in this scenario. Additionally, an explicit `version-updated` event
15
+ * from the server can be missed (ie. the user's laptop is closed).
16
+ *
17
+ * @param currentVersion the version of the bundle currently on the client
18
+ * @param socket a Socket.IO `Socket`
19
+ * @returns `true` if the client should be notified to update to the latest version (ie. by reloading the page)
20
+ */
21
+ export declare const useVersionChecker: (currentVersion: string, socket: Socket | null) => {
22
+ needToUpdate: boolean;
23
+ };
24
+ //# sourceMappingURL=useVersionChecker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVersionChecker.d.ts","sourceRoot":"","sources":["../../src/useVersionChecker.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,mBAAoB,MAAM,UAAU,MAAM,GAAG,IAAI;;CAqC9E,CAAC"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.useVersionChecker = void 0;
13
+ const react_1 = require("react");
14
+ const app_version_common_1 = require("@proteinjs/app-version-common");
15
+ const util_1 = require("@proteinjs/util");
16
+ /**
17
+ * A hook that exposes `needToUpdate`, which indicates whether or not the client's
18
+ * bundle is stale and the new version has been marked as significant enough to
19
+ * notify the client that they should update. See `LatestVersionRequiringUpdate` in
20
+ * `@proteinjs/app-version-common` for more details on implementing the server-side logic.
21
+ *
22
+ * This hook will check with the server if the client should update when the socket
23
+ * receives a `connect` event.
24
+ *
25
+ * Reason:
26
+ *
27
+ * When a new version is deployed, the server process will terminate. The socket will always
28
+ * need to reconnect in this scenario. Additionally, an explicit `version-updated` event
29
+ * from the server can be missed (ie. the user's laptop is closed).
30
+ *
31
+ * @param currentVersion the version of the bundle currently on the client
32
+ * @param socket a Socket.IO `Socket`
33
+ * @returns `true` if the client should be notified to update to the latest version (ie. by reloading the page)
34
+ */
35
+ const useVersionChecker = (currentVersion, socket) => {
36
+ const [needToUpdate, setNeedToUpdate] = (0, react_1.useState)(false);
37
+ const debouncerRef = (0, react_1.useRef)(new util_1.Debouncer(1000));
38
+ const needToUpdateRef = (0, react_1.useRef)(needToUpdate); // having `needToUpdate` in the `checkVersion` dep array was causing double checks, despite debouncer
39
+ (0, react_1.useEffect)(() => {
40
+ needToUpdateRef.current = needToUpdate;
41
+ }, [needToUpdate]);
42
+ const checkVersion = (0, react_1.useCallback)(() => __awaiter(void 0, void 0, void 0, function* () {
43
+ try {
44
+ const newNeedToUpdate = yield (0, app_version_common_1.getVersionCheckerService)().needToUpdate(currentVersion);
45
+ if (needToUpdateRef.current !== newNeedToUpdate) {
46
+ setNeedToUpdate(newNeedToUpdate);
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error('Failed to check version:', error);
51
+ }
52
+ }), [currentVersion]);
53
+ (0, react_1.useEffect)(() => {
54
+ if (!socket) {
55
+ return;
56
+ }
57
+ const handleConnect = () => {
58
+ debouncerRef.current.debounce(checkVersion);
59
+ };
60
+ socket.on('connect', handleConnect);
61
+ return () => {
62
+ socket.off('connect', handleConnect);
63
+ };
64
+ }, [socket, checkVersion]);
65
+ return { needToUpdate };
66
+ };
67
+ exports.useVersionChecker = useVersionChecker;
68
+ //# sourceMappingURL=useVersionChecker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useVersionChecker.js","sourceRoot":"","sources":["../../src/useVersionChecker.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,iCAAiE;AACjE,sEAAyE;AAEzE,0CAA4C;AAE5C;;;;;;;;;;;;;;;;;;GAkBG;AACI,MAAM,iBAAiB,GAAG,CAAC,cAAsB,EAAE,MAAqB,EAAE,EAAE;IACjF,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,IAAA,cAAM,EAAY,IAAI,gBAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5D,MAAM,eAAe,GAAG,IAAA,cAAM,EAAC,YAAY,CAAC,CAAC,CAAC,qGAAqG;IAEnJ,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;IACzC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,MAAM,YAAY,GAAG,IAAA,mBAAW,EAAC,GAAS,EAAE;QAC1C,IAAI;YACF,MAAM,eAAe,GAAG,MAAM,IAAA,6CAAwB,GAAE,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;YACtF,IAAI,eAAe,CAAC,OAAO,KAAK,eAAe,EAAE;gBAC/C,eAAe,CAAC,eAAe,CAAC,CAAC;aAClC;SACF;QAAC,OAAO,KAAK,EAAE;YACd,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;SAClD;IACH,CAAC,CAAA,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM,EAAE;YACX,OAAO;SACR;QAED,MAAM,aAAa,GAAG,GAAG,EAAE;YACzB,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC9C,CAAC,CAAC;QAEF,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QAEpC,OAAO,GAAG,EAAE;YACV,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACvC,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IAE3B,OAAO,EAAE,YAAY,EAAE,CAAC;AAC1B,CAAC,CAAC;AArCW,QAAA,iBAAiB,qBAqC5B"}
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './src/useVersionChecker';
package/jest.config.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ roots: ['<rootDir>/test'],
3
+ transform: {
4
+ '^.+\\.tsx?$': 'ts-jest',
5
+ },
6
+ testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$',
7
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
8
+ testEnvironment: 'node',
9
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@proteinjs/app-version-ui",
3
+ "version": "1.0.1",
4
+ "description": "App version ui components",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/proteinjs/notifications.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/proteinjs/notifications/issues"
14
+ },
15
+ "homepage": "https://github.com/proteinjs/notifications#readme",
16
+ "author": "Brent Bahry",
17
+ "license": "MIT",
18
+ "scripts": {
19
+ "clean": "rm -rf dist/ node_modules/",
20
+ "build": "tsc",
21
+ "watch": "tsc -w -p ."
22
+ },
23
+ "dependencies": {
24
+ "@emotion/react": "11.11.1",
25
+ "@emotion/styled": "11.11.0",
26
+ "@mui/icons-material": "5.14.11",
27
+ "@mui/material": "5.14.11",
28
+ "@proteinjs/app-version-common": "^1.0.1",
29
+ "@proteinjs/util": "1.3.1",
30
+ "react": "18.2.0",
31
+ "react-dom": "18.2.0",
32
+ "socket.io-client": "4.7.5"
33
+ },
34
+ "devDependencies": {
35
+ "@types/jest": "29.5.5",
36
+ "@types/react": "18.2.23",
37
+ "@types/react-dom": "18.2.8",
38
+ "@typescript-eslint/eslint-plugin": "7.8.0",
39
+ "@typescript-eslint/parser": "7.8.0",
40
+ "eslint": "8.57.0",
41
+ "eslint-config-prettier": "9.1.0",
42
+ "eslint-plugin-prettier": "5.1.3",
43
+ "jest": "29.7.0",
44
+ "ts-jest": "29.1.1",
45
+ "typescript": "5.2.2"
46
+ },
47
+ "main": "./dist/generated/index.js",
48
+ "types": "./dist/generated/index.d.ts",
49
+ "gitHead": "38cee278cf7a5929023ca0d0ca346cdffc42662e"
50
+ }
@@ -0,0 +1,62 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { getVersionCheckerService } from '@proteinjs/app-version-common';
3
+ import { Socket } from 'socket.io-client';
4
+ import { Debouncer } from '@proteinjs/util';
5
+
6
+ /**
7
+ * A hook that exposes `needToUpdate`, which indicates whether or not the client's
8
+ * bundle is stale and the new version has been marked as significant enough to
9
+ * notify the client that they should update. See `LatestVersionRequiringUpdate` in
10
+ * `@proteinjs/app-version-common` for more details on implementing the server-side logic.
11
+ *
12
+ * This hook will check with the server if the client should update when the socket
13
+ * receives a `connect` event.
14
+ *
15
+ * Reason:
16
+ *
17
+ * When a new version is deployed, the server process will terminate. The socket will always
18
+ * need to reconnect in this scenario. Additionally, an explicit `version-updated` event
19
+ * from the server can be missed (ie. the user's laptop is closed).
20
+ *
21
+ * @param currentVersion the version of the bundle currently on the client
22
+ * @param socket a Socket.IO `Socket`
23
+ * @returns `true` if the client should be notified to update to the latest version (ie. by reloading the page)
24
+ */
25
+ export const useVersionChecker = (currentVersion: string, socket: Socket | null) => {
26
+ const [needToUpdate, setNeedToUpdate] = useState(false);
27
+ const debouncerRef = useRef<Debouncer>(new Debouncer(1000));
28
+ const needToUpdateRef = useRef(needToUpdate); // having `needToUpdate` in the `checkVersion` dep array was causing double checks, despite debouncer
29
+
30
+ useEffect(() => {
31
+ needToUpdateRef.current = needToUpdate;
32
+ }, [needToUpdate]);
33
+
34
+ const checkVersion = useCallback(async () => {
35
+ try {
36
+ const newNeedToUpdate = await getVersionCheckerService().needToUpdate(currentVersion);
37
+ if (needToUpdateRef.current !== newNeedToUpdate) {
38
+ setNeedToUpdate(newNeedToUpdate);
39
+ }
40
+ } catch (error) {
41
+ console.error('Failed to check version:', error);
42
+ }
43
+ }, [currentVersion]);
44
+
45
+ useEffect(() => {
46
+ if (!socket) {
47
+ return;
48
+ }
49
+
50
+ const handleConnect = () => {
51
+ debouncerRef.current.debounce(checkVersion);
52
+ };
53
+
54
+ socket.on('connect', handleConnect);
55
+
56
+ return () => {
57
+ socket.off('connect', handleConnect);
58
+ };
59
+ }, [socket, checkVersion]);
60
+
61
+ return { needToUpdate };
62
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "rootDir": "./",
4
+ "target": "es6",
5
+ "module": "commonjs",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true,
9
+ "outDir": "./dist/",
10
+ "strict": true,
11
+ "noImplicitAny": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "jsx": "react"
17
+ }
18
+ }