improve loading states:
* app loader * specific loaders with spinner
This commit is contained in:
1 parent
29101b1daf
commit
931384b468
18 files changed
+867
-46
No files matched your search
@@ -0,0 +1,15 @@
|
|||||||
|
.ring {
|
||||||
|
display: inline-block;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: 3px solid rgba(0, 0, 0, 0.15);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: currentColor;
|
||||||
|
animation: spin 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
import styles from "./spinner.module.css";
|
||||||
|
|
||||||
|
export default function Spinner() {
|
||||||
|
return (
|
||||||
|
<div className="h-full flex">
|
||||||
|
<div className={clsx(styles.ring, "m-auto text-primary-400")} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ function NavLink({ path, label, icon }: NavLinkProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-around h-full">
|
<div className="flex flex-col items-center justify-around h-full">
|
||||||
<Link href={path}>
|
<Link href={path} prefetch={false}>
|
||||||
<a
|
<a
|
||||||
className={clsx("flex flex-col items-center", {
|
className={clsx("flex flex-col items-center", {
|
||||||
"text-primary-500": isActiveRoute,
|
"text-primary-500": isActiveRoute,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ErrorInfo, FunctionComponent } from "react";
|
import type { ErrorInfo, FunctionComponent } from "react";
|
||||||
import { Component } from "react";
|
import { Component, Suspense } from "react";
|
||||||
import {
|
import {
|
||||||
Head,
|
Head,
|
||||||
withRouter,
|
withRouter,
|
||||||
@@ -14,6 +14,7 @@ import type { WithRouterProps } from "next/dist/client/with-router";
|
|||||||
import appLogger from "../../../../integrations/logger";
|
import appLogger from "../../../../integrations/logger";
|
||||||
|
|
||||||
import Footer from "./footer";
|
import Footer from "./footer";
|
||||||
|
import Loader from "./loader";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -23,7 +24,7 @@ type Props = {
|
|||||||
|
|
||||||
const logger = appLogger.child({ module: "Layout" });
|
const logger = appLogger.child({ module: "Layout" });
|
||||||
|
|
||||||
const Layout: FunctionComponent<Props> = ({ children, title, pageTitle = title, hideFooter = false }) => {
|
const AppLayout: FunctionComponent<Props> = ({ children, title, pageTitle = title, hideFooter = false }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{pageTitle ? (
|
{pageTitle ? (
|
||||||
@@ -32,16 +33,18 @@ const Layout: FunctionComponent<Props> = ({ children, title, pageTitle = title,
|
|||||||
</Head>
|
</Head>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="h-full w-full overflow-hidden fixed bg-gray-100">
|
<Suspense fallback={<Loader />}>
|
||||||
<div className="flex flex-col w-full h-full">
|
<div className="h-full w-full overflow-hidden fixed bg-gray-100">
|
||||||
<div className="flex flex-col flex-1 w-full overflow-y-auto">
|
<div className="flex flex-col w-full h-full">
|
||||||
<main className="flex flex-col flex-1 my-0 h-full">
|
<div className="flex flex-col flex-1 w-full overflow-y-auto">
|
||||||
<ErrorBoundary>{children}</ErrorBoundary>
|
<main className="flex flex-col flex-1 my-0 h-full">
|
||||||
</main>
|
<ErrorBoundary>{children}</ErrorBoundary>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
{!hideFooter ? <Footer /> : null}
|
||||||
</div>
|
</div>
|
||||||
{!hideFooter ? <Footer /> : null}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Suspense>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -109,4 +112,4 @@ const ErrorBoundary = withRouter(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Layout;
|
export default AppLayout;
|
||||||
@@ -0,0 +1,749 @@
|
|||||||
|
/*
|
||||||
|
* Stripe WebGl Gradient Animation
|
||||||
|
* All Credits to Stripe.com
|
||||||
|
* ScrollObserver functionality to disable animation when not scrolled into view has been disabled and
|
||||||
|
* commented out for now.
|
||||||
|
* https://kevinhufnagl.com
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Converting colors to proper format
|
||||||
|
function normalizeColor(hexCode) {
|
||||||
|
return [((hexCode >> 16) & 255) / 255, ((hexCode >> 8) & 255) / 255, (255 & hexCode) / 255];
|
||||||
|
}
|
||||||
|
["SCREEN", "LINEAR_LIGHT"].reduce(
|
||||||
|
(hexCode, t, n) =>
|
||||||
|
Object.assign(hexCode, {
|
||||||
|
[t]: n,
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
//Essential functionality of WebGl
|
||||||
|
//t = width
|
||||||
|
//n = height
|
||||||
|
class MiniGl {
|
||||||
|
constructor(canvas, width, height, debug = false) {
|
||||||
|
const _miniGl = this,
|
||||||
|
debug_output = -1 !== document.location.search.toLowerCase().indexOf("debug=webgl");
|
||||||
|
(_miniGl.canvas = canvas),
|
||||||
|
(_miniGl.gl = _miniGl.canvas.getContext("webgl", {
|
||||||
|
antialias: true,
|
||||||
|
})),
|
||||||
|
(_miniGl.meshes = []);
|
||||||
|
const context = _miniGl.gl;
|
||||||
|
width && height && this.setSize(width, height),
|
||||||
|
_miniGl.lastDebugMsg,
|
||||||
|
(_miniGl.debug =
|
||||||
|
debug && debug_output
|
||||||
|
? function (e) {
|
||||||
|
const t = new Date();
|
||||||
|
t - _miniGl.lastDebugMsg > 1e3 && console.log("---"),
|
||||||
|
console.log(
|
||||||
|
t.toLocaleTimeString() + Array(Math.max(0, 32 - e.length)).join(" ") + e + ": ",
|
||||||
|
...Array.from(arguments).slice(1),
|
||||||
|
),
|
||||||
|
(_miniGl.lastDebugMsg = t);
|
||||||
|
}
|
||||||
|
: () => {}),
|
||||||
|
Object.defineProperties(_miniGl, {
|
||||||
|
Material: {
|
||||||
|
enumerable: false,
|
||||||
|
value: class {
|
||||||
|
constructor(vertexShaders, fragments, uniforms = {}) {
|
||||||
|
const material = this;
|
||||||
|
function getShaderByType(type, source) {
|
||||||
|
const shader = context.createShader(type);
|
||||||
|
return (
|
||||||
|
context.shaderSource(shader, source),
|
||||||
|
context.compileShader(shader),
|
||||||
|
context.getShaderParameter(shader, context.COMPILE_STATUS) ||
|
||||||
|
console.error(context.getShaderInfoLog(shader)),
|
||||||
|
_miniGl.debug("Material.compileShaderSource", {
|
||||||
|
source: source,
|
||||||
|
}),
|
||||||
|
shader
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function getUniformVariableDeclarations(uniforms, type) {
|
||||||
|
return Object.entries(uniforms)
|
||||||
|
.map(([uniform, value]) => value.getDeclaration(uniform, type))
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
(material.uniforms = uniforms), (material.uniformInstances = []);
|
||||||
|
|
||||||
|
const prefix = "\n precision highp float;\n ";
|
||||||
|
(material.vertexSource = `\n ${prefix}\n attribute vec4 position;\n attribute vec2 uv;\n attribute vec2 uvNorm;\n ${getUniformVariableDeclarations(
|
||||||
|
_miniGl.commonUniforms,
|
||||||
|
"vertex",
|
||||||
|
)}\n ${getUniformVariableDeclarations(
|
||||||
|
uniforms,
|
||||||
|
"vertex",
|
||||||
|
)}\n ${vertexShaders}\n `),
|
||||||
|
(material.Source = `\n ${prefix}\n ${getUniformVariableDeclarations(
|
||||||
|
_miniGl.commonUniforms,
|
||||||
|
"fragment",
|
||||||
|
)}\n ${getUniformVariableDeclarations(
|
||||||
|
uniforms,
|
||||||
|
"fragment",
|
||||||
|
)}\n ${fragments}\n `),
|
||||||
|
(material.vertexShader = getShaderByType(context.VERTEX_SHADER, material.vertexSource)),
|
||||||
|
(material.fragmentShader = getShaderByType(context.FRAGMENT_SHADER, material.Source)),
|
||||||
|
(material.program = context.createProgram()),
|
||||||
|
context.attachShader(material.program, material.vertexShader),
|
||||||
|
context.attachShader(material.program, material.fragmentShader),
|
||||||
|
context.linkProgram(material.program),
|
||||||
|
context.getProgramParameter(material.program, context.LINK_STATUS) ||
|
||||||
|
console.error(context.getProgramInfoLog(material.program)),
|
||||||
|
context.useProgram(material.program),
|
||||||
|
material.attachUniforms(void 0, _miniGl.commonUniforms),
|
||||||
|
material.attachUniforms(void 0, material.uniforms);
|
||||||
|
}
|
||||||
|
//t = uniform
|
||||||
|
attachUniforms(name, uniforms) {
|
||||||
|
//n = material
|
||||||
|
const material = this;
|
||||||
|
void 0 === name
|
||||||
|
? Object.entries(uniforms).forEach(([name, uniform]) => {
|
||||||
|
material.attachUniforms(name, uniform);
|
||||||
|
})
|
||||||
|
: "array" == uniforms.type
|
||||||
|
? uniforms.value.forEach((uniform, i) =>
|
||||||
|
material.attachUniforms(`${name}[${i}]`, uniform),
|
||||||
|
)
|
||||||
|
: "struct" == uniforms.type
|
||||||
|
? Object.entries(uniforms.value).forEach(([uniform, i]) =>
|
||||||
|
material.attachUniforms(`${name}.${uniform}`, i),
|
||||||
|
)
|
||||||
|
: (_miniGl.debug("Material.attachUniforms", {
|
||||||
|
name: name,
|
||||||
|
uniform: uniforms,
|
||||||
|
}),
|
||||||
|
material.uniformInstances.push({
|
||||||
|
uniform: uniforms,
|
||||||
|
location: context.getUniformLocation(material.program, name),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Uniform: {
|
||||||
|
enumerable: !1,
|
||||||
|
value: class {
|
||||||
|
constructor(e) {
|
||||||
|
(this.type = "float"), Object.assign(this, e);
|
||||||
|
(this.typeFn =
|
||||||
|
{
|
||||||
|
float: "1f",
|
||||||
|
int: "1i",
|
||||||
|
vec2: "2fv",
|
||||||
|
vec3: "3fv",
|
||||||
|
vec4: "4fv",
|
||||||
|
mat4: "Matrix4fv",
|
||||||
|
}[this.type] || "1f"),
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
update(value) {
|
||||||
|
void 0 !== this.value &&
|
||||||
|
context[`uniform${this.typeFn}`](
|
||||||
|
value,
|
||||||
|
0 === this.typeFn.indexOf("Matrix") ? this.transpose : this.value,
|
||||||
|
0 === this.typeFn.indexOf("Matrix") ? this.value : null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//e - name
|
||||||
|
//t - type
|
||||||
|
//n - length
|
||||||
|
getDeclaration(name, type, length) {
|
||||||
|
const uniform = this;
|
||||||
|
if (uniform.excludeFrom !== type) {
|
||||||
|
if ("array" === uniform.type)
|
||||||
|
return (
|
||||||
|
uniform.value[0].getDeclaration(name, type, uniform.value.length) +
|
||||||
|
`\nconst int ${name}_length = ${uniform.value.length};`
|
||||||
|
);
|
||||||
|
if ("struct" === uniform.type) {
|
||||||
|
let name_no_prefix = name.replace("u_", "");
|
||||||
|
return (
|
||||||
|
(name_no_prefix =
|
||||||
|
name_no_prefix.charAt(0).toUpperCase() + name_no_prefix.slice(1)),
|
||||||
|
`uniform struct ${name_no_prefix}
|
||||||
|
{\n` +
|
||||||
|
Object.entries(uniform.value)
|
||||||
|
.map(([name, uniform]) =>
|
||||||
|
uniform.getDeclaration(name, type).replace(/^uniform/, ""),
|
||||||
|
)
|
||||||
|
.join("") +
|
||||||
|
`\n} ${name}${length > 0 ? `[${length}]` : ""};`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return `uniform ${uniform.type} ${name}${length > 0 ? `[${length}]` : ""};`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PlaneGeometry: {
|
||||||
|
enumerable: !1,
|
||||||
|
value: class {
|
||||||
|
constructor(width, height, n, i, orientation) {
|
||||||
|
context.createBuffer(),
|
||||||
|
(this.attributes = {
|
||||||
|
position: new _miniGl.Attribute({
|
||||||
|
target: context.ARRAY_BUFFER,
|
||||||
|
size: 3,
|
||||||
|
}),
|
||||||
|
uv: new _miniGl.Attribute({
|
||||||
|
target: context.ARRAY_BUFFER,
|
||||||
|
size: 2,
|
||||||
|
}),
|
||||||
|
uvNorm: new _miniGl.Attribute({
|
||||||
|
target: context.ARRAY_BUFFER,
|
||||||
|
size: 2,
|
||||||
|
}),
|
||||||
|
index: new _miniGl.Attribute({
|
||||||
|
target: context.ELEMENT_ARRAY_BUFFER,
|
||||||
|
size: 3,
|
||||||
|
type: context.UNSIGNED_SHORT,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
this.setTopology(n, i),
|
||||||
|
this.setSize(width, height, orientation);
|
||||||
|
}
|
||||||
|
setTopology(e = 1, t = 1) {
|
||||||
|
const n = this;
|
||||||
|
(n.xSegCount = e),
|
||||||
|
(n.ySegCount = t),
|
||||||
|
(n.vertexCount = (n.xSegCount + 1) * (n.ySegCount + 1)),
|
||||||
|
(n.quadCount = n.xSegCount * n.ySegCount * 2),
|
||||||
|
(n.attributes.uv.values = new Float32Array(2 * n.vertexCount)),
|
||||||
|
(n.attributes.uvNorm.values = new Float32Array(2 * n.vertexCount)),
|
||||||
|
(n.attributes.index.values = new Uint16Array(3 * n.quadCount));
|
||||||
|
for (let e = 0; e <= n.ySegCount; e++)
|
||||||
|
for (let t = 0; t <= n.xSegCount; t++) {
|
||||||
|
const i = e * (n.xSegCount + 1) + t;
|
||||||
|
if (
|
||||||
|
((n.attributes.uv.values[2 * i] = t / n.xSegCount),
|
||||||
|
(n.attributes.uv.values[2 * i + 1] = 1 - e / n.ySegCount),
|
||||||
|
(n.attributes.uvNorm.values[2 * i] = (t / n.xSegCount) * 2 - 1),
|
||||||
|
(n.attributes.uvNorm.values[2 * i + 1] = 1 - (e / n.ySegCount) * 2),
|
||||||
|
t < n.xSegCount && e < n.ySegCount)
|
||||||
|
) {
|
||||||
|
const s = e * n.xSegCount + t;
|
||||||
|
(n.attributes.index.values[6 * s] = i),
|
||||||
|
(n.attributes.index.values[6 * s + 1] = i + 1 + n.xSegCount),
|
||||||
|
(n.attributes.index.values[6 * s + 2] = i + 1),
|
||||||
|
(n.attributes.index.values[6 * s + 3] = i + 1),
|
||||||
|
(n.attributes.index.values[6 * s + 4] = i + 1 + n.xSegCount),
|
||||||
|
(n.attributes.index.values[6 * s + 5] = i + 2 + n.xSegCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.attributes.uv.update(),
|
||||||
|
n.attributes.uvNorm.update(),
|
||||||
|
n.attributes.index.update(),
|
||||||
|
_miniGl.debug("Geometry.setTopology", {
|
||||||
|
uv: n.attributes.uv,
|
||||||
|
uvNorm: n.attributes.uvNorm,
|
||||||
|
index: n.attributes.index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSize(width = 1, height = 1, orientation = "xz") {
|
||||||
|
const geometry = this;
|
||||||
|
(geometry.width = width),
|
||||||
|
(geometry.height = height),
|
||||||
|
(geometry.orientation = orientation),
|
||||||
|
(geometry.attributes.position.values &&
|
||||||
|
geometry.attributes.position.values.length === 3 * geometry.vertexCount) ||
|
||||||
|
(geometry.attributes.position.values = new Float32Array(3 * geometry.vertexCount));
|
||||||
|
const o = width / -2,
|
||||||
|
r = height / -2,
|
||||||
|
segment_width = width / geometry.xSegCount,
|
||||||
|
segment_height = height / geometry.ySegCount;
|
||||||
|
for (let yIndex = 0; yIndex <= geometry.ySegCount; yIndex++) {
|
||||||
|
const t = r + yIndex * segment_height;
|
||||||
|
for (let xIndex = 0; xIndex <= geometry.xSegCount; xIndex++) {
|
||||||
|
const r = o + xIndex * segment_width,
|
||||||
|
l = yIndex * (geometry.xSegCount + 1) + xIndex;
|
||||||
|
(geometry.attributes.position.values[3 * l + "xyz".indexOf(orientation[0])] = r),
|
||||||
|
(geometry.attributes.position.values[3 * l + "xyz".indexOf(orientation[1])] =
|
||||||
|
-t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
geometry.attributes.position.update(),
|
||||||
|
_miniGl.debug("Geometry.setSize", {
|
||||||
|
position: geometry.attributes.position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Mesh: {
|
||||||
|
enumerable: !1,
|
||||||
|
value: class {
|
||||||
|
constructor(geometry, material) {
|
||||||
|
const mesh = this;
|
||||||
|
(mesh.geometry = geometry),
|
||||||
|
(mesh.material = material),
|
||||||
|
(mesh.wireframe = !1),
|
||||||
|
(mesh.attributeInstances = []),
|
||||||
|
Object.entries(mesh.geometry.attributes).forEach(([e, attribute]) => {
|
||||||
|
mesh.attributeInstances.push({
|
||||||
|
attribute: attribute,
|
||||||
|
location: attribute.attach(e, mesh.material.program),
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
_miniGl.meshes.push(mesh),
|
||||||
|
_miniGl.debug("Mesh.constructor", {
|
||||||
|
mesh: mesh,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
context.useProgram(this.material.program),
|
||||||
|
this.material.uniformInstances.forEach(({ uniform: e, location: t }) => e.update(t)),
|
||||||
|
this.attributeInstances.forEach(({ attribute: e, location: t }) => e.use(t)),
|
||||||
|
context.drawElements(
|
||||||
|
this.wireframe ? context.LINES : context.TRIANGLES,
|
||||||
|
this.geometry.attributes.index.values.length,
|
||||||
|
context.UNSIGNED_SHORT,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
_miniGl.meshes = _miniGl.meshes.filter((e) => e != this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Attribute: {
|
||||||
|
enumerable: !1,
|
||||||
|
value: class {
|
||||||
|
constructor(e) {
|
||||||
|
(this.type = context.FLOAT),
|
||||||
|
(this.normalized = !1),
|
||||||
|
(this.buffer = context.createBuffer()),
|
||||||
|
Object.assign(this, e),
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
void 0 !== this.values &&
|
||||||
|
(context.bindBuffer(this.target, this.buffer),
|
||||||
|
context.bufferData(this.target, this.values, context.STATIC_DRAW));
|
||||||
|
}
|
||||||
|
attach(e, t) {
|
||||||
|
const n = context.getAttribLocation(t, e);
|
||||||
|
return (
|
||||||
|
this.target === context.ARRAY_BUFFER &&
|
||||||
|
(context.enableVertexAttribArray(n),
|
||||||
|
context.vertexAttribPointer(n, this.size, this.type, this.normalized, 0, 0)),
|
||||||
|
n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
use(e) {
|
||||||
|
context.bindBuffer(this.target, this.buffer),
|
||||||
|
this.target === context.ARRAY_BUFFER &&
|
||||||
|
(context.enableVertexAttribArray(e),
|
||||||
|
context.vertexAttribPointer(e, this.size, this.type, this.normalized, 0, 0));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const a = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
||||||
|
_miniGl.commonUniforms = {
|
||||||
|
projectionMatrix: new _miniGl.Uniform({
|
||||||
|
type: "mat4",
|
||||||
|
value: a,
|
||||||
|
}),
|
||||||
|
modelViewMatrix: new _miniGl.Uniform({
|
||||||
|
type: "mat4",
|
||||||
|
value: a,
|
||||||
|
}),
|
||||||
|
resolution: new _miniGl.Uniform({
|
||||||
|
type: "vec2",
|
||||||
|
value: [1, 1],
|
||||||
|
}),
|
||||||
|
aspectRatio: new _miniGl.Uniform({
|
||||||
|
type: "float",
|
||||||
|
value: 1,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
setSize(e = 640, t = 480) {
|
||||||
|
(this.width = e),
|
||||||
|
(this.height = t),
|
||||||
|
(this.canvas.width = e),
|
||||||
|
(this.canvas.height = t),
|
||||||
|
this.gl.viewport(0, 0, e, t),
|
||||||
|
(this.commonUniforms.resolution.value = [e, t]),
|
||||||
|
(this.commonUniforms.aspectRatio.value = e / t),
|
||||||
|
this.debug("MiniGL.setSize", {
|
||||||
|
width: e,
|
||||||
|
height: t,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//left, right, top, bottom, near, far
|
||||||
|
setOrthographicCamera(e = 0, t = 0, n = 0, i = -2e3, s = 2e3) {
|
||||||
|
(this.commonUniforms.projectionMatrix.value = [
|
||||||
|
2 / this.width,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2 / this.height,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2 / (i - s),
|
||||||
|
0,
|
||||||
|
e,
|
||||||
|
t,
|
||||||
|
n,
|
||||||
|
1,
|
||||||
|
]),
|
||||||
|
this.debug("setOrthographicCamera", this.commonUniforms.projectionMatrix.value);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
this.gl.clearColor(0, 0, 0, 0), this.gl.clearDepth(1), this.meshes.forEach((e) => e.draw());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sets initial properties
|
||||||
|
function e(object, propertyName, val) {
|
||||||
|
return (
|
||||||
|
propertyName in object
|
||||||
|
? Object.defineProperty(object, propertyName, {
|
||||||
|
value: val,
|
||||||
|
enumerable: !0,
|
||||||
|
configurable: !0,
|
||||||
|
writable: !0,
|
||||||
|
})
|
||||||
|
: (object[propertyName] = val),
|
||||||
|
object
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Gradient object
|
||||||
|
class Gradient {
|
||||||
|
constructor(...t) {
|
||||||
|
e(this, "el", void 0),
|
||||||
|
e(this, "cssVarRetries", 0),
|
||||||
|
e(this, "maxCssVarRetries", 200),
|
||||||
|
e(this, "angle", 0),
|
||||||
|
e(this, "isLoadedClass", !1),
|
||||||
|
e(this, "isScrolling", !1),
|
||||||
|
/*e(this, "isStatic", o.disableAmbientAnimations()),*/ e(this, "scrollingTimeout", void 0),
|
||||||
|
e(this, "scrollingRefreshDelay", 200),
|
||||||
|
e(this, "isIntersecting", !1),
|
||||||
|
e(this, "shaderFiles", void 0),
|
||||||
|
e(this, "vertexShader", void 0),
|
||||||
|
e(this, "sectionColors", void 0),
|
||||||
|
e(this, "computedCanvasStyle", void 0),
|
||||||
|
e(this, "conf", void 0),
|
||||||
|
e(this, "uniforms", void 0),
|
||||||
|
e(this, "t", 1253106),
|
||||||
|
e(this, "last", 0),
|
||||||
|
e(this, "width", void 0),
|
||||||
|
e(this, "minWidth", 1111),
|
||||||
|
e(this, "height", 600),
|
||||||
|
e(this, "xSegCount", void 0),
|
||||||
|
e(this, "ySegCount", void 0),
|
||||||
|
e(this, "mesh", void 0),
|
||||||
|
e(this, "material", void 0),
|
||||||
|
e(this, "geometry", void 0),
|
||||||
|
e(this, "minigl", void 0),
|
||||||
|
e(this, "scrollObserver", void 0),
|
||||||
|
e(this, "amp", 320),
|
||||||
|
e(this, "seed", 5),
|
||||||
|
e(this, "freqX", 14e-5),
|
||||||
|
e(this, "freqY", 29e-5),
|
||||||
|
e(this, "freqDelta", 1e-5),
|
||||||
|
e(this, "activeColors", [1, 1, 1, 1]),
|
||||||
|
e(this, "isMetaKey", !1),
|
||||||
|
e(this, "isGradientLegendVisible", !1),
|
||||||
|
e(this, "isMouseDown", !1),
|
||||||
|
e(this, "handleScroll", () => {
|
||||||
|
clearTimeout(this.scrollingTimeout),
|
||||||
|
(this.scrollingTimeout = setTimeout(this.handleScrollEnd, this.scrollingRefreshDelay)),
|
||||||
|
this.isGradientLegendVisible && this.hideGradientLegend(),
|
||||||
|
this.conf.playing && ((this.isScrolling = !0), this.pause());
|
||||||
|
}),
|
||||||
|
e(this, "handleScrollEnd", () => {
|
||||||
|
(this.isScrolling = !1), this.isIntersecting && this.play();
|
||||||
|
}),
|
||||||
|
e(this, "resize", () => {
|
||||||
|
(this.width = window.innerWidth),
|
||||||
|
this.minigl.setSize(this.width, this.height),
|
||||||
|
this.minigl.setOrthographicCamera(),
|
||||||
|
(this.xSegCount = Math.ceil(this.width * this.conf.density[0])),
|
||||||
|
(this.ySegCount = Math.ceil(this.height * this.conf.density[1])),
|
||||||
|
this.mesh.geometry.setTopology(this.xSegCount, this.ySegCount),
|
||||||
|
this.mesh.geometry.setSize(this.width, this.height),
|
||||||
|
(this.mesh.material.uniforms.u_shadow_power.value = this.width < 600 ? 5 : 6);
|
||||||
|
}),
|
||||||
|
e(this, "handleMouseDown", (e) => {
|
||||||
|
this.isGradientLegendVisible &&
|
||||||
|
((this.isMetaKey = e.metaKey),
|
||||||
|
(this.isMouseDown = !0),
|
||||||
|
!1 === this.conf.playing && requestAnimationFrame(this.animate));
|
||||||
|
}),
|
||||||
|
e(this, "handleMouseUp", () => {
|
||||||
|
this.isMouseDown = !1;
|
||||||
|
}),
|
||||||
|
e(this, "animate", (e) => {
|
||||||
|
if (!this.shouldSkipFrame(e) || this.isMouseDown) {
|
||||||
|
if (((this.t += Math.min(e - this.last, 1e3 / 15)), (this.last = e), this.isMouseDown)) {
|
||||||
|
let e = 160;
|
||||||
|
this.isMetaKey && (e = -160), (this.t += e);
|
||||||
|
}
|
||||||
|
(this.mesh.material.uniforms.u_time.value = this.t), this.minigl.render();
|
||||||
|
}
|
||||||
|
if (0 !== this.last && this.isStatic) return this.minigl.render(), void this.disconnect();
|
||||||
|
/*this.isIntersecting && */ (this.conf.playing || this.isMouseDown) &&
|
||||||
|
requestAnimationFrame(this.animate);
|
||||||
|
}),
|
||||||
|
e(this, "addIsLoadedClass", () => {
|
||||||
|
/*this.isIntersecting && */ !this.isLoadedClass &&
|
||||||
|
((this.isLoadedClass = !0),
|
||||||
|
this.el.classList.add("isLoaded"),
|
||||||
|
setTimeout(() => {
|
||||||
|
this.el.parentElement.classList.add("isLoaded");
|
||||||
|
}, 3e3));
|
||||||
|
}),
|
||||||
|
e(this, "pause", () => {
|
||||||
|
this.conf.playing = false;
|
||||||
|
}),
|
||||||
|
e(this, "play", () => {
|
||||||
|
requestAnimationFrame(this.animate), (this.conf.playing = true);
|
||||||
|
}),
|
||||||
|
e(this, "initGradient", (selector) => {
|
||||||
|
this.el = document.querySelector(selector);
|
||||||
|
this.connect();
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async connect() {
|
||||||
|
(this.shaderFiles = {
|
||||||
|
vertex: "varying vec3 v_color;\n\nvoid main() {\n float time = u_time * u_global.noiseSpeed;\n\n vec2 noiseCoord = resolution * uvNorm * u_global.noiseFreq;\n\n vec2 st = 1. - uvNorm.xy;\n\n //\n // Tilting the plane\n //\n\n // Front-to-back tilt\n float tilt = resolution.y / 2.0 * uvNorm.y;\n\n // Left-to-right angle\n float incline = resolution.x * uvNorm.x / 2.0 * u_vertDeform.incline;\n\n // Up-down shift to offset incline\n float offset = resolution.x / 2.0 * u_vertDeform.incline * mix(u_vertDeform.offsetBottom, u_vertDeform.offsetTop, uv.y);\n\n //\n // Vertex noise\n //\n\n float noise = snoise(vec3(\n noiseCoord.x * u_vertDeform.noiseFreq.x + time * u_vertDeform.noiseFlow,\n noiseCoord.y * u_vertDeform.noiseFreq.y,\n time * u_vertDeform.noiseSpeed + u_vertDeform.noiseSeed\n )) * u_vertDeform.noiseAmp;\n\n // Fade noise to zero at edges\n noise *= 1.0 - pow(abs(uvNorm.y), 2.0);\n\n // Clamp to 0\n noise = max(0.0, noise);\n\n vec3 pos = vec3(\n position.x,\n position.y + tilt + incline + noise - offset,\n position.z\n );\n\n //\n // Vertex color, to be passed to fragment shader\n //\n\n if (u_active_colors[0] == 1.) {\n v_color = u_baseColor;\n }\n\n for (int i = 0; i < u_waveLayers_length; i++) {\n if (u_active_colors[i + 1] == 1.) {\n WaveLayers layer = u_waveLayers[i];\n\n float noise = smoothstep(\n layer.noiseFloor,\n layer.noiseCeil,\n snoise(vec3(\n noiseCoord.x * layer.noiseFreq.x + time * layer.noiseFlow,\n noiseCoord.y * layer.noiseFreq.y,\n time * layer.noiseSpeed + layer.noiseSeed\n )) / 2.0 + 0.5\n );\n\n v_color = blendNormal(v_color, layer.color, pow(noise, 4.));\n }\n }\n\n //\n // Finish\n //\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);\n}",
|
||||||
|
noise: "//\n// Description : Array and textureless GLSL 2D/3D/4D simplex\n// noise functions.\n// Author : Ian McEwan, Ashima Arts.\n// Maintainer : stegu\n// Lastmod : 20110822 (ijm)\n// License : Copyright (C) 2011 Ashima Arts. All rights reserved.\n// Distributed under the MIT License. See LICENSE file.\n// https://github.com/ashima/webgl-noise\n// https://github.com/stegu/webgl-noise\n//\n\nvec3 mod289(vec3 x) {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nvec4 mod289(vec4 x) {\n return x - floor(x * (1.0 / 289.0)) * 289.0;\n}\n\nvec4 permute(vec4 x) {\n return mod289(((x*34.0)+1.0)*x);\n}\n\nvec4 taylorInvSqrt(vec4 r)\n{\n return 1.79284291400159 - 0.85373472095314 * r;\n}\n\nfloat snoise(vec3 v)\n{\n const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;\n const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\n// First corner\n vec3 i = floor(v + dot(v, C.yyy) );\n vec3 x0 = v - i + dot(i, C.xxx) ;\n\n// Other corners\n vec3 g = step(x0.yzx, x0.xyz);\n vec3 l = 1.0 - g;\n vec3 i1 = min( g.xyz, l.zxy );\n vec3 i2 = max( g.xyz, l.zxy );\n\n // x0 = x0 - 0.0 + 0.0 * C.xxx;\n // x1 = x0 - i1 + 1.0 * C.xxx;\n // x2 = x0 - i2 + 2.0 * C.xxx;\n // x3 = x0 - 1.0 + 3.0 * C.xxx;\n vec3 x1 = x0 - i1 + C.xxx;\n vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y\n vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y\n\n// Permutations\n i = mod289(i);\n vec4 p = permute( permute( permute(\n i.z + vec4(0.0, i1.z, i2.z, 1.0 ))\n + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))\n + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));\n\n// Gradients: 7x7 points over a square, mapped onto an octahedron.\n// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)\n float n_ = 0.142857142857; // 1.0/7.0\n vec3 ns = n_ * D.wyz - D.xzx;\n\n vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)\n\n vec4 x_ = floor(j * ns.z);\n vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\n vec4 x = x_ *ns.x + ns.yyyy;\n vec4 y = y_ *ns.x + ns.yyyy;\n vec4 h = 1.0 - abs(x) - abs(y);\n\n vec4 b0 = vec4( x.xy, y.xy );\n vec4 b1 = vec4( x.zw, y.zw );\n\n //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;\n //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;\n vec4 s0 = floor(b0)*2.0 + 1.0;\n vec4 s1 = floor(b1)*2.0 + 1.0;\n vec4 sh = -step(h, vec4(0.0));\n\n vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;\n vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;\n\n vec3 p0 = vec3(a0.xy,h.x);\n vec3 p1 = vec3(a0.zw,h.y);\n vec3 p2 = vec3(a1.xy,h.z);\n vec3 p3 = vec3(a1.zw,h.w);\n\n//Normalise gradients\n vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n p0 *= norm.x;\n p1 *= norm.y;\n p2 *= norm.z;\n p3 *= norm.w;\n\n// Mix final noise value\n vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n m = m * m;\n return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),\n dot(p2,x2), dot(p3,x3) ) );\n}",
|
||||||
|
blend: "//\n// https://github.com/jamieowen/glsl-blend\n//\n\n// Normal\n\nvec3 blendNormal(vec3 base, vec3 blend) {\n\treturn blend;\n}\n\nvec3 blendNormal(vec3 base, vec3 blend, float opacity) {\n\treturn (blendNormal(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Screen\n\nfloat blendScreen(float base, float blend) {\n\treturn 1.0-((1.0-base)*(1.0-blend));\n}\n\nvec3 blendScreen(vec3 base, vec3 blend) {\n\treturn vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));\n}\n\nvec3 blendScreen(vec3 base, vec3 blend, float opacity) {\n\treturn (blendScreen(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Multiply\n\nvec3 blendMultiply(vec3 base, vec3 blend) {\n\treturn base*blend;\n}\n\nvec3 blendMultiply(vec3 base, vec3 blend, float opacity) {\n\treturn (blendMultiply(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Overlay\n\nfloat blendOverlay(float base, float blend) {\n\treturn base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));\n}\n\nvec3 blendOverlay(vec3 base, vec3 blend) {\n\treturn vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b));\n}\n\nvec3 blendOverlay(vec3 base, vec3 blend, float opacity) {\n\treturn (blendOverlay(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Hard light\n\nvec3 blendHardLight(vec3 base, vec3 blend) {\n\treturn blendOverlay(blend,base);\n}\n\nvec3 blendHardLight(vec3 base, vec3 blend, float opacity) {\n\treturn (blendHardLight(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Soft light\n\nfloat blendSoftLight(float base, float blend) {\n\treturn (blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend));\n}\n\nvec3 blendSoftLight(vec3 base, vec3 blend) {\n\treturn vec3(blendSoftLight(base.r,blend.r),blendSoftLight(base.g,blend.g),blendSoftLight(base.b,blend.b));\n}\n\nvec3 blendSoftLight(vec3 base, vec3 blend, float opacity) {\n\treturn (blendSoftLight(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Color dodge\n\nfloat blendColorDodge(float base, float blend) {\n\treturn (blend==1.0)?blend:min(base/(1.0-blend),1.0);\n}\n\nvec3 blendColorDodge(vec3 base, vec3 blend) {\n\treturn vec3(blendColorDodge(base.r,blend.r),blendColorDodge(base.g,blend.g),blendColorDodge(base.b,blend.b));\n}\n\nvec3 blendColorDodge(vec3 base, vec3 blend, float opacity) {\n\treturn (blendColorDodge(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Color burn\n\nfloat blendColorBurn(float base, float blend) {\n\treturn (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0);\n}\n\nvec3 blendColorBurn(vec3 base, vec3 blend) {\n\treturn vec3(blendColorBurn(base.r,blend.r),blendColorBurn(base.g,blend.g),blendColorBurn(base.b,blend.b));\n}\n\nvec3 blendColorBurn(vec3 base, vec3 blend, float opacity) {\n\treturn (blendColorBurn(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Vivid Light\n\nfloat blendVividLight(float base, float blend) {\n\treturn (blend<0.5)?blendColorBurn(base,(2.0*blend)):blendColorDodge(base,(2.0*(blend-0.5)));\n}\n\nvec3 blendVividLight(vec3 base, vec3 blend) {\n\treturn vec3(blendVividLight(base.r,blend.r),blendVividLight(base.g,blend.g),blendVividLight(base.b,blend.b));\n}\n\nvec3 blendVividLight(vec3 base, vec3 blend, float opacity) {\n\treturn (blendVividLight(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Lighten\n\nfloat blendLighten(float base, float blend) {\n\treturn max(blend,base);\n}\n\nvec3 blendLighten(vec3 base, vec3 blend) {\n\treturn vec3(blendLighten(base.r,blend.r),blendLighten(base.g,blend.g),blendLighten(base.b,blend.b));\n}\n\nvec3 blendLighten(vec3 base, vec3 blend, float opacity) {\n\treturn (blendLighten(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Linear burn\n\nfloat blendLinearBurn(float base, float blend) {\n\t// Note : Same implementation as BlendSubtractf\n\treturn max(base+blend-1.0,0.0);\n}\n\nvec3 blendLinearBurn(vec3 base, vec3 blend) {\n\t// Note : Same implementation as BlendSubtract\n\treturn max(base+blend-vec3(1.0),vec3(0.0));\n}\n\nvec3 blendLinearBurn(vec3 base, vec3 blend, float opacity) {\n\treturn (blendLinearBurn(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Linear dodge\n\nfloat blendLinearDodge(float base, float blend) {\n\t// Note : Same implementation as BlendAddf\n\treturn min(base+blend,1.0);\n}\n\nvec3 blendLinearDodge(vec3 base, vec3 blend) {\n\t// Note : Same implementation as BlendAdd\n\treturn min(base+blend,vec3(1.0));\n}\n\nvec3 blendLinearDodge(vec3 base, vec3 blend, float opacity) {\n\treturn (blendLinearDodge(base, blend) * opacity + base * (1.0 - opacity));\n}\n\n// Linear light\n\nfloat blendLinearLight(float base, float blend) {\n\treturn blend<0.5?blendLinearBurn(base,(2.0*blend)):blendLinearDodge(base,(2.0*(blend-0.5)));\n}\n\nvec3 blendLinearLight(vec3 base, vec3 blend) {\n\treturn vec3(blendLinearLight(base.r,blend.r),blendLinearLight(base.g,blend.g),blendLinearLight(ba
Line truncated
|
||||||
|
fragment:
|
||||||
|
"varying vec3 v_color;\n\nvoid main() {\n vec3 color = v_color;\n if (u_darken_top == 1.0) {\n vec2 st = gl_FragCoord.xy/resolution.xy;\n color.g -= pow(st.y + sin(-12.0) * st.x, u_shadow_power) * 0.4;\n }\n gl_FragColor = vec4(color, 1.0);\n}",
|
||||||
|
}),
|
||||||
|
(this.conf = {
|
||||||
|
presetName: "",
|
||||||
|
wireframe: false,
|
||||||
|
density: [0.06, 0.16],
|
||||||
|
zoom: 1,
|
||||||
|
rotation: 0,
|
||||||
|
playing: true,
|
||||||
|
}),
|
||||||
|
document.querySelectorAll("canvas").length < 1
|
||||||
|
? console.log("DID NOT LOAD HERO STRIPE CANVAS")
|
||||||
|
: ((this.minigl = new MiniGl(this.el, null, null, !0)),
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.el && ((this.computedCanvasStyle = getComputedStyle(this.el)), this.waitForCssVars());
|
||||||
|
}));
|
||||||
|
/*
|
||||||
|
this.scrollObserver = await s.create(.1, !1),
|
||||||
|
this.scrollObserver.observe(this.el),
|
||||||
|
this.scrollObserver.onSeparate(() => {
|
||||||
|
window.removeEventListener("scroll", this.handleScroll), window.removeEventListener("mousedown", this.handleMouseDown), window.removeEventListener("mouseup", this.handleMouseUp), window.removeEventListener("keydown", this.handleKeyDown), this.isIntersecting = !1, this.conf.playing && this.pause()
|
||||||
|
}),
|
||||||
|
this.scrollObserver.onIntersect(() => {
|
||||||
|
window.addEventListener("scroll", this.handleScroll), window.addEventListener("mousedown", this.handleMouseDown), window.addEventListener("mouseup", this.handleMouseUp), window.addEventListener("keydown", this.handleKeyDown), this.isIntersecting = !0, this.addIsLoadedClass(), this.play()
|
||||||
|
})*/
|
||||||
|
}
|
||||||
|
disconnect() {
|
||||||
|
this.scrollObserver &&
|
||||||
|
(window.removeEventListener("scroll", this.handleScroll),
|
||||||
|
window.removeEventListener("mousedown", this.handleMouseDown),
|
||||||
|
window.removeEventListener("mouseup", this.handleMouseUp),
|
||||||
|
window.removeEventListener("keydown", this.handleKeyDown),
|
||||||
|
this.scrollObserver.disconnect()),
|
||||||
|
window.removeEventListener("resize", this.resize);
|
||||||
|
}
|
||||||
|
initMaterial() {
|
||||||
|
this.uniforms = {
|
||||||
|
u_time: new this.minigl.Uniform({
|
||||||
|
value: 0,
|
||||||
|
}),
|
||||||
|
u_shadow_power: new this.minigl.Uniform({
|
||||||
|
value: 5,
|
||||||
|
}),
|
||||||
|
u_darken_top: new this.minigl.Uniform({
|
||||||
|
value: "" === this.el.dataset.jsDarkenTop ? 1 : 0,
|
||||||
|
}),
|
||||||
|
u_active_colors: new this.minigl.Uniform({
|
||||||
|
value: this.activeColors,
|
||||||
|
type: "vec4",
|
||||||
|
}),
|
||||||
|
u_global: new this.minigl.Uniform({
|
||||||
|
value: {
|
||||||
|
noiseFreq: new this.minigl.Uniform({
|
||||||
|
value: [this.freqX, this.freqY],
|
||||||
|
type: "vec2",
|
||||||
|
}),
|
||||||
|
noiseSpeed: new this.minigl.Uniform({
|
||||||
|
value: 5e-6,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
type: "struct",
|
||||||
|
}),
|
||||||
|
u_vertDeform: new this.minigl.Uniform({
|
||||||
|
value: {
|
||||||
|
incline: new this.minigl.Uniform({
|
||||||
|
value: Math.sin(this.angle) / Math.cos(this.angle),
|
||||||
|
}),
|
||||||
|
offsetTop: new this.minigl.Uniform({
|
||||||
|
value: -0.5,
|
||||||
|
}),
|
||||||
|
offsetBottom: new this.minigl.Uniform({
|
||||||
|
value: -0.5,
|
||||||
|
}),
|
||||||
|
noiseFreq: new this.minigl.Uniform({
|
||||||
|
value: [3, 4],
|
||||||
|
type: "vec2",
|
||||||
|
}),
|
||||||
|
noiseAmp: new this.minigl.Uniform({
|
||||||
|
value: this.amp,
|
||||||
|
}),
|
||||||
|
noiseSpeed: new this.minigl.Uniform({
|
||||||
|
value: 10,
|
||||||
|
}),
|
||||||
|
noiseFlow: new this.minigl.Uniform({
|
||||||
|
value: 3,
|
||||||
|
}),
|
||||||
|
noiseSeed: new this.minigl.Uniform({
|
||||||
|
value: this.seed,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
type: "struct",
|
||||||
|
excludeFrom: "fragment",
|
||||||
|
}),
|
||||||
|
u_baseColor: new this.minigl.Uniform({
|
||||||
|
value: this.sectionColors[0],
|
||||||
|
type: "vec3",
|
||||||
|
excludeFrom: "fragment",
|
||||||
|
}),
|
||||||
|
u_waveLayers: new this.minigl.Uniform({
|
||||||
|
value: [],
|
||||||
|
excludeFrom: "fragment",
|
||||||
|
type: "array",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
for (let e = 1; e < this.sectionColors.length; e += 1)
|
||||||
|
this.uniforms.u_waveLayers.value.push(
|
||||||
|
new this.minigl.Uniform({
|
||||||
|
value: {
|
||||||
|
color: new this.minigl.Uniform({
|
||||||
|
value: this.sectionColors[e],
|
||||||
|
type: "vec3",
|
||||||
|
}),
|
||||||
|
noiseFreq: new this.minigl.Uniform({
|
||||||
|
value: [2 + e / this.sectionColors.length, 3 + e / this.sectionColors.length],
|
||||||
|
type: "vec2",
|
||||||
|
}),
|
||||||
|
noiseSpeed: new this.minigl.Uniform({
|
||||||
|
value: 11 + 0.3 * e,
|
||||||
|
}),
|
||||||
|
noiseFlow: new this.minigl.Uniform({
|
||||||
|
value: 6.5 + 0.3 * e,
|
||||||
|
}),
|
||||||
|
noiseSeed: new this.minigl.Uniform({
|
||||||
|
value: this.seed + 10 * e,
|
||||||
|
}),
|
||||||
|
noiseFloor: new this.minigl.Uniform({
|
||||||
|
value: 0.1,
|
||||||
|
}),
|
||||||
|
noiseCeil: new this.minigl.Uniform({
|
||||||
|
value: 0.63 + 0.07 * e,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
type: "struct",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
(this.vertexShader = [this.shaderFiles.noise, this.shaderFiles.blend, this.shaderFiles.vertex].join(
|
||||||
|
"\n\n",
|
||||||
|
)),
|
||||||
|
new this.minigl.Material(this.vertexShader, this.shaderFiles.fragment, this.uniforms)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
initMesh() {
|
||||||
|
(this.material = this.initMaterial()),
|
||||||
|
(this.geometry = new this.minigl.PlaneGeometry()),
|
||||||
|
(this.mesh = new this.minigl.Mesh(this.geometry, this.material));
|
||||||
|
}
|
||||||
|
shouldSkipFrame(e) {
|
||||||
|
return !!window.document.hidden || !this.conf.playing || parseInt(e, 10) % 2 == 0 || void 0;
|
||||||
|
}
|
||||||
|
updateFrequency(e) {
|
||||||
|
(this.freqX += e), (this.freqY += e);
|
||||||
|
}
|
||||||
|
toggleColor(index) {
|
||||||
|
this.activeColors[index] = 0 === this.activeColors[index] ? 1 : 0;
|
||||||
|
}
|
||||||
|
showGradientLegend() {
|
||||||
|
this.width > this.minWidth &&
|
||||||
|
((this.isGradientLegendVisible = !0), document.body.classList.add("isGradientLegendVisible"));
|
||||||
|
}
|
||||||
|
hideGradientLegend() {
|
||||||
|
(this.isGradientLegendVisible = !1), document.body.classList.remove("isGradientLegendVisible");
|
||||||
|
}
|
||||||
|
init() {
|
||||||
|
this.initGradientColors(),
|
||||||
|
this.initMesh(),
|
||||||
|
this.resize(),
|
||||||
|
requestAnimationFrame(this.animate),
|
||||||
|
window.addEventListener("resize", this.resize);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Waiting for the css variables to become available, usually on page load before we can continue.
|
||||||
|
* Using default colors assigned below if no variables have been found after maxCssVarRetries
|
||||||
|
*/
|
||||||
|
waitForCssVars() {
|
||||||
|
if (
|
||||||
|
this.computedCanvasStyle &&
|
||||||
|
-1 !== this.computedCanvasStyle.getPropertyValue("--gradient-color-1").indexOf("#")
|
||||||
|
)
|
||||||
|
this.init(), this.addIsLoadedClass();
|
||||||
|
else {
|
||||||
|
if (((this.cssVarRetries += 1), this.cssVarRetries > this.maxCssVarRetries)) {
|
||||||
|
return (this.sectionColors = [16711680, 16711680, 16711935, 65280, 255]), void this.init();
|
||||||
|
}
|
||||||
|
requestAnimationFrame(() => this.waitForCssVars());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Initializes the four section colors by retrieving them from css variables.
|
||||||
|
*/
|
||||||
|
initGradientColors() {
|
||||||
|
this.sectionColors = ["--gradient-color-1", "--gradient-color-2", "--gradient-color-3", "--gradient-color-4"]
|
||||||
|
.map((cssPropertyName) => {
|
||||||
|
let hex = this.computedCanvasStyle.getPropertyValue(cssPropertyName).trim();
|
||||||
|
//Check if shorthand hex value was used and double the length so the conversion in normalizeColor will work.
|
||||||
|
if (4 === hex.length) {
|
||||||
|
const hexTemp = hex
|
||||||
|
.substr(1)
|
||||||
|
.split("")
|
||||||
|
.map((hexTemp) => hexTemp + hexTemp)
|
||||||
|
.join("");
|
||||||
|
hex = `#${hexTemp}`;
|
||||||
|
}
|
||||||
|
return hex && `0x${hex.substr(1)}`;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(normalizeColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*Finally initializing the Gradient class, assigning a canvas to it and calling Gradient.connect() which initializes everything,
|
||||||
|
* Use Gradient.pause() and Gradient.play() for controls.
|
||||||
|
*
|
||||||
|
* Here are some default property values you can change anytime:
|
||||||
|
* Amplitude: Gradient.amp = 0
|
||||||
|
* Colors: Gradient.sectionColors (if you change colors, use normalizeColor(#hexValue)) before you assign it.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Useful functions
|
||||||
|
* Gradient.toggleColor(index)
|
||||||
|
* Gradient.updateFrequency(freq)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { Gradient };
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#gradientCanvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
--gradient-color-1: #c3e4ff;
|
||||||
|
--gradient-color-2: #6ec3f4;
|
||||||
|
--gradient-color-3: #eae2ff;
|
||||||
|
--gradient-color-4: #b9beff;
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
import Logo from "../../components/logo";
|
||||||
|
import { Gradient } from "./loader-gradient.js";
|
||||||
|
|
||||||
|
import styles from "./loader.module.css";
|
||||||
|
|
||||||
|
export default function Loader() {
|
||||||
|
useEffect(() => {
|
||||||
|
const gradient = new Gradient();
|
||||||
|
// @ts-ignore
|
||||||
|
gradient.initGradient(`#${styles.gradientCanvas}`);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen min-w-screen relative">
|
||||||
|
<div className="relative z-10 min-h-screen flex flex-col justify-center py-12 px-4 sm:px-6 lg:px-8">
|
||||||
|
<div className="flex flex-col sm:mx-auto sm:w-full sm:max-w-sm">
|
||||||
|
<Logo className="mx-auto h-12 w-12" />
|
||||||
|
<span className="mt-2 text-center text-lg leading-9 text-gray-900">Loading up Shellphone...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<canvas id={styles.gradientCanvas} className="absolute top-0 z-0" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@ import type { BlitzPage } from "blitz";
|
|||||||
import { Routes, dynamic } from "blitz";
|
import { Routes, dynamic } from "blitz";
|
||||||
import { atom, useAtom } from "jotai";
|
import { atom, useAtom } from "jotai";
|
||||||
|
|
||||||
import Layout from "app/core/layouts/layout";
|
import AppLayout from "app/core/layouts/layout";
|
||||||
import ConversationsList from "../components/conversations-list";
|
import ConversationsList from "../components/conversations-list";
|
||||||
import NewMessageButton from "../components/new-message-button";
|
import NewMessageButton from "../components/new-message-button";
|
||||||
import MissingTwilioCredentials from "app/core/components/missing-twilio-credentials";
|
import MissingTwilioCredentials from "app/core/components/missing-twilio-credentials";
|
||||||
import useNotifications from "app/core/hooks/use-notifications";
|
import useNotifications from "app/core/hooks/use-notifications";
|
||||||
import useCurrentUser from "app/core/hooks/use-current-user";
|
import useCurrentUser from "app/core/hooks/use-current-user";
|
||||||
import PageTitle from "../../core/components/page-title";
|
import PageTitle from "../../core/components/page-title";
|
||||||
|
import Spinner from "../../core/components/spinner";
|
||||||
|
|
||||||
const Messages: BlitzPage = () => {
|
const Messages: BlitzPage = () => {
|
||||||
const { hasFilledTwilioCredentials, hasPhoneNumber } = useCurrentUser();
|
const { hasFilledTwilioCredentials, hasPhoneNumber } = useCurrentUser();
|
||||||
@@ -26,7 +27,7 @@ const Messages: BlitzPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MissingTwilioCredentials />
|
<MissingTwilioCredentials />
|
||||||
<PageTitle className="filter blur-sm absolute top-0" title="Messages" />
|
<PageTitle className="filter blur-sm select-none absolute top-0" title="Messages" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -35,7 +36,8 @@ const Messages: BlitzPage = () => {
|
|||||||
<>
|
<>
|
||||||
<PageTitle title="Messages" />
|
<PageTitle title="Messages" />
|
||||||
<section className="flex flex-grow flex-col">
|
<section className="flex flex-grow flex-col">
|
||||||
<Suspense fallback="Loading...">
|
<Suspense fallback={<Spinner />}>
|
||||||
|
{/* TODO: skeleton conversations list */}
|
||||||
<ConversationsList />
|
<ConversationsList />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</section>
|
</section>
|
||||||
@@ -52,7 +54,7 @@ const NewMessageBottomSheet = dynamic(() => import("../components/new-message-bo
|
|||||||
|
|
||||||
export const bottomSheetOpenAtom = atom(false);
|
export const bottomSheetOpenAtom = atom(false);
|
||||||
|
|
||||||
Messages.getLayout = (page) => <Layout title="Messages">{page}</Layout>;
|
Messages.getLayout = (page) => <AppLayout title="Messages">{page}</AppLayout>;
|
||||||
|
|
||||||
Messages.authenticate = { redirectTo: Routes.SignIn().pathname };
|
Messages.authenticate = { redirectTo: Routes.SignIn().pathname };
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { BlitzPage } from "blitz";
|
|||||||
import { Routes, useRouter } from "blitz";
|
import { Routes, useRouter } from "blitz";
|
||||||
import { IoChevronBack, IoInformationCircle, IoCall } from "react-icons/io5";
|
import { IoChevronBack, IoInformationCircle, IoCall } from "react-icons/io5";
|
||||||
|
|
||||||
import Layout from "../../../core/layouts/layout";
|
import AppLayout from "../../../core/layouts/layout";
|
||||||
import Conversation from "../../components/conversation";
|
import Conversation from "../../components/conversation";
|
||||||
import useConversation from "../../hooks/use-conversation";
|
import useConversation from "../../hooks/use-conversation";
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ const ConversationPage: BlitzPage = () => {
|
|||||||
const conversation = useConversation(recipient)[0];
|
const conversation = useConversation(recipient)[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout title={pageTitle} hideFooter>
|
<AppLayout title={pageTitle} hideFooter>
|
||||||
<header className="absolute top-0 w-screen h-12 backdrop-filter backdrop-blur-sm bg-white bg-opacity-75 border-b grid grid-cols-3 items-center">
|
<header className="absolute top-0 w-screen h-12 backdrop-filter backdrop-blur-sm bg-white bg-opacity-75 border-b grid grid-cols-3 items-center">
|
||||||
<span className="col-start-1 col-span-1 pl-2 cursor-pointer" onClick={router.back}>
|
<span className="col-start-1 col-span-1 pl-2 cursor-pointer" onClick={router.back}>
|
||||||
<IoChevronBack className="h-8 w-8" />
|
<IoChevronBack className="h-8 w-8" />
|
||||||
@@ -28,7 +28,7 @@ const ConversationPage: BlitzPage = () => {
|
|||||||
<Suspense fallback={<div className="pt-12">Loading messages with {recipient}</div>}>
|
<Suspense fallback={<div className="pt-12">Loading messages with {recipient}</div>}>
|
||||||
<Conversation />
|
<Conversation />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Layout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -21,7 +21,7 @@ import "app/core/styles/index.css";
|
|||||||
const { publicRuntimeConfig } = getConfig();
|
const { publicRuntimeConfig } = getConfig();
|
||||||
|
|
||||||
export default function App({ Component, pageProps }: AppProps) {
|
export default function App({ Component, pageProps }: AppProps) {
|
||||||
const session = useSession();
|
const session = useSession({ suspense: false });
|
||||||
usePanelbear(publicRuntimeConfig.panelBear.siteId);
|
usePanelbear(publicRuntimeConfig.panelBear.siteId);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session.userId) {
|
if (session.userId) {
|
||||||
@@ -42,7 +42,8 @@ export default function App({ Component, pageProps }: AppProps) {
|
|||||||
FallbackComponent={RootErrorFallback}
|
FallbackComponent={RootErrorFallback}
|
||||||
onReset={useQueryErrorResetBoundary().reset}
|
onReset={useQueryErrorResetBoundary().reset}
|
||||||
>
|
>
|
||||||
<Suspense fallback="Silence, ca pousse">{getLayout(<Component {...pageProps} />)}</Suspense>
|
{/* TODO: better default fallback */}
|
||||||
|
<Suspense fallback={null}>{getLayout(<Component {...pageProps} />)}</Suspense>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useRouter } from "blitz";
|
import { useRouter } from "blitz";
|
||||||
|
|
||||||
import Layout from "../core/layouts/layout";
|
import AppLayout from "../core/layouts/layout";
|
||||||
|
|
||||||
export default function Offline() {
|
export default function Offline() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
return (
|
return (
|
||||||
<Layout title="App went offline">
|
<AppLayout title="App went offline">
|
||||||
<h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
|
<h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
|
||||||
Oops, looks like you went offline.
|
Oops, looks like you went offline.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -17,6 +17,6 @@ export default function Offline() {
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</Layout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,12 @@ import { Suspense } from "react";
|
|||||||
import type { BlitzPage } from "blitz";
|
import type { BlitzPage } from "blitz";
|
||||||
import { Routes } from "blitz";
|
import { Routes } from "blitz";
|
||||||
|
|
||||||
import Layout from "app/core/layouts/layout";
|
import AppLayout from "app/core/layouts/layout";
|
||||||
import PhoneCallsList from "../components/phone-calls-list";
|
import PhoneCallsList from "../components/phone-calls-list";
|
||||||
import MissingTwilioCredentials from "app/core/components/missing-twilio-credentials";
|
import MissingTwilioCredentials from "app/core/components/missing-twilio-credentials";
|
||||||
import useCurrentUser from "app/core/hooks/use-current-user";
|
import useCurrentUser from "app/core/hooks/use-current-user";
|
||||||
import PageTitle from "../../core/components/page-title";
|
import PageTitle from "../../core/components/page-title";
|
||||||
|
import Spinner from "../../core/components/spinner";
|
||||||
|
|
||||||
const PhoneCalls: BlitzPage = () => {
|
const PhoneCalls: BlitzPage = () => {
|
||||||
const { hasFilledTwilioCredentials, hasPhoneNumber } = useCurrentUser();
|
const { hasFilledTwilioCredentials, hasPhoneNumber } = useCurrentUser();
|
||||||
@@ -15,7 +16,7 @@ const PhoneCalls: BlitzPage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MissingTwilioCredentials />
|
<MissingTwilioCredentials />
|
||||||
<PageTitle className="filter blur-sm absolute top-0" title="Calls" />
|
<PageTitle className="filter blur-sm select-none absolute top-0" title="Calls" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -24,7 +25,8 @@ const PhoneCalls: BlitzPage = () => {
|
|||||||
<>
|
<>
|
||||||
<PageTitle className="pl-12" title="Calls" />
|
<PageTitle className="pl-12" title="Calls" />
|
||||||
<section className="flex flex-grow flex-col">
|
<section className="flex flex-grow flex-col">
|
||||||
<Suspense fallback="Loading...">
|
<Suspense fallback={<Spinner />}>
|
||||||
|
{/* TODO: skeleton phone calls list */}
|
||||||
<PhoneCallsList />
|
<PhoneCallsList />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</section>
|
</section>
|
||||||
@@ -32,7 +34,7 @@ const PhoneCalls: BlitzPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
PhoneCalls.getLayout = (page) => <Layout title="Calls">{page}</Layout>;
|
PhoneCalls.getLayout = (page) => <AppLayout title="Calls">{page}</AppLayout>;
|
||||||
|
|
||||||
PhoneCalls.authenticate = { redirectTo: Routes.SignIn() };
|
PhoneCalls.authenticate = { redirectTo: Routes.SignIn() };
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Transition } from "@headlessui/react";
|
|||||||
import { IoBackspace, IoCall } from "react-icons/io5";
|
import { IoBackspace, IoCall } from "react-icons/io5";
|
||||||
|
|
||||||
import { Direction } from "db";
|
import { Direction } from "db";
|
||||||
import Layout from "app/core/layouts/layout";
|
import AppLayout from "app/core/layouts/layout";
|
||||||
import Keypad from "../components/keypad";
|
import Keypad from "../components/keypad";
|
||||||
import usePhoneCalls from "../hooks/use-phone-calls";
|
import usePhoneCalls from "../hooks/use-phone-calls";
|
||||||
import useKeyPress from "../hooks/use-key-press";
|
import useKeyPress from "../hooks/use-key-press";
|
||||||
@@ -161,7 +161,7 @@ const pressBackspaceAtom = atom(null, (get, set) => {
|
|||||||
set(phoneNumberAtom, (prevState) => prevState.slice(0, -1));
|
set(phoneNumberAtom, (prevState) => prevState.slice(0, -1));
|
||||||
});
|
});
|
||||||
|
|
||||||
KeypadPage.getLayout = (page) => <Layout title="Keypad">{page}</Layout>;
|
KeypadPage.getLayout = (page) => <AppLayout title="Keypad">{page}</AppLayout>;
|
||||||
|
|
||||||
KeypadPage.authenticate = { redirectTo: Routes.SignIn() };
|
KeypadPage.authenticate = { redirectTo: Routes.SignIn() };
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ export default function BillingHistory() {
|
|||||||
skip,
|
skip,
|
||||||
pagesNumber,
|
pagesNumber,
|
||||||
currentPage,
|
currentPage,
|
||||||
goToPreviousPage,
|
lastPage,
|
||||||
hasPreviousPage,
|
hasPreviousPage,
|
||||||
goToNextPage,
|
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
|
goToPreviousPage,
|
||||||
|
goToNextPage,
|
||||||
setPage,
|
setPage,
|
||||||
} = usePaymentsHistory();
|
} = usePaymentsHistory();
|
||||||
|
|
||||||
@@ -104,8 +105,8 @@ export default function BillingHistory() {
|
|||||||
Previous
|
Previous
|
||||||
</button>
|
</button>
|
||||||
<p className="text-sm text-gray-700 self-center">
|
<p className="text-sm text-gray-700 self-center">
|
||||||
Page <span className="font-medium">1</span> of{" "}
|
Page <span className="font-medium">{currentPage}</span> of{" "}
|
||||||
<span className="font-medium">4</span>
|
<span className="font-medium">{lastPage}</span>
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={goToNextPage}
|
onClick={goToNextPage}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ type Props = {
|
|||||||
const PaddleLink: FunctionComponent<Props> = ({ onClick, text }) => (
|
const PaddleLink: FunctionComponent<Props> = ({ onClick, text }) => (
|
||||||
<button className="flex space-x-2 items-center text-left" onClick={onClick}>
|
<button className="flex space-x-2 items-center text-left" onClick={onClick}>
|
||||||
<HiExternalLink className="w-6 h-6 flex-shrink-0" />
|
<HiExternalLink className="w-6 h-6 flex-shrink-0" />
|
||||||
<span className="transition-colors duration-150 border-b border-transparent hover:border-primary-500">
|
<span className="font-medium transition-colors duration-150 border-b border-transparent hover:border-primary-500">
|
||||||
{text}
|
{text}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { FunctionComponent } from "react";
|
import type { FunctionComponent } from "react";
|
||||||
|
import { Suspense } from "react";
|
||||||
import { Link, Routes, useMutation, useRouter } from "blitz";
|
import { Link, Routes, useMutation, useRouter } from "blitz";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import {
|
import {
|
||||||
@@ -10,9 +11,10 @@ import {
|
|||||||
IoPersonCircleOutline,
|
IoPersonCircleOutline,
|
||||||
} from "react-icons/io5";
|
} from "react-icons/io5";
|
||||||
|
|
||||||
import Layout from "app/core/layouts/layout";
|
import AppLayout from "app/core/layouts/layout";
|
||||||
import logout from "app/auth/mutations/logout";
|
|
||||||
import Divider from "./divider";
|
import Divider from "./divider";
|
||||||
|
import Spinner from "../../core/components/spinner";
|
||||||
|
import logout from "app/auth/mutations/logout";
|
||||||
|
|
||||||
const subNavigation = [
|
const subNavigation = [
|
||||||
{ name: "Account", href: Routes.Account(), icon: IoPersonCircleOutline },
|
{ name: "Account", href: Routes.Account(), icon: IoPersonCircleOutline },
|
||||||
@@ -26,7 +28,7 @@ const SettingsLayout: FunctionComponent = ({ children }) => {
|
|||||||
const [logoutMutation] = useMutation(logout);
|
const [logoutMutation] = useMutation(logout);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout title="Settings">
|
<AppLayout title="Settings">
|
||||||
<header className="bg-gray-100 px-2 sm:px-6 lg:px-8">
|
<header className="bg-gray-100 px-2 sm:px-6 lg:px-8">
|
||||||
<header className="flex">
|
<header className="flex">
|
||||||
<span className="flex items-center cursor-pointer" onClick={router.back}>
|
<span className="flex items-center cursor-pointer" onClick={router.back}>
|
||||||
@@ -43,7 +45,7 @@ const SettingsLayout: FunctionComponent = ({ children }) => {
|
|||||||
const isCurrentPage = item.href.pathname === router.pathname;
|
const isCurrentPage = item.href.pathname === router.pathname;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link key={item.name} href={item.href}>
|
<Link key={item.name} href={item.href} prefetch>
|
||||||
<a
|
<a
|
||||||
className={clsx(
|
className={clsx(
|
||||||
isCurrentPage
|
isCurrentPage
|
||||||
@@ -79,10 +81,12 @@ const SettingsLayout: FunctionComponent = ({ children }) => {
|
|||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div className="flex-grow overflow-y-auto space-y-6 px-2 sm:px-6 lg:col-span-9">{children}</div>
|
<div className="flex-grow overflow-y-auto space-y-6 px-2 sm:px-6 lg:col-span-9">
|
||||||
|
<Suspense fallback={<Spinner />}>{children}</Suspense>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</Layout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export default function usePaymentsHistory() {
|
|||||||
.fill(-1)
|
.fill(-1)
|
||||||
.map((_, i) => i + 1);
|
.map((_, i) => i + 1);
|
||||||
const currentPage = Math.floor((skip / count) * totalPages) + 1;
|
const currentPage = Math.floor((skip / count) * totalPages) + 1;
|
||||||
|
const lastPage = pagesNumber[pagesNumber.length - 1];
|
||||||
const hasPreviousPage = skip > 0;
|
const hasPreviousPage = skip > 0;
|
||||||
const hasNextPage = hasMore && !!nextPage;
|
const hasNextPage = hasMore && !!nextPage;
|
||||||
const goToPreviousPage = () => hasPreviousPage && setSkip(skip - itemsPerPage);
|
const goToPreviousPage = () => hasPreviousPage && setSkip(skip - itemsPerPage);
|
||||||
@@ -26,6 +27,7 @@ export default function usePaymentsHistory() {
|
|||||||
skip,
|
skip,
|
||||||
pagesNumber,
|
pagesNumber,
|
||||||
currentPage,
|
currentPage,
|
||||||
|
lastPage,
|
||||||
hasPreviousPage,
|
hasPreviousPage,
|
||||||
hasNextPage,
|
hasNextPage,
|
||||||
goToPreviousPage,
|
goToPreviousPage,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Suspense } from "react";
|
|
||||||
import type { BlitzPage } from "blitz";
|
import type { BlitzPage } from "blitz";
|
||||||
import { Routes, dynamic } from "blitz";
|
import { Routes, dynamic } from "blitz";
|
||||||
|
|
||||||
@@ -8,10 +7,8 @@ import PhoneNumberForm from "../../components/phone/phone-number-form";
|
|||||||
const PhoneSettings: BlitzPage = () => {
|
const PhoneSettings: BlitzPage = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-6">
|
<div className="flex flex-col space-y-6">
|
||||||
<Suspense fallback="Loading...">
|
<TwilioApiForm />
|
||||||
<TwilioApiForm />
|
<PhoneNumberForm />
|
||||||
<PhoneNumberForm />
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in new issue
Block a user