Setup apps/website (#383)
* scaffold website project with Next.js * set tamagui target * remove whitespace * update clean npm scripts * apply patches post-install * lock tamagui version * add patch-package to root * patch @tamagui/core-node * patch @tamagui/normalize-css-color * patch @achingbrain/ssdp * remove patches from website * update clean script in website * remove @tamagui/vite-plugin from dependencies * patch @tamagui/static * update node-version * define env vars
This commit is contained in:
parent
e98b4008cb
commit
593dccf33d
30
.eslintrc
30
.eslintrc
|
@ -42,6 +42,22 @@
|
|||
"react/jsx-uses-react": "off",
|
||||
"react/react-in-jsx-scope": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
// TODO: add https://github.com/francoismassart/eslint-plugin-tailwindcss
|
||||
"files": ["./apps/website/"],
|
||||
"extends": [
|
||||
// "next"
|
||||
"next/core-web-vitals"
|
||||
]
|
||||
// "settings": {
|
||||
// "import/resolver": {
|
||||
// "typescript": {
|
||||
// "extensions": [".js", ".jsx", ".ts", ".tsx"],
|
||||
// "project": ["tsconfig.json", "apps/website/tsconfig.json"]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
],
|
||||
"rules": {
|
||||
|
@ -84,13 +100,23 @@
|
|||
"import/resolver": {
|
||||
"node": {
|
||||
"extensions": [".js", ".jsx", ".ts", ".tsx"],
|
||||
"project": ["tsconfig.base.json", "packages/*/tsconfig.json"]
|
||||
"project": [
|
||||
"tsconfig.base.json",
|
||||
"packages/*/tsconfig.json",
|
||||
"apps/*/tsconfig.json"
|
||||
]
|
||||
},
|
||||
"typescript": {
|
||||
"alwaysTryTypes": true,
|
||||
"project": ["tsconfig.base.json", "packages/*/tsconfig.json"]
|
||||
"project": [
|
||||
"tsconfig.base.json",
|
||||
"packages/*/tsconfig.json",
|
||||
"apps/*/tsconfig.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"import/ignore": ["react-native"]
|
||||
}
|
||||
}
|
||||
|
||||
// appp/website; extend eslint-config-next
|
||||
|
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"zxh404.vscode-proto3",
|
||||
"ms-vscode.hexeditor",
|
||||
"zixuanchen.vitest-explorer",
|
||||
"mikestead.dotenv"
|
||||
"mikestead.dotenv",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -59,6 +59,44 @@
|
|||
"smartStep": true,
|
||||
"console": "integratedTerminal",
|
||||
"sourceMaps": true
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug server-side",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "yarn dev"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug client-side",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000",
|
||||
"skipFiles": [
|
||||
".next/**",
|
||||
"${workspaceFolder}/node_modules/**",
|
||||
"<node_internals>/**",
|
||||
"**/webpack-internal://**"
|
||||
],
|
||||
"runtimeArgs": ["--auto-open-devtools-for-tabs"]
|
||||
},
|
||||
// todo: consider https://code.visualstudio.com/docs/editor/debugging#_compound-launch-configurations instead
|
||||
// todo: consider client+prelaunch as full stack
|
||||
{
|
||||
"name": "Next.js: debug full stack",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "yarn dev -p 3000",
|
||||
"serverReadyAction": {
|
||||
"pattern": "started server on .+, url: (https?://.+)",
|
||||
"action": "startDebugging",
|
||||
"name": "Next.js: debug client-side",
|
||||
"killOnServerStop": false
|
||||
},
|
||||
"skipFiles": [
|
||||
".next/**",
|
||||
"${workspaceFolder}/node_modules/**",
|
||||
"<node_internals>/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint src",
|
||||
"typecheck": "tsc"
|
||||
"typecheck": "tsc",
|
||||
"clean": "rimraf node_modules .turbo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@status-im/components": "*",
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# Tamagui
|
||||
.tamagui
|
|
@ -0,0 +1,74 @@
|
|||
/* eslint-disable eslint-comments/disable-enable-pair */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const { withTamagui } = require('@tamagui/next-plugin')
|
||||
const { join } = require('path')
|
||||
|
||||
process.env.IGNORE_TS_CONFIG_PATHS = 'true'
|
||||
process.env.TAMAGUI_TARGET = 'web'
|
||||
process.env.TAMAGUI_DISABLE_WARN_DYNAMIC_LOAD = '1'
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
let config = {
|
||||
reactStrictMode: true,
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
images: {
|
||||
disableStaticImages: true,
|
||||
},
|
||||
transpilePackages: [
|
||||
// 'react-native',
|
||||
'react-native-web',
|
||||
// 'expo-modules-core',
|
||||
'expo-blur',
|
||||
// '@status-im/components',
|
||||
// '@status-im/js',
|
||||
],
|
||||
experimental: {
|
||||
legacyBrowsers: false,
|
||||
// esmExternals: 'loose',
|
||||
},
|
||||
}
|
||||
|
||||
const plugins = [
|
||||
withTamagui({
|
||||
config: './tamagui.config.ts',
|
||||
components: [
|
||||
// fixme?: works without it
|
||||
// '@status-im/icons',
|
||||
'@status-im/components',
|
||||
// './node_modules/@status-im/components/packages/components/dist',
|
||||
],
|
||||
importsWhitelist: ['constants.js', 'colors.js'],
|
||||
logTimings: true,
|
||||
disableExtraction: true,
|
||||
// experiment - reduced bundle size react-native-web
|
||||
useReactNativeWebLite: false,
|
||||
shouldExtract: path => {
|
||||
if (path.includes(join('packages', 'app'))) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
excludeReactNativeWebExports: [
|
||||
'Switch',
|
||||
'ProgressBar',
|
||||
'Picker',
|
||||
'CheckBox',
|
||||
'Touchable',
|
||||
'Modal',
|
||||
],
|
||||
}),
|
||||
]
|
||||
|
||||
module.exports = () => {
|
||||
for (const plugin of plugins) {
|
||||
config = {
|
||||
...config,
|
||||
...plugin(config),
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "website",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "TAMAGUI_TARGET='web' next dev",
|
||||
"build": "TAMAGUI_TARGET='web' next build",
|
||||
"start": "TAMAGUI_TARGET='web' next start",
|
||||
"lint": "next lint",
|
||||
"typecheck": "tsc",
|
||||
"clean": "rimraf .next .tamagui .vercel/output node_modules",
|
||||
"serve": "TAMAGUI_TARGET='web' NODE_ENV=production next start --port 8151"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.0.3",
|
||||
"@scure/base": "^1.1.1",
|
||||
"@status-im/components": "*",
|
||||
"@status-im/icons": "*",
|
||||
"@status-im/js": "*",
|
||||
"@tamagui/next-theme": "1.11.1",
|
||||
"next": "13.2.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-native-web": "^0.18.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@achingbrain/ssdp": "^4.0.1",
|
||||
"@tamagui/next-plugin": "1.11.1",
|
||||
"@types/node": "^18.11.11",
|
||||
"@types/react": "^18.0.33",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"postcss": "^8.4.21",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"typescript": "^5.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.x"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import '@/styles/app.css'
|
||||
import '@/styles/reset.css'
|
||||
|
||||
import { Provider } from '@status-im/components'
|
||||
import { Inter } from 'next/font/google'
|
||||
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
const inter = Inter({
|
||||
variable: '--font-sans',
|
||||
weight: ['400', '500', '600'],
|
||||
subsets: ['latin'],
|
||||
})
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<div id="app" className={inter.variable}>
|
||||
<Provider>
|
||||
<Component {...pageProps} />
|
||||
</Provider>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
import { Children } from 'react'
|
||||
|
||||
import NextDocument, { Head, Html, Main, NextScript } from 'next/document'
|
||||
import { AppRegistry } from 'react-native'
|
||||
|
||||
import Tamagui from '../tamagui.config'
|
||||
|
||||
import type { DocumentContext } from 'next/document'
|
||||
|
||||
export default class Document extends NextDocument {
|
||||
static async getInitialProps({ renderPage }: DocumentContext) {
|
||||
AppRegistry.registerComponent('app', () => Main)
|
||||
// @ts-expect-error todo
|
||||
const { getStyleElement } = AppRegistry.getApplication('app')
|
||||
|
||||
const page = await renderPage()
|
||||
|
||||
const styles = [getStyleElement()]
|
||||
|
||||
return { ...page, styles: Children.toArray(styles) }
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<style
|
||||
id="tamagui"
|
||||
dangerouslySetInnerHTML={{ __html: Tamagui.getCSS() }}
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
|
@ -0,0 +1,23 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body,
|
||||
#__next {
|
||||
height: 100%;
|
||||
overscroll-behavior: none;
|
||||
user-select: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
*::selection {
|
||||
color: #fff;
|
||||
background: hsla(229, 71%, 57%, 1);
|
||||
}
|
||||
|
||||
#app {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
height: 100%;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
1. Use a more-intuitive box-sizing model.
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
/* color: rgba(255, 255, 255, 0.87); */
|
||||
/* background-color: #fff; */
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
2. Remove default margin
|
||||
*/
|
||||
* {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
3. Allow percentage-based heights in the application
|
||||
*/
|
||||
html,
|
||||
body {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
overscroll-behavior-y: none; /* not working on Safari */
|
||||
}
|
||||
/*
|
||||
Typographic tweaks!
|
||||
4. Add accessible line-height
|
||||
5. Improve text rendering
|
||||
*/
|
||||
body {
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
padding: 0;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
/*
|
||||
6. Improve media defaults
|
||||
*/
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
/*
|
||||
7. Remove built-in form typography styles
|
||||
*/
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
all: unset;
|
||||
}
|
||||
/*
|
||||
8. Avoid text overflows
|
||||
*/
|
||||
p,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
/*
|
||||
9. Create a root stacking context
|
||||
*/
|
||||
#root,
|
||||
#__next {
|
||||
isolation: isolate;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
// './app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
|
||||
// todo: move all to `src` directory
|
||||
// // Or if using `src` directory:
|
||||
// './src/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import { config } from '@status-im/components'
|
||||
|
||||
export default config
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"react-native": ["react-native-web"],
|
||||
"@/*": ["./*"]
|
||||
// "@status-im/*": ["./node_modules/@status-im/*"]
|
||||
// "@status-im/js": ["./node_modules/@status-im/js/packages/status-js"],
|
||||
// "@status-im/components": [
|
||||
// "./node_modules/@status-im/components/packages/components"
|
||||
// ]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules", "next.config.js"]
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
},
|
||||
"keywords": [],
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
"prepare": "husky install",
|
||||
"test": "turbo run test --filter=@status-im/* -- --run",
|
||||
"dev": "turbo run dev --filter=@status-im/* --parallel",
|
||||
|
@ -23,6 +24,7 @@
|
|||
"web": "yarn workspace web dev",
|
||||
"mobile": "yarn workspace mobile dev",
|
||||
"desktop": "yarn workspace desktop dev",
|
||||
"website": "yarn workspace website dev",
|
||||
"storybook": "yarn workspace @status-im/components storybook"
|
||||
},
|
||||
"resolutions": {
|
||||
|
@ -36,6 +38,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
||||
"@typescript-eslint/parser": "^5.57.1",
|
||||
"eslint": "^8.37.0",
|
||||
"eslint-config-next": "^13.2.4",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-import-resolver-node": "^0.3.7",
|
||||
"eslint-import-resolver-typescript": "^3.5.5",
|
||||
|
@ -47,7 +50,9 @@
|
|||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.2.0",
|
||||
"patch-package": "^6.5.1",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier-plugin-tailwindcss": "^0.2.7",
|
||||
"rimraf": "^4.4.1",
|
||||
"turbo": "^1.8.8",
|
||||
"typescript": "^5.0.3",
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "TAMAGUI_TARGET='web' vite build --watch --mode development",
|
||||
"build": "TAMAGUI_TARGET='web' vite build",
|
||||
"dev": "vite build --watch --mode development",
|
||||
"build": "vite build",
|
||||
"postbuild": "yarn build:types",
|
||||
"build:types": "tsc --noEmit false --emitDeclarationOnly || true",
|
||||
"lint": "eslint src",
|
||||
|
@ -59,7 +59,7 @@
|
|||
"@storybook/react": "7.0.2",
|
||||
"@storybook/react-vite": "7.0.2",
|
||||
"@storybook/testing-library": "^0.1.0",
|
||||
"@tamagui/vite-plugin": "^1.11.1",
|
||||
"@tamagui/vite-plugin": "1.11.1",
|
||||
"@vitejs/plugin-react-swc": "^3.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
|
|
@ -18,7 +18,8 @@ const external = [
|
|||
export default defineConfig(({ mode }) => {
|
||||
return {
|
||||
define: {
|
||||
TAMAGUI_TARGET: JSON.stringify('web'),
|
||||
'process.env.TAMAGUI_TARGET': JSON.stringify('web'),
|
||||
'process.env.INCLUDE_CSS_COLOR_NAMES': JSON.stringify(false),
|
||||
},
|
||||
build: {
|
||||
target: 'es2020',
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
"protos:list": "buf ls-files src/protos",
|
||||
"protos:lint": "buf lint src/protos",
|
||||
"protos": "buf generate src/protos",
|
||||
"clean": "rm -rf dist node_modules .turbo"
|
||||
"clean": "rimraf dist node_modules .turbo"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^1.0.0",
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
diff --git a/node_modules/@achingbrain/ssdp/dist/src/default-ssdp-options.js b/node_modules/@achingbrain/ssdp/dist/src/default-ssdp-options.js
|
||||
index bcce6af..f687757 100644
|
||||
--- a/node_modules/@achingbrain/ssdp/dist/src/default-ssdp-options.js
|
||||
+++ b/node_modules/@achingbrain/ssdp/dist/src/default-ssdp-options.js
|
||||
@@ -1,11 +1,8 @@
|
||||
import { v4 } from 'uuid';
|
||||
import { defaultSocketOptions } from './default-socket-options.js';
|
||||
import util from 'util';
|
||||
-import { createRequire } from 'module';
|
||||
import mergeOptions from 'merge-options';
|
||||
-const req = createRequire(import.meta.url);
|
||||
-const pkg = req('../../package.json');
|
||||
-const DEFAULT_SSDP_SIGNATURE = util.format('node.js/%s UPnP/1.1 %s/%s', process.version.substring(1), pkg.name, pkg.version);
|
||||
+const DEFAULT_SSDP_SIGNATURE = util.format('node.js/%s UPnP/1.1 %s/%s', process.version.substring(1), '@achingbrain/ssdp', '4.0.1');
|
||||
export function defaultSsdpOptions(options) {
|
||||
return mergeOptions(options ?? {}, {
|
||||
usn: `uuid:${v4()}`,
|
|
@ -0,0 +1,24 @@
|
|||
diff --git a/node_modules/@tamagui/static/dist/cjs/extractor/bundle.js b/node_modules/@tamagui/static/dist/cjs/extractor/bundle.js
|
||||
index 0709a69..5138aa5 100644
|
||||
--- a/node_modules/@tamagui/static/dist/cjs/extractor/bundle.js
|
||||
+++ b/node_modules/@tamagui/static/dist/cjs/extractor/bundle.js
|
||||
@@ -87,7 +87,18 @@ function getESBuildConfig({ entryPoints, resolvePlatformSpecificEntries, ...opti
|
||||
...aliases
|
||||
})
|
||||
],
|
||||
- ...options
|
||||
+ ...options,
|
||||
+ resolveExtensions: [
|
||||
+ '.web.js',
|
||||
+ '.web.ts',
|
||||
+ '.web.tsx',
|
||||
+ '.js',
|
||||
+ '.jsx',
|
||||
+ '.json',
|
||||
+ '.ts',
|
||||
+ '.tsx',
|
||||
+ '.mjs',
|
||||
+ ],
|
||||
};
|
||||
return res;
|
||||
}
|
Loading…
Reference in New Issue