Skip to content

Commit 046dd20

Browse files
committed
♻️ Refactored env class
1 parent bfea6b9 commit 046dd20

File tree

1 file changed

+191
-1
lines changed
  • src/framework/controllers

1 file changed

+191
-1
lines changed

src/framework/controllers/env.ts

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,195 @@
1+
import { UUID } from "@codewithkyle/uuid";
2+
3+
export type NetworkType = "4g" | "3g" | "2g" | "slow-2g";
4+
5+
export type DOMState = "loading" | "idling" | "booting";
6+
7+
export type Browser = "chrome" | "safari" | "edge" | "chromium-edge" | "ie" | "firefox" | "unknown" | "opera";
8+
19
class Environment {
10+
public connection: NetworkType;
11+
public cpu: number;
12+
public memory: number | null;
13+
public domState: DOMState;
14+
public dataSaver: boolean;
15+
public browser: Browser;
16+
private tickets: Array<string>;
17+
18+
constructor() {
19+
this.memory = 4;
20+
this.cpu = window.navigator?.hardwareConcurrency || 2;
21+
this.connection = "4g";
22+
this.domState = "booting";
23+
this.dataSaver = false;
24+
this.browser = "unknown";
25+
this.tickets = [];
26+
}
27+
28+
public boot() {
29+
this.setBrowser();
30+
31+
if ("connection" in navigator) {
32+
// @ts-ignore
33+
this.connection = window.navigator.connection.effectiveType;
34+
// @ts-ignore
35+
this.dataSaver = window.navigator.connection.saveData;
36+
// @ts-ignore
37+
navigator.connection.onchange = this.handleNetworkChange.bind(this);
38+
}
39+
40+
if ("deviceMemory" in navigator) {
41+
// @ts-ignore
42+
this.memory = window.navigator.deviceMemory;
43+
}
44+
45+
if (this.tickets.length) {
46+
this.setDOMState("loading");
47+
} else {
48+
this.setDOMState("idling");
49+
}
50+
}
51+
52+
private handleNetworkChange: EventListener = () => {
53+
// @ts-ignore
54+
this.connection = window.navigator.connection.effectiveType;
55+
sessionStorage.removeItem("connection-choice");
56+
};
57+
58+
/**
59+
* Attempts to set the DOM to the `idling` state. The DOM will only idle when all `startLoading()` methods have been resolved.
60+
* @param ticket - the `string` the was provided by the `startLoading()` method.
61+
*/
62+
public stopLoading(ticket: string): void {
63+
if (!ticket || typeof ticket !== "string") {
64+
console.error(`A ticket with the typeof 'string' is required to end the loading state.`);
65+
return;
66+
}
67+
68+
const index = this.tickets.indexOf(ticket);
69+
if (index !== -1) {
70+
this.tickets.splice(index);
71+
}
72+
73+
if (this.tickets.length === 0 && this.domState === "loading") {
74+
this.setDOMState("idling");
75+
}
76+
}
77+
78+
/**
79+
* Sets the DOM to the `soft-loading` state.
80+
* @returns a ticket `string` that is required to stop the loading state.
81+
*/
82+
public startLoading(): string {
83+
if (this.domState === "idling") {
84+
this.setDOMState("loading");
85+
}
86+
const ticket = UUID();
87+
this.tickets.push(ticket);
88+
return ticket;
89+
}
90+
91+
/**
92+
* Sets the DOMs state attribute.
93+
* DO NOT USE THIS METHOD. DO NOT MANUALLY SET THE DOMs STATE.
94+
* @param newState - the new state of the document element
95+
*/
96+
private setDOMState(newState: DOMState): void {
97+
this.domState = newState;
98+
if (this.domState !== "loading") {
99+
this.tickets = [];
100+
}
101+
document.documentElement.setAttribute("state", this.domState);
102+
}
103+
104+
/**
105+
* Checks if the provided connection is greater than or equal to the current conneciton.
106+
* @param requiredConnection - network connection string
107+
*/
108+
public checkConnection(requiredConnection): boolean {
109+
let passed = false;
110+
switch (requiredConnection) {
111+
case "4g":
112+
if (this.connection !== "2g" && this.connection !== "slow-2g" && this.connection !== "3g") {
113+
passed = true;
114+
}
115+
break;
116+
case "3g":
117+
if (this.connection !== "2g" && this.connection !== "slow-2g") {
118+
passed = true;
119+
}
120+
break;
121+
case "2g":
122+
if (this.connection !== "slow-2g") {
123+
passed = true;
124+
}
125+
break;
126+
case "slow-2g":
127+
passed = true;
128+
break;
129+
default:
130+
passed = true;
131+
break;
132+
}
133+
return passed;
134+
}
135+
136+
private setBrowser() {
137+
// @ts-ignore
138+
const isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(" OPR/") >= 0;
139+
140+
// @ts-ignore
141+
const isFirefox = typeof InstallTrigger !== "undefined";
142+
143+
const isSafari =
144+
// @ts-ignore
145+
/constructor/i.test(window.HTMLElement) ||
146+
(function (p) {
147+
return p.toString() === "[object SafariRemoteNotification]";
148+
// @ts-ignore
149+
})(!window["safari"] || (typeof safari !== "undefined" && safari.pushNotification));
150+
151+
// @ts-ignore
152+
const isIE = /*@cc_on!@*/ false || !!document.documentMode;
153+
154+
// @ts-ignore
155+
const isEdge = !isIE && !!window.StyleMedia;
156+
157+
// @ts-ignore
158+
const isChrome = !!window.chrome;
159+
160+
const isEdgeChromium = isChrome && navigator.userAgent.indexOf("Edg") != -1;
161+
162+
if (isOpera) {
163+
this.browser = "opera";
164+
} else if (isFirefox) {
165+
this.browser = "firefox";
166+
} else if (isSafari) {
167+
this.browser = "safari";
168+
} else if (isIE) {
169+
this.browser = "ie";
170+
} else if (isEdge) {
171+
this.browser = "edge";
172+
} else if (isChrome) {
173+
this.browser = "chrome";
174+
} else if (isEdgeChromium) {
175+
this.browser = "chromium-edge";
176+
} else {
177+
this.browser = "unknown";
178+
}
179+
document.documentElement.setAttribute("browser", this.browser);
180+
}
181+
182+
/**
183+
* Binds the custom element to the class.
184+
* @deprecated use `bind()` instead.
185+
*/
2186
public mount(tagName: string, constructor: CustomElementConstructor) {
187+
this.bind(tagName, constructor);
188+
}
189+
/**
190+
* Registers a Web Component by binding the Custom Element's tag name to the provided class.
191+
*/
192+
public bind(tagName: string, constructor: CustomElementConstructor) {
3193
if (!customElements.get(tagName)) {
4194
customElements.define(tagName, constructor);
5195
}
@@ -53,4 +243,4 @@ class Environment {
53243
}
54244
}
55245
const env = new Environment();
56-
export { env as default };
246+
export { env as default };

0 commit comments

Comments
 (0)