mirror of
https://github.com/logos-storage/ethcc-demo.git
synced 2026-01-05 06:33:12 +00:00
Initial commit
This commit is contained in:
commit
1cfc90a1bf
14
.eslintrc.cjs
Normal file
14
.eslintrc.cjs
Normal file
@ -0,0 +1,14 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-prettier/skip-formatting'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
||||
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*.tsbuildinfo
|
||||
*.key
|
||||
8
.prettierrc.json
Normal file
8
.prettierrc.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/prettierrc",
|
||||
"semi": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"Vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
194
README.md
Normal file
194
README.md
Normal file
@ -0,0 +1,194 @@
|
||||
# ethcc-demo
|
||||
|
||||
This demo app shows past and real-time Codex storage request events on the Codex
|
||||
testnet.
|
||||
|
||||
## Running the demo
|
||||
|
||||
### First, clone this repo
|
||||
|
||||
```shell
|
||||
git clone https://github.com/codex-storage/ethcc-demo && cd ethcc-demo
|
||||
```
|
||||
|
||||
### Then, create a private key to run codex
|
||||
|
||||
Create an Ethereum public/private key pair using a [web
|
||||
wallet](https://wallet.testnet.codex.storage) or consider other local methods:
|
||||
|
||||
<details>
|
||||
<summary>Use Docker</summary>
|
||||
|
||||
```shell
|
||||
# Generate keypair
|
||||
docker run --rm gochain/web3 account create
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Use MetaMask</summary>
|
||||
|
||||
1. [Accounts and Addresses](https://support.metamask.io/hc/en-us/sections/4471975962907-Accounts-and-Addresses)
|
||||
2. [How to export an account's private key](https://support.metamask.io/hc/en-us/articles/360015289632-How-to-export-an-account-s-private-key)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Use Python code</summary>
|
||||
|
||||
1. Create a venv
|
||||
|
||||
```shell
|
||||
pip3 install virtualenv
|
||||
|
||||
venv=codex-eth-key
|
||||
mkdir $venv && cd $venv
|
||||
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
```
|
||||
|
||||
2. Install required packages
|
||||
|
||||
```shell
|
||||
pip3 install web3
|
||||
```
|
||||
|
||||
3. Create a script
|
||||
|
||||
```shell
|
||||
vi eth-keys.py
|
||||
```
|
||||
|
||||
```python
|
||||
from eth_account import Account
|
||||
|
||||
def generate_ethereum_keypair():
|
||||
# Generate a new Ethereum account
|
||||
account = Account.create()
|
||||
|
||||
# Get the private key
|
||||
private_key = account._private_key.hex()
|
||||
|
||||
# Get the public key (Ethereum address)
|
||||
public_key = account.address
|
||||
|
||||
return private_key, public_key
|
||||
|
||||
# Generate the Ethereum key pair
|
||||
private_key, public_key = generate_ethereum_keypair()
|
||||
|
||||
# Print the keys
|
||||
print("Private Key:", private_key)
|
||||
print("Public Key (Ethereum Address):", public_key)
|
||||
```
|
||||
|
||||
4. Generate the keys
|
||||
|
||||
```shell
|
||||
python3 eth-keys.py
|
||||
```
|
||||
|
||||
5. Cleanup
|
||||
|
||||
```shell
|
||||
deactivate
|
||||
cd .. && rm -rf $venv
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
```shell
|
||||
# Example
|
||||
Private key: 0xacec4df7549199708a9f66b151aea7bf41b4d30bd325b96b26f017246226e1a3
|
||||
Public address: 0x1C408C8572ce7d5E79a3a6D353e5FC2E8E2c49ce
|
||||
```
|
||||
|
||||
### Mint testnet tokens
|
||||
|
||||
Before you can use the marketplace functionality of Codex, you will need to
|
||||
obtain some tokens in the testnet.
|
||||
|
||||
1. Join the [Codex Discord server](https://discord.gg/codex-storage)
|
||||
2. Go to the `#bot` channel.
|
||||
3. Give your public key to the bot using `set` command.
|
||||

|
||||
4. Ask it politely to mint some tokens for you using `mint` command.
|
||||

|
||||
|
||||
### Copy private key to the setup directory
|
||||
|
||||
Before we run a codex instance, we need to ensure our private key is saved on
|
||||
disk for the Codex instance to use. Copy your private key to
|
||||
`path/to/demo/codex-setup/eth.key`:
|
||||
|
||||
```shell
|
||||
# copy your private key to eth.key
|
||||
echo 0xacec4df7549199708a9f66b151aea7bf41b4d30bd325b96b26f017246226e1a3 >> ./codex-setup/eth.key
|
||||
chmod 0600 ./codex-setup/eth.key
|
||||
```
|
||||
|
||||
### Build codex and setup your environment
|
||||
|
||||
We need to build a Codex instance from source before running it. In a separate
|
||||
terminal, run:
|
||||
|
||||
```shell
|
||||
cd .. && git clone https://github.com/codex-storage/nim-codex && cd nim-codex
|
||||
export CODEX_PATH=$(pwd)
|
||||
make -j12 update && make -j12
|
||||
```
|
||||
|
||||
Note: the parameter `-j12` specifies the number of logical cores your CPU has,
|
||||
change to match your CPU. If unknown, use `-j2`.
|
||||
|
||||
### Run codex
|
||||
|
||||
In a new terminal, run codex:
|
||||
|
||||
```shell
|
||||
chmod +x codex.sh
|
||||
./codex.sh
|
||||
```
|
||||
|
||||
### Finally, run the demo app
|
||||
|
||||
Back in the original terminal, run the demo app:
|
||||
|
||||
```shell
|
||||
yarn
|
||||
yarn dev
|
||||
```
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
yarn dev
|
||||
```
|
||||
|
||||
### Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
yarn build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
yarn lint
|
||||
```
|
||||
26
codex.sh
Executable file
26
codex.sh
Executable file
@ -0,0 +1,26 @@
|
||||
# /bin/bash
|
||||
echo Running codex node at ${CODEX_PATH}/build/codex
|
||||
${CODEX_PATH}/build/codex \
|
||||
--bootstrap-node=spr:CiUIAhIhAiJvIcA_ZwPZ9ugVKDbmqwhJZaig5zKyLiuaicRcCGqLEgIDARo8CicAJQgCEiECIm8hwD9nA9n26BUoNuarCEllqKDnMrIuK5qJxFwIaosQ3d6esAYaCwoJBJ_f8zKRAnU6KkYwRAIgM0MvWNJL296kJ9gWvfatfmVvT-A7O2s8Mxp8l9c8EW0CIC-h-H-jBVSgFjg3Eny2u33qF7BDnWFzo7fGfZ7_qc9P \
|
||||
--bootstrap-node=spr:CiUIAhIhAyUvcPkKoGE7-gh84RmKIPHJPdsX5Ugm_IHVJgF-Mmu_EgIDARo8CicAJQgCEiEDJS9w-QqgYTv6CHzhGYog8ck92xflSCb8gdUmAX4ya78QoemesAYaCwoJBES39Q2RAnVOKkYwRAIgLi3rouyaZFS_Uilx8k99ySdQCP1tsmLR21tDb9p8LcgCIG30o5YnEooQ1n6tgm9fCT7s53k6XlxyeSkD_uIO9mb3 \
|
||||
--bootstrap-node=spr:CiUIAhIhA6_j28xa--PvvOUxH10wKEm9feXEKJIK3Z9JQ5xXgSD9EgIDARo8CicAJQgCEiEDr-PbzFr74--85TEfXTAoSb195cQokgrdn0lDnFeBIP0QzOGesAYaCwoJBK6Kf1-RAnVEKkcwRQIhAPUH5nQrqG4OW86JQWphdSdnPA98ErQ0hL9OZH9a4e5kAiBBZmUl9KnhSOiDgU3_hvjXrXZXoMxhGuZ92_rk30sNDA \
|
||||
--bootstrap-node=spr:CiUIAhIhA7E4DEMer8nUOIUSaNPA4z6x0n9Xaknd28Cfw9S2-cCeEgIDARo8CicAJQgCEiEDsTgMQx6vydQ4hRJo08DjPrHSf1dqSd3bwJ_D1Lb5wJ4Qt_CesAYaCwoJBEDhWZORAnVYKkYwRAIgFNzhnftocLlVHJl1onuhbSUM7MysXPV6dawHAA0DZNsCIDRVu9gnPTH5UkcRXLtt7MLHCo4-DL-RCMyTcMxYBXL0 \
|
||||
--bootstrap-node=spr:CiUIAhIhAzZn3JmJab46BNjadVnLNQKbhnN3eYxwqpteKYY32SbOEgIDARo8CicAJQgCEiEDNmfcmYlpvjoE2Np1Wcs1ApuGc3d5jHCqm14phjfZJs4QrvWesAYaCwoJBKpA-TaRAnViKkcwRQIhANuMmZDD2c25xzTbKSirEpkZYoxbq-FU_lpI0K0e4mIVAiBfQX4yR47h1LCnHznXgDs6xx5DLO5q3lUcicqUeaqGeg \
|
||||
--bootstrap-node=spr:CiUIAhIhAgybmRwboqDdUJjeZrzh43sn5mp8jt6ENIb08tLn4x01EgIDARo8CicAJQgCEiECDJuZHBuioN1QmN5mvOHjeyfmanyO3oQ0hvTy0ufjHTUQh4ifsAYaCwoJBI_0zSiRAnVsKkcwRQIhAJCb_z0E3RsnQrEePdJzMSQrmn_ooHv6mbw1DOh5IbVNAiBbBJrWR8eBV6ftzMd6ofa5khNA2h88OBhMqHCIzSjCeA \
|
||||
--bootstrap-node=spr:CiUIAhIhAntGLadpfuBCD9XXfiN_43-V3L5VWgFCXxg4a8uhDdnYEgIDARo8CicAJQgCEiECe0Ytp2l-4EIP1dd-I3_jf5XcvlVaAUJfGDhry6EN2dgQsIufsAYaCwoJBNEmoCiRAnV2KkYwRAIgXO3bzd5VF8jLZG8r7dcLJ_FnQBYp1BcxrOvovEa40acCIDhQ14eJRoPwJ6GKgqOkXdaFAsoszl-HIRzYcXKeb7D9 \
|
||||
--data-dir=./codex-data \
|
||||
--log-level='INFO;TRACE:marketplace,node,statemachine,erasure' \
|
||||
--api-port=8080 \
|
||||
--api-bindaddr=0.0.0.0 \
|
||||
--metrics=true \
|
||||
--listen-addrs=/ip4/0.0.0.0/tcp/8070 \
|
||||
--disc-port=8090 \
|
||||
--nat=$(curl https://ip.codex.storage) \
|
||||
persistence \
|
||||
--eth-private-key=./codex-setup/eth.key \
|
||||
--eth-provider=https://rpc.testnet.codex.storage \
|
||||
--marketplace-address=0x4cBDfab37baB0AA3AC69A7b12C4396907dfF5227 \
|
||||
prover \
|
||||
--circom-r1cs=./codex-setup/circuits/proof_main.r1cs \
|
||||
--circom-wasm=./codex-setup/circuits/proof_main.wasm \
|
||||
--circom-zkey=./codex-setup/circuits/proof_main.zkey
|
||||
BIN
docs/bot-mint.png
Normal file
BIN
docs/bot-mint.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
BIN
docs/bot-set.png
Normal file
BIN
docs/bot-set.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
jsconfig.json
Normal file
8
jsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "ethcc-demo",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"ethers": "^6.12.1",
|
||||
"flowbite": "^2.3.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.8.0",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-prettier": "^9.0.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"vite": "^5.2.8",
|
||||
"vite-plugin-vue-devtools": "^7.0.25"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
96
src/App.vue
Normal file
96
src/App.vue
Normal file
@ -0,0 +1,96 @@
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useEventsStore } from './stores/events'
|
||||
import { RouterView } from 'vue-router'
|
||||
import Balance from './components/Balance.vue'
|
||||
import BlockNumber from './components/BlockNumber.vue'
|
||||
import AppNav from './components/AppNav.vue'
|
||||
import { initDrawers } from 'flowbite'
|
||||
|
||||
const eventsStore = useEventsStore()
|
||||
|
||||
onMounted(async () => {
|
||||
initDrawers()
|
||||
await eventsStore.fetchPastEvents()
|
||||
|
||||
function onStorageRequested(blockNumber, requestId, request, state) {
|
||||
alert('storage requested')
|
||||
}
|
||||
await eventsStore.listenForNewEvents(onStorageRequested)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<AppNav />
|
||||
</header>
|
||||
<RouterView class="max-w-screen-xl mx-auto" />
|
||||
<footer>
|
||||
<Balance />
|
||||
<BlockNumber />
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<!-- <style scoped>
|
||||
header {
|
||||
line-height: 1.5;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: inline-block;
|
||||
padding: 0 1rem;
|
||||
border-left: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
nav a:first-of-type {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
nav {
|
||||
text-align: left;
|
||||
margin-left: -1rem;
|
||||
font-size: 1rem;
|
||||
|
||||
padding: 1rem 0;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
</style> -->
|
||||
86
src/assets/base.css
Normal file
86
src/assets/base.css
Normal file
@ -0,0 +1,86 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition:
|
||||
color 0.5s,
|
||||
background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
Roboto,
|
||||
Oxygen,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
'Fira Sans',
|
||||
'Droid Sans',
|
||||
'Helvetica Neue',
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
BIN
src/assets/image-1@2x.jpg
Normal file
BIN
src/assets/image-1@2x.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
10
src/assets/logo-black.svg
Normal file
10
src/assets/logo-black.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_274_4287)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7001 32.7386C19.7705 32.7792 19.8502 32.8001 19.9306 32.8001C20.0111 32.8001 20.188 32.7225 20.188 32.7225L30.85 26.581C30.8555 26.5783 30.8632 26.5744 30.8706 26.5701C30.942 26.5295 31.0002 26.4707 31.0406 26.4008C31.0819 26.3302 31.1036 26.25 31.1036 26.1685C31.1036 26.1588 31.1032 26.1499 31.1028 26.1432V13.8597C31.1036 13.8486 31.1036 13.8387 31.1036 13.8337L31.1036 13.8324C31.1036 13.7507 31.0818 13.671 31.0413 13.6009C31.0008 13.5302 30.9421 13.4718 30.872 13.4313C30.8644 13.4268 30.8564 13.4225 30.8484 13.4186L20.1868 7.27756C20.179 7.27228 20.1714 7.26767 20.165 7.26389L20.1644 7.26354C20.0937 7.22255 20.0136 7.20151 19.9325 7.20151H19.9306C19.8495 7.20151 19.77 7.22295 19.701 7.26233C19.692 7.26733 19.6837 7.27257 19.6762 7.27763L9.01204 13.4202C9.00655 13.4229 8.99886 13.4269 8.99144 13.4311C8.92049 13.4717 8.86173 13.531 8.82123 13.6011C8.78067 13.6712 8.75879 13.7509 8.75879 13.8327C8.75879 13.8425 8.75919 13.8513 8.75956 13.858V26.1419C8.75876 26.153 8.75878 26.1629 8.75879 26.1679L8.75879 26.1692C8.75879 26.2508 8.78058 26.3312 8.82193 26.4018C8.86238 26.4712 8.92065 26.5296 8.98909 26.5693L8.99047 26.5701L8.99187 26.5708C9.00023 26.5754 9.00781 26.5793 9.01357 26.5822L19.6768 32.7241C19.6818 32.7275 19.6906 32.7332 19.7001 32.7386ZM30.8023 26.4503C30.7969 26.4533 30.7908 26.4564 30.7847 26.4594C30.788 26.4578 30.7915 26.4561 30.7946 26.4544C30.7973 26.4531 30.7999 26.4517 30.8023 26.4503ZM30.9649 26.1472C30.9651 26.1495 30.9652 26.152 30.9653 26.1545C30.9655 26.1589 30.9657 26.1636 30.9657 26.1685C30.9657 26.167 30.9657 26.1655 30.9656 26.164C30.9655 26.158 30.9652 26.1523 30.9649 26.1472ZM20.3983 26.9771L24.3592 29.2549L20.3951 31.538L20.3983 26.9771ZM15.5034 29.2548L19.4645 26.9769L19.4676 31.538L15.5034 29.2548ZM25.7528 23.8924L29.7137 26.1701L25.7497 28.4532L25.7528 23.8924ZM20.3984 25.3628V20.8084L24.3514 23.0856L20.3984 25.3628ZM15.0439 22.2784V17.724L18.9969 20.0012L15.0439 22.2784ZM20.3984 19.1932V14.6388L24.3514 16.916L20.3984 19.1932ZM19.4649 13.0247L15.5038 10.7468L19.468 8.4636L19.4649 13.0247ZM10.1491 26.1701L14.1131 28.4532L14.11 23.8921L10.1491 26.1701ZM24.8194 23.8927L24.8225 28.4527L20.8658 26.1705L24.8194 23.8927ZM15.0438 23.8927L15.0406 28.4527L18.9974 26.1705L15.0438 23.8927ZM26.2198 23.0856L30.178 20.8025V25.3687L26.2198 23.0856ZM9.68483 20.8025V25.3687L13.643 23.0856L9.68483 20.8025ZM15.5111 23.085L19.4644 25.3624V20.8076L15.5111 23.085ZM29.7125 20.0004L25.7529 17.7227V22.2781L29.7125 20.0004ZM10.1503 20.0008L14.1099 22.2785V17.7231L10.1503 20.0008ZM24.8189 17.7232V22.278L20.8656 20.0006L24.8189 17.7232ZM30.178 14.6329V19.1991L26.2198 16.916L30.178 14.6329ZM13.643 16.916L9.68483 19.1991V14.6329L13.643 16.916ZM19.4644 14.6384V19.1928L15.5114 16.9156L19.4644 14.6384ZM29.7141 13.8315L25.7497 11.5482L25.7528 16.1095L29.7141 13.8315ZM14.1135 11.5484L14.1104 16.1095L10.1491 13.8315L14.1135 11.5484ZM24.8225 11.5489L24.8194 16.1089L20.8658 13.8311L24.8225 11.5489ZM18.9974 13.8311L15.0438 16.1089L15.0406 11.5489L18.9974 13.8311ZM24.3596 10.7467L20.3951 8.46359L20.3983 13.0247L24.3596 10.7467Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_274_4287">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
10
src/assets/logo.svg
Normal file
10
src/assets/logo.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_291_2793)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.7001 32.7386C19.7705 32.7792 19.8502 32.8001 19.9306 32.8001C20.0111 32.8001 20.188 32.7226 20.188 32.7226L30.85 26.581C30.8555 26.5783 30.8632 26.5744 30.8706 26.5702C30.942 26.5296 31.0002 26.4707 31.0406 26.4008C31.0819 26.3303 31.1036 26.25 31.1036 26.1685C31.1036 26.1588 31.1032 26.1499 31.1028 26.1432V13.8597C31.1036 13.8486 31.1036 13.8387 31.1036 13.8337L31.1036 13.8324C31.1036 13.7507 31.0818 13.671 31.0413 13.6009C31.0008 13.5302 30.9421 13.4718 30.872 13.4313C30.8644 13.4269 30.8564 13.4225 30.8484 13.4186L20.1868 7.27759C20.179 7.27231 20.1714 7.2677 20.165 7.26392L20.1644 7.26357C20.0937 7.22258 20.0136 7.20154 19.9325 7.20154H19.9306C19.8495 7.20154 19.77 7.22298 19.701 7.26236C19.692 7.26736 19.6837 7.2726 19.6762 7.27766L9.01204 13.4202C9.00655 13.423 8.99886 13.4269 8.99144 13.4311C8.92049 13.4717 8.86173 13.5311 8.82123 13.6011C8.78067 13.6712 8.75879 13.751 8.75879 13.8328C8.75879 13.8425 8.75919 13.8514 8.75956 13.858V26.1419C8.75876 26.153 8.75878 26.1629 8.75879 26.1679L8.75879 26.1693C8.75879 26.2509 8.78058 26.3312 8.82193 26.4019C8.86238 26.4712 8.92065 26.5297 8.98909 26.5693L8.99047 26.5701L8.99187 26.5709C9.00023 26.5755 9.00781 26.5793 9.01357 26.5822L19.6768 32.7242C19.6818 32.7275 19.6906 32.7333 19.7001 32.7386ZM30.8023 26.4503C30.7969 26.4534 30.7908 26.4564 30.7847 26.4595C30.788 26.4578 30.7915 26.4561 30.7946 26.4545C30.7973 26.4531 30.7999 26.4517 30.8023 26.4503ZM30.9649 26.1472C30.9651 26.1495 30.9652 26.152 30.9653 26.1545C30.9655 26.159 30.9657 26.1637 30.9657 26.1685C30.9657 26.167 30.9657 26.1655 30.9656 26.164C30.9655 26.1581 30.9652 26.1524 30.9649 26.1472ZM20.3983 26.9772L24.3592 29.2549L20.3951 31.538L20.3983 26.9772ZM15.5034 29.2548L19.4645 26.977L19.4676 31.538L15.5034 29.2548ZM25.7528 23.8924L29.7137 26.1701L25.7497 28.4532L25.7528 23.8924ZM20.3984 25.3628V20.8084L24.3514 23.0856L20.3984 25.3628ZM15.0439 22.2784V17.724L18.9969 20.0012L15.0439 22.2784ZM20.3984 19.1932V14.6389L24.3514 16.916L20.3984 19.1932ZM19.4649 13.0247L15.5038 10.7468L19.468 8.46363L19.4649 13.0247ZM10.1491 26.1701L14.1131 28.4532L14.11 23.8922L10.1491 26.1701ZM24.8194 23.8928L24.8225 28.4527L20.8658 26.1705L24.8194 23.8928ZM15.0438 23.8928L15.0406 28.4527L18.9974 26.1705L15.0438 23.8928ZM26.2198 23.0856L30.178 20.8025V25.3687L26.2198 23.0856ZM9.68483 20.8025V25.3687L13.643 23.0856L9.68483 20.8025ZM15.5111 23.085L19.4644 25.3624V20.8077L15.5111 23.085ZM29.7125 20.0004L25.7529 17.7227V22.2782L29.7125 20.0004ZM10.1503 20.0008L14.1099 22.2786V17.7231L10.1503 20.0008ZM24.8189 17.7233V22.278L20.8656 20.0006L24.8189 17.7233ZM30.178 14.6329V19.1992L26.2198 16.916L30.178 14.6329ZM13.643 16.916L9.68483 19.1992V14.6329L13.643 16.916ZM19.4644 14.6385V19.1928L15.5114 16.9157L19.4644 14.6385ZM29.7141 13.8315L25.7497 11.5482L25.7528 16.1095L29.7141 13.8315ZM14.1135 11.5484L14.1104 16.1095L10.1491 13.8315L14.1135 11.5484ZM24.8225 11.549L24.8194 16.1089L20.8658 13.8312L24.8225 11.549ZM18.9974 13.8312L15.0438 16.1089L15.0406 11.549L18.9974 13.8312ZM24.3596 10.7467L20.3951 8.46362L20.3983 13.0247L24.3596 10.7467Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_291_2793">
|
||||
<rect width="40" height="40" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
35
src/assets/main.css
Normal file
35
src/assets/main.css
Normal file
@ -0,0 +1,35 @@
|
||||
@import './base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
90
src/components/AppNav.vue
Normal file
90
src/components/AppNav.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { initDrawers } from 'flowbite'
|
||||
|
||||
onMounted(() => {
|
||||
initDrawers()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav
|
||||
class="bg-white dark:bg-gray-900 fixed w-full z-20 top-0 start-0 border-b border-gray-200 dark:border-gray-600"
|
||||
>
|
||||
<div class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
|
||||
<a href="https://codex.storage/" class="flex items-center space-x-3 rtl:space-x-reverse">
|
||||
<img src="../assets/logo.svg" class="h-8 hidden dark:inline" alt="Codex Logo" />
|
||||
<img src="../assets/logo-black.svg" class="h-8 inline dark:hidden" alt="Codex Logo" />
|
||||
<span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white"
|
||||
>Codex</span
|
||||
>
|
||||
</a>
|
||||
<div class="flex md:order-2 space-x-3 md:space-x-0 rtl:space-x-reverse">
|
||||
<button
|
||||
type="button"
|
||||
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
>
|
||||
Instructions
|
||||
</button>
|
||||
<button
|
||||
data-collapse-toggle="navbar-sticky"
|
||||
type="button"
|
||||
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
||||
aria-controls="navbar-sticky"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 17 14"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M1 1h15M1 7h15M1 13h15"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="items-center justify-between hidden w-full md:flex md:w-auto md:order-1"
|
||||
id="navbar-sticky"
|
||||
>
|
||||
<ul
|
||||
class="flex flex-col p-4 md:p-0 mt-4 font-medium border border-gray-100 rounded-lg bg-gray-50 md:space-x-8 rtl:space-x-reverse md:flex-row md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700"
|
||||
>
|
||||
<li>
|
||||
<RouterLink
|
||||
class="router-link block py-2 px-3 rounded md:bg-transparent md:text-blue-700 md:p-0 md:dark:text-blue-500"
|
||||
to="/"
|
||||
>Requests</RouterLink
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<RouterLink
|
||||
class="router-link block py-2 px-3 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 md:dark:hover:text-blue-500 dark:text-white dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700"
|
||||
to="/slots"
|
||||
>Slots</RouterLink
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
nav a.router-link {
|
||||
@apply text-gray-900;
|
||||
}
|
||||
nav a.router-link-exact-active {
|
||||
/* color: var(--color-text); */
|
||||
@apply text-white bg-blue-700 md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500;
|
||||
}
|
||||
</style>
|
||||
20
src/components/Balance.vue
Normal file
20
src/components/Balance.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<script setup>
|
||||
import { inject, ref, onMounted } from 'vue'
|
||||
import {formatEther} from 'ethers'
|
||||
|
||||
const token = inject('token')
|
||||
const myAddress = inject('myAddress')
|
||||
const balance = ref(1)
|
||||
|
||||
onMounted(async () => {
|
||||
balance.value = await token.balanceOf(myAddress)
|
||||
|
||||
token.on(token.filters.Transfer, async (from, to, amount, event) => {
|
||||
balance.value = await token.balanceOf(myAddress)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>My balance: {{ formatEther(balance) }} TST</div>
|
||||
</template>
|
||||
14
src/components/BlockNumber.vue
Normal file
14
src/components/BlockNumber.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup>
|
||||
import { inject, ref, onMounted } from 'vue'
|
||||
|
||||
const ethProvider = inject('ethProvider')
|
||||
const blockNumber = ref(0)
|
||||
|
||||
onMounted(async () => {
|
||||
blockNumber.value = await ethProvider.getBlockNumber()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span>Current block: {{ blockNumber }}</span>
|
||||
</template>
|
||||
93
src/components/CodexImage.vue
Normal file
93
src/components/CodexImage.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<script setup>
|
||||
import { inject, ref, onMounted } from 'vue'
|
||||
import { initModals } from 'flowbite'
|
||||
import SpinnerLoading from './SpinnerLoading.vue'
|
||||
|
||||
const codexApi = inject('codexApi')
|
||||
const loading = ref(false)
|
||||
const imgSrc = ref('')
|
||||
const error = ref('')
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
// Required string
|
||||
cid: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
localOnly: {
|
||||
// only try downloading from the local node
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
alt: String
|
||||
})
|
||||
// var imgSrc = ""
|
||||
// var error = ""
|
||||
// if (state == "New" || state == "Started"){
|
||||
// try {
|
||||
// let res = await codexApi.download(cid)
|
||||
// try{
|
||||
// const blob = await res.blob()
|
||||
// imgSrc = URL.createObjectURL(blob)
|
||||
// } catch (e) {
|
||||
// error = `not an image (error: ${error.message})`
|
||||
// }
|
||||
// } catch(error) {
|
||||
// error = `failed to download cid data: ${error}`
|
||||
// }
|
||||
// }
|
||||
|
||||
onMounted(async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
let res = await codexApi.downloadLocal(props.cid)
|
||||
if (res.status === 404 && !props.localOnly) {
|
||||
res = await codexApi.download(props.cid)
|
||||
}
|
||||
if (!res.ok) {
|
||||
throw new Error(`${res.status} ${res.statusText}`)
|
||||
}
|
||||
try {
|
||||
const blob = await res.blob()
|
||||
imgSrc.value = URL.createObjectURL(blob)
|
||||
} catch (e) {
|
||||
error.value = `not an image (error: ${e.message})`
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = `failed to download cid data: ${e.message}`
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-center">
|
||||
<SpinnerLoading v-if="loading" />
|
||||
<div
|
||||
v-else-if="error"
|
||||
v-bind="$attrs"
|
||||
class="dark:bg-orange-700 dark:text-orange-200"
|
||||
>
|
||||
<svg
|
||||
class="text-red-500 fill-red-100 dark:text-white"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">{{ error }}</span>
|
||||
</div>
|
||||
|
||||
<img v-bind="$attrs" v-else-if="imgSrc" :src="imgSrc" class="rounded-full" :alt="props.alt" />
|
||||
</div>
|
||||
</template>
|
||||
16
src/components/CounterButton.vue
Normal file
16
src/components/CounterButton.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script setup>
|
||||
import {useCounterStore} from '../stores/counter'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
useCounterStore()
|
||||
|
||||
const pElementRef = ref(null)
|
||||
|
||||
// var {count, doubleCount, increment} = useCounterStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button @click="_pStores.counter.increment">
|
||||
{{ _pStores.counter.count }}
|
||||
</button>
|
||||
</template>
|
||||
44
src/components/HelloWorld.vue
Normal file
44
src/components/HelloWorld.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
msg: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="greetings">
|
||||
<h1 class="green">{{ msg }}</h1>
|
||||
<h3>
|
||||
You’ve successfully created a project with
|
||||
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2.6rem;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
90
src/components/SkeletonLoading.vue
Normal file
90
src/components/SkeletonLoading.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
type: {
|
||||
validator(value) {
|
||||
// The value must match one of these strings
|
||||
return ['default', 'image', 'text'].includes(value)
|
||||
},
|
||||
default: 'default'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<!-- DEFAULT -->
|
||||
<div v-if="props.type === 'default'" role="status" class="max-w-sm animate-pulse">
|
||||
<div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px] mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[300px] mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px]"></div>
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
|
||||
<!-- IMAGE -->
|
||||
<div
|
||||
v-else-if="props.type === 'image'"
|
||||
role="status"
|
||||
class="space-y-8 animate-pulse md:space-y-0 md:space-x-8 rtl:space-x-reverse md:flex md:items-center"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-center w-full h-48 bg-gray-300 rounded sm:w-96 dark:bg-gray-700"
|
||||
>
|
||||
<svg
|
||||
class="w-10 h-10 text-gray-200 dark:text-gray-600"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 18"
|
||||
>
|
||||
<path
|
||||
d="M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[480px] mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[440px] mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[460px] mb-2.5"></div>
|
||||
<div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px]"></div>
|
||||
</div>
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
|
||||
<!-- TEXT -->
|
||||
<div v-else-if="props.type === 'text'" role="status" class="space-y-2.5 animate-pulse max-w-lg">
|
||||
<div class="flex items-center w-full">
|
||||
<div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-32"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-24"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-full"></div>
|
||||
</div>
|
||||
<div class="flex items-center w-full max-w-[480px]">
|
||||
<div class="h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 w-full"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-full"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-24"></div>
|
||||
</div>
|
||||
<div class="flex items-center w-full max-w-[400px]">
|
||||
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-full"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-200 rounded-full dark:bg-gray-700 w-80"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-full"></div>
|
||||
</div>
|
||||
<div class="flex items-center w-full max-w-[480px]">
|
||||
<div class="h-2.5 ms-2 bg-gray-200 rounded-full dark:bg-gray-700 w-full"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-full"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-24"></div>
|
||||
</div>
|
||||
<div class="flex items-center w-full max-w-[440px]">
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-32"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-24"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-200 rounded-full dark:bg-gray-700 w-full"></div>
|
||||
</div>
|
||||
<div class="flex items-center w-full max-w-[360px]">
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-full"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-200 rounded-full dark:bg-gray-700 w-80"></div>
|
||||
<div class="h-2.5 ms-2 bg-gray-300 rounded-full dark:bg-gray-600 w-full"></div>
|
||||
</div>
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</template>
|
||||
124
src/components/Slots.vue
Normal file
124
src/components/Slots.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { initModals } from 'flowbite'
|
||||
import { useEventsStore } from '../stores/events'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const eventsStore = useEventsStore()
|
||||
|
||||
// const ethProvider = inject('ethProvider')
|
||||
// const marketplace = inject('marketplace')
|
||||
// const codexApi = inject('codexApi')
|
||||
// const token = inject('token')
|
||||
// const blockNumber = ref(0)
|
||||
// const requests = ref([])
|
||||
|
||||
// const onStorageRequested = async ({requestId, ask, expiry, blockNumber}) => {
|
||||
// let request = await marketplace.getRequest(requestId)
|
||||
// let stateIdx = await marketplace.requestState(requestId)
|
||||
// let cid = request[2][0]
|
||||
// let state = requestState[stateIdx]
|
||||
// var imgSrc = ""
|
||||
// var error = ""
|
||||
// if (state == "New" || state == "Started"){
|
||||
// try {
|
||||
// let res = await codexApi.download(cid)
|
||||
// try{
|
||||
// const blob = await res.blob()
|
||||
// imgSrc = URL.createObjectURL(blob)
|
||||
// } catch (e) {
|
||||
// error = `not an image (error: ${error.message})`
|
||||
// }
|
||||
// } catch(error) {
|
||||
// error = `failed to download cid data: ${error}`
|
||||
// }
|
||||
// }
|
||||
// requests.value.push({
|
||||
// blockNumber,
|
||||
// cid,
|
||||
// requestId,
|
||||
// ask,
|
||||
// expiry,
|
||||
// state,
|
||||
// imgSrc,
|
||||
// error
|
||||
// })
|
||||
// }
|
||||
// onMounted(async () => {
|
||||
// await eventsStore.fetchPastEvents()
|
||||
// await eventsStore.listenForNewEvents()
|
||||
// })
|
||||
// let storageRequestedFilter = marketplace.filters.StorageRequested
|
||||
// marketplace.on(storageRequestedFilter, async (requestId, ask, expiry, event) => {
|
||||
// let {blockNumber} = event
|
||||
// // onStorageRequested({
|
||||
// // blockNumber,
|
||||
// // requestId,
|
||||
// // ask,
|
||||
// // expiry,
|
||||
// // })
|
||||
// let request = await marketplace.getRequest(requestId)
|
||||
// let state = await getRequestState(requestId)
|
||||
// onStorageRequested(blockNumber, request, state)
|
||||
// })
|
||||
|
||||
// // query past events
|
||||
// let requests = (await marketplace.queryFilter(storageRequestedFilter))
|
||||
// requests.forEach(async (event) => {
|
||||
// let {requestId, ask, expiry} = event.args
|
||||
// let {blockNumber} = event
|
||||
// // onStorageRequested({
|
||||
// // blockNumber,
|
||||
// // requestId,
|
||||
// // ask,
|
||||
// // expiry
|
||||
// // })
|
||||
// let request = await marketplace.getRequest(requestId)
|
||||
// let state = await getRequestState(requestId)
|
||||
// onStorageRequested(blockNumber, request, state)
|
||||
// })
|
||||
|
||||
// let slotFreedFilter = marketplace.filters.SlotFreed
|
||||
// marketplace.on(slotFreedFilter, (requestId, ask, expiry, event) => {
|
||||
// let {blockNumber} = event
|
||||
// onSlotFreed({
|
||||
// blockNumber,
|
||||
// requestId,
|
||||
// ask,
|
||||
// expiry,
|
||||
// })
|
||||
// })
|
||||
|
||||
// let slotsFreed = (await marketplace.queryFilter(slotFreedFilter))
|
||||
// slotsFreed.forEach(event => {
|
||||
// let {requestId, ask, expiry} = event.args
|
||||
// let {blockNumber} = event
|
||||
// onSlotFreed({
|
||||
// blockNumber,
|
||||
// requestId,
|
||||
// ask,
|
||||
// expiry
|
||||
// })
|
||||
// })
|
||||
|
||||
// })
|
||||
// console.log(await ethProvider.getBlockNumber())
|
||||
const { requests } = storeToRefs(eventsStore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <span>asdf{{ blockNumber.value }}</span> -->
|
||||
<!-- <span>Yo yo yo yo yo {{ blockNumber }}</span> -->
|
||||
<ul class="requests">
|
||||
<!-- <li v-for="(request, idx) in eventsStore.requests" :key="{requestId}"> WORKS! -->
|
||||
<li v-for="([requestId, { request, state }], idx) in requests" :key="{ requestId }">
|
||||
{{ idx }}.
|
||||
<div>CID: {{ request[2][0] }}</div>
|
||||
<div>RequestID: {{ requestId }}</div>
|
||||
<div>State: {{ state }}</div>
|
||||
<CodexImage v-if="state == 'New' || state == 'Started'" cid="cid" />
|
||||
<!-- <div v-if="error">{{error}}</div>
|
||||
<img v-else-if="imgSrc" :src="imgSrc" width="100%"/> -->
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
21
src/components/SpinnerLoading.vue
Normal file
21
src/components/SpinnerLoading.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div role="status">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600"
|
||||
viewBox="0 0 100 101"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
|
||||
fill="currentFill"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</template>
|
||||
361
src/components/StorageRequests.vue
Normal file
361
src/components/StorageRequests.vue
Normal file
@ -0,0 +1,361 @@
|
||||
<script setup>
|
||||
import { inject, ref, onMounted, computed } from 'vue'
|
||||
import { initModals } from 'flowbite'
|
||||
import { useEventsStore } from '../stores/events'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import CodexImage from './CodexImage.vue'
|
||||
import { shortHex } from '@/utils/ids'
|
||||
|
||||
const eventsStore = useEventsStore()
|
||||
const { requests } = storeToRefs(eventsStore)
|
||||
|
||||
function getStateColour(state) {
|
||||
if (state === 'New') {
|
||||
return 'bg-yellow-200'
|
||||
} else if (state === 'Started') {
|
||||
return 'bg-green-500'
|
||||
} else {
|
||||
return 'bg-red-500'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <ul class="requests">
|
||||
<li v-for="([requestId, { request, state }], idx) in requests" :key="{ requestId }">
|
||||
{{ idx }}.
|
||||
<div>CID: {{ request[2][0] }}</div>
|
||||
<div>RequestID: {{ requestId }}</div>
|
||||
<div>State: {{ state }}</div>
|
||||
<CodexImage v-if="state == 'New' || state == 'Started'" cid="request[2][0]" />
|
||||
</li>
|
||||
</ul> -->
|
||||
|
||||
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<div
|
||||
class="flex items-center justify-between flex-column md:flex-row flex-wrap space-y-4 md:space-y-0 py-4 bg-white dark:bg-gray-900"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
id="dropdownActionButton"
|
||||
data-dropdown-toggle="dropdownAction"
|
||||
class="inline-flex items-center text-gray-500 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-3 py-1.5 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700"
|
||||
type="button"
|
||||
>
|
||||
<span class="sr-only">Action button</span>
|
||||
Action
|
||||
<svg
|
||||
class="w-2.5 h-2.5 ms-2.5"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 10 6"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m1 1 4 4 4-4"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<!-- Dropdown menu -->
|
||||
<div
|
||||
id="dropdownAction"
|
||||
class="z-10 hidden bg-white divide-y divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700 dark:divide-gray-600"
|
||||
>
|
||||
<ul
|
||||
class="py-1 text-sm text-gray-700 dark:text-gray-200"
|
||||
aria-labelledby="dropdownActionButton"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
>Reward</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
>Promote</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
>Activate account</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="py-1">
|
||||
<a
|
||||
href="#"
|
||||
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
|
||||
>Delete User</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label for="table-search-users" class="sr-only">Search</label>
|
||||
<div class="relative">
|
||||
<div
|
||||
class="absolute inset-y-0 rtl:inset-r-0 start-0 flex items-center ps-3 pointer-events-none"
|
||||
>
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="table-search-users"
|
||||
class="block pt-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg w-80 bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Search for users"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3">RequestID</th>
|
||||
<th scope="col" class="px-6 py-3">CID</th>
|
||||
<th scope="col" class="px-6 py-3">State</th>
|
||||
<th scope="col" class="px-6 py-3">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="([requestId, { request, state }], idx) in requests"
|
||||
:key="{ requestId }"
|
||||
class="bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-600"
|
||||
>
|
||||
<th
|
||||
scope="row"
|
||||
class="flex items-center px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
<CodexImage
|
||||
:local-only="!['New', 'Started'].includes(state)"
|
||||
:cid="request[2][0]"
|
||||
class="w-10 h-10 rounded-full mt-1"
|
||||
/>
|
||||
<!-- class="w-10 h-10 rounded-full"
|
||||
src="/docs/images/people/profile-picture-4.jpg"
|
||||
alt="Jese image"
|
||||
/> -->
|
||||
<div class="ps-3">
|
||||
<div class="text-base font-semibold">{{ shortHex(requestId) }}</div>
|
||||
<div class="font-normal text-gray-500">leslie@flowbite.com</div>
|
||||
</div>
|
||||
</th>
|
||||
<td class="px-6 py-4">{{ shortHex(request[2][0]) }}</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="flex items-center">
|
||||
<div :class="`h-2.5 w-2.5 rounded-full ${getStateColour(state)} me-2`"></div>
|
||||
{{ state }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<!-- Modal toggle -->
|
||||
<a
|
||||
href="#"
|
||||
type="button"
|
||||
data-modal-show="editUserModal"
|
||||
class="font-medium text-blue-600 dark:text-blue-500 hover:underline"
|
||||
>Edit user</a
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Edit user modal -->
|
||||
<div
|
||||
id="editUserModal"
|
||||
tabindex="-1"
|
||||
aria-hidden="true"
|
||||
class="fixed top-0 left-0 right-0 z-50 items-center justify-center hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full"
|
||||
>
|
||||
<div class="relative w-full max-w-2xl max-h-full">
|
||||
<!-- Modal content -->
|
||||
<form class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||
<!-- Modal header -->
|
||||
<div class="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600">
|
||||
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">Edit user</h3>
|
||||
<button
|
||||
type="button"
|
||||
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white"
|
||||
data-modal-hide="editUserModal"
|
||||
>
|
||||
<svg
|
||||
class="w-3 h-3"
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 14 14"
|
||||
>
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal body -->
|
||||
<div class="p-6 space-y-6">
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="first-name"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>First Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
name="first-name"
|
||||
id="first-name"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Bonnie"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="last-name"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>Last Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
name="last-name"
|
||||
id="last-name"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Green"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="email"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>Email</label
|
||||
>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
id="email"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="example@company.com"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="phone-number"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>Phone Number</label
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
name="phone-number"
|
||||
id="phone-number"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="e.g. +(12)3456 789"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="department"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>Department</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
name="department"
|
||||
id="department"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="Development"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="company"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>Company</label
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
name="company"
|
||||
id="company"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="123456"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="current-password"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>Current Password</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
name="current-password"
|
||||
id="current-password"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="••••••••"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3">
|
||||
<label
|
||||
for="new-password"
|
||||
class="block mb-2 text-sm font-medium text-gray-900 dark:text-white"
|
||||
>New Password</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
name="new-password"
|
||||
id="new-password"
|
||||
class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
placeholder="••••••••"
|
||||
required=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal footer -->
|
||||
<div
|
||||
class="flex items-center p-6 space-x-3 rtl:space-x-reverse border-t border-gray-200 rounded-b dark:border-gray-600"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||
>
|
||||
Save all
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
88
src/components/TheWelcome.vue
Normal file
88
src/components/TheWelcome.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<script setup>
|
||||
import WelcomeItem from './WelcomeItem.vue'
|
||||
import DocumentationIcon from './icons/IconDocumentation.vue'
|
||||
import ToolingIcon from './icons/IconTooling.vue'
|
||||
import EcosystemIcon from './icons/IconEcosystem.vue'
|
||||
import CommunityIcon from './icons/IconCommunity.vue'
|
||||
import SupportIcon from './icons/IconSupport.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<DocumentationIcon />
|
||||
</template>
|
||||
<template #heading>Documentation</template>
|
||||
|
||||
Vue’s
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
|
||||
provides you with all information you need to get started.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<ToolingIcon />
|
||||
</template>
|
||||
<template #heading>Tooling</template>
|
||||
|
||||
This project is served and bundled with
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
|
||||
recommended IDE setup is
|
||||
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
|
||||
you need to test your components and web pages, check out
|
||||
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
|
||||
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
|
||||
>Cypress Component Testing</a
|
||||
>.
|
||||
|
||||
<br />
|
||||
|
||||
More instructions are available in <code>README.md</code>.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<EcosystemIcon />
|
||||
</template>
|
||||
<template #heading>Ecosystem</template>
|
||||
|
||||
Get official tools and libraries for your project:
|
||||
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
|
||||
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
|
||||
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
|
||||
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
|
||||
you need more resources, we suggest paying
|
||||
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
|
||||
a visit.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<CommunityIcon />
|
||||
</template>
|
||||
<template #heading>Community</template>
|
||||
|
||||
Got stuck? Ask your question on
|
||||
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
|
||||
Discord server, or
|
||||
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
|
||||
>StackOverflow</a
|
||||
>. You should also subscribe to
|
||||
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
|
||||
the official
|
||||
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
|
||||
twitter account for latest news in the Vue world.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<SupportIcon />
|
||||
</template>
|
||||
<template #heading>Support Vue</template>
|
||||
|
||||
As an independent project, Vue relies on community backing for its sustainability. You can help
|
||||
us by
|
||||
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
|
||||
</WelcomeItem>
|
||||
</template>
|
||||
86
src/components/WelcomeItem.vue
Normal file
86
src/components/WelcomeItem.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="item">
|
||||
<i>
|
||||
<slot name="icon"></slot>
|
||||
</i>
|
||||
<div class="details">
|
||||
<h3>
|
||||
<slot name="heading"></slot>
|
||||
</h3>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.item {
|
||||
margin-top: 0;
|
||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
i {
|
||||
top: calc(50% - 25px);
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
border-radius: 8px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.item:before {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:after {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:first-of-type:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
7
src/components/icons/IconCommunity.vue
Normal file
7
src/components/icons/IconCommunity.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
7
src/components/icons/IconDocumentation.vue
Normal file
7
src/components/icons/IconDocumentation.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
7
src/components/icons/IconEcosystem.vue
Normal file
7
src/components/icons/IconEcosystem.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
7
src/components/icons/IconSupport.vue
Normal file
7
src/components/icons/IconSupport.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
19
src/components/icons/IconTooling.vue
Normal file
19
src/components/icons/IconTooling.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
class="iconify iconify--mdi"
|
||||
width="24"
|
||||
height="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
3
src/index.css
Normal file
3
src/index.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
1391
src/main.js
Normal file
1391
src/main.js
Normal file
File diff suppressed because it is too large
Load Diff
22
src/plugins/CodexPlugin.js
Normal file
22
src/plugins/CodexPlugin.js
Normal file
@ -0,0 +1,22 @@
|
||||
export default {
|
||||
install: (app, { codexRestUrl, myAddress }) => {
|
||||
// const baseUrl = new URL(codexRestUrl)
|
||||
const options = {
|
||||
mode: 'cors'
|
||||
}
|
||||
const codexApi = {
|
||||
info: () => {
|
||||
return fetch(`${codexRestUrl}/debug/info`, options)
|
||||
},
|
||||
download: (cid) => {
|
||||
return fetch(`${codexRestUrl}/data/${cid}/network`, options)
|
||||
},
|
||||
downloadLocal: (cid) => {
|
||||
return fetch(`${codexRestUrl}/data/${cid}`, options)
|
||||
}
|
||||
}
|
||||
|
||||
app.provide('myAddress', myAddress)
|
||||
app.provide('codexApi', codexApi)
|
||||
}
|
||||
}
|
||||
10
src/plugins/EthersPlugin.js
Normal file
10
src/plugins/EthersPlugin.js
Normal file
@ -0,0 +1,10 @@
|
||||
import {ethers} from 'ethers'
|
||||
|
||||
export default {
|
||||
install: (app, options) => {
|
||||
let { rpcUrl } = app.config.globalProperties
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl)
|
||||
|
||||
app.provide('ethProvider', provider)
|
||||
}
|
||||
}
|
||||
12
src/plugins/MarketplacePlugin.js
Normal file
12
src/plugins/MarketplacePlugin.js
Normal file
@ -0,0 +1,12 @@
|
||||
import {ethers} from 'ethers'
|
||||
|
||||
export default {
|
||||
install: (app, {address, abi}) => {
|
||||
let { rpcUrl } = app.config.globalProperties
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl)
|
||||
|
||||
const marketplace = new ethers.Contract(address, abi, provider)
|
||||
|
||||
app.provide('marketplace', marketplace)
|
||||
}
|
||||
}
|
||||
12
src/plugins/TestTokenPlugin.js
Normal file
12
src/plugins/TestTokenPlugin.js
Normal file
@ -0,0 +1,12 @@
|
||||
import {ethers} from 'ethers'
|
||||
|
||||
export default {
|
||||
install: (app, {abi, address}) => {
|
||||
let { rpcUrl } = app.config.globalProperties
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl)
|
||||
|
||||
const token = new ethers.Contract(address, abi, provider)
|
||||
|
||||
app.provide('token', token)
|
||||
}
|
||||
}
|
||||
23
src/router/index.js
Normal file
23
src/router/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import RequestsView from '../views/RequestsView.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'requests',
|
||||
component: RequestsView
|
||||
},
|
||||
{
|
||||
path: '/slots',
|
||||
name: 'slots',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import('../views/SlotsView.vue')
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
12
src/stores/counter.js
Normal file
12
src/stores/counter.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useCounterStore = defineStore('counter', () => {
|
||||
const count = ref(0)
|
||||
const doubleCount = computed(() => count.value * 2)
|
||||
function increment() {
|
||||
count.value++
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment }
|
||||
})
|
||||
130
src/stores/events.js
Normal file
130
src/stores/events.js
Normal file
@ -0,0 +1,130 @@
|
||||
import { ref, computed, inject } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { slotId } from '../utils/ids'
|
||||
|
||||
|
||||
export const useEventsStore = defineStore('events', () => {
|
||||
const marketplace = inject('marketplace')
|
||||
let {StorageRequested, SlotFilled} = marketplace.filters
|
||||
const requests = ref(new Map()) // key: requestId, val: {request, state}
|
||||
const slots = ref(new Map()) // key: slotId, val: {requestId, slotIdx, state}
|
||||
const blockNumbers = ref(new Set()) // includes blocks that had events
|
||||
const storageRequestedEvents = ref([]) // {blockNumber, requestId}
|
||||
const slotFilledEvents = ref([]) // {blockNumber, requestId, slotIdx, slotId}
|
||||
const slotFreedEvents = ref([]) // {blockNumber, requestId, slotIdx, slotId}
|
||||
const requestStartedEvents = ref([]) // {blockNumber, requestId}
|
||||
const requestCancelledEvents = ref([]) // {blockNumber, requestId}
|
||||
const requestFailedEvents = ref([]) // {blockNumber, requestId}
|
||||
const requestFinishedEvents = ref([]) // {blockNumber, requestId}
|
||||
const loading = ref(false)
|
||||
|
||||
// onStorageRequested => add request to requests ref
|
||||
// => add to storageRequestedEvents {blockNumber, requestId}
|
||||
// => add request slots to slots ref
|
||||
// => add blockNumber to blockNumbers
|
||||
// onRequestStarted => update requests[requestId].state with marketplace.getRequestState(requestId)
|
||||
// => add to requestStartedEvents {blockNumber, requestId}
|
||||
// => add blockNumber to blockNumbers
|
||||
// onRequestCancelled => update requests[requestId].state with marketplace.getRequestState(requestId)
|
||||
// => add to requestCancelledEvents {blockNumber, requestId}
|
||||
// => add blockNumber to blockNumbers
|
||||
// onRequestFailed => update requests[requestId].state with marketplace.getRequestState(requestId)
|
||||
// => add to requestFailedEvents {blockNumber, requestId}
|
||||
// => add blockNumber to blockNumbers
|
||||
// onRequestFinished => update requests[requestId].state with marketplace.getRequestState(requestId)
|
||||
// => add to requestFinishedEvents {blockNumber, requestId}
|
||||
// => add blockNumber to blockNumbers
|
||||
// onSlotFilled => update slots[slotId].state with getSlotState
|
||||
// => add to slotFilledEvents {blockNumber, slotId, slotIdx}
|
||||
// => add blockNumber to blockNumbers
|
||||
// onSlotFreed => update slots[slotId].state with getSlotState
|
||||
// => add to slotFreedEvents {blockNumber, slotId, slotIdx}
|
||||
// => add blockNumber to blockNumbers
|
||||
const requestState = [
|
||||
"New", // [default] waiting to fill slots
|
||||
"Started", // all slots filled, accepting regular proofs
|
||||
"Cancelled", // not enough slots filled before expiry
|
||||
"Finished", // successfully completed
|
||||
"Failed" // too many nodes have failed to provide proofs, data lost
|
||||
]
|
||||
|
||||
const getRequestState = async (requestId) => {
|
||||
let stateIdx = await marketplace.requestState(requestId)
|
||||
return requestState[stateIdx]
|
||||
}
|
||||
|
||||
const storageRequested = async (blockNumber, requestId, request, state) => {
|
||||
requests.value.set(requestId, {request, state})
|
||||
storageRequestedEvents.value.push({blockNumber, requestId})
|
||||
let ask = request[1]
|
||||
let numSlots = ask[0]
|
||||
for (let i=0; i<numSlots; i++){
|
||||
let id = slotId(requestId, i)
|
||||
let state = await marketplace.slotState(id)
|
||||
slots.value.set(id, {requestId, slotIdx: i, state})
|
||||
}
|
||||
blockNumbers.value.add(blockNumber)
|
||||
}
|
||||
|
||||
async function fetchPastEvents() {
|
||||
// query past events
|
||||
loading.value = true
|
||||
try {
|
||||
let requests = (await marketplace.queryFilter(StorageRequested))
|
||||
requests.forEach(async (event) => {
|
||||
let {requestId, ask, expiry} = event.args
|
||||
let {blockNumber} = event
|
||||
let request = await marketplace.getRequest(requestId)
|
||||
let state = await getRequestState(requestId)
|
||||
storageRequested(blockNumber, requestId, request, state)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(`failed to load past contract events: ${error.message}`)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
// let slotsFilled = (await marketplace.queryFilter(SlotFilled))
|
||||
// slotsFilled.forEach(async (event) => {
|
||||
// let {requestId, slotIdx} = event.args
|
||||
// let {blockNumber} = event
|
||||
// let request = await marketplace.getRequest(requestId)
|
||||
// let state = await getRequestState(requestId)
|
||||
// storageRequested(blockNumber, requestId, request, state)
|
||||
// })
|
||||
}
|
||||
|
||||
async function listenForNewEvents(onStorageRequested) {
|
||||
marketplace.on(StorageRequested, async (requestId, ask, expiry, event) => {
|
||||
let {blockNumber} = event
|
||||
let request = await marketplace.getRequest(requestId)
|
||||
let state = await getRequestState(requestId)
|
||||
storageRequested(blockNumber, requestId, request, state)
|
||||
if (onStorageRequested) { // callback
|
||||
onStorageRequested(blockNumber, requestId, request, state)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// const eventsByBlock = computed(() =>)
|
||||
// const doubleCount = computed(() => count.value * 2)
|
||||
// function increment() {
|
||||
// count.value++
|
||||
// }
|
||||
|
||||
return {
|
||||
requests,
|
||||
slots,
|
||||
blockNumbers,
|
||||
storageRequestedEvents,
|
||||
slotFilledEvents,
|
||||
slotFreedEvents,
|
||||
requestStartedEvents,
|
||||
requestCancelledEvents,
|
||||
requestFailedEvents,
|
||||
requestFinishedEvents,
|
||||
fetchPastEvents,
|
||||
listenForNewEvents,
|
||||
loading
|
||||
}
|
||||
})
|
||||
53
src/utils/ids.js
Normal file
53
src/utils/ids.js
Normal file
@ -0,0 +1,53 @@
|
||||
import { keccak256 } from 'ethers/crypto'
|
||||
import { AbiCoder } from 'ethers/abi'
|
||||
|
||||
// func shortLog*(long: string, ellipses = "*", start = 3, stop = 6): string =
|
||||
// ## Returns compact string representation of ``long``.
|
||||
// var short = long
|
||||
// let minLen = start + ellipses.len + stop
|
||||
// if len(short) > minLen:
|
||||
// short.insert(ellipses, start)
|
||||
|
||||
// when (NimMajor, NimMinor) > (1, 4):
|
||||
// short.delete(start + ellipses.len .. short.high - stop)
|
||||
// else:
|
||||
// short.delete(start + ellipses.len, short.high - stop)
|
||||
|
||||
// short
|
||||
|
||||
// func shortHexLog*(long: string): string =
|
||||
// if long[0..1] == "0x": result &= "0x"
|
||||
// result &= long[2..long.high].shortLog("..", 4, 4)
|
||||
|
||||
// func short0xHexLog*[N: static[int], T: array[N, byte]](v: T): string =
|
||||
// v.to0xHex.shortHexLog
|
||||
|
||||
export function short(long, ellipses = '*', start = 3, stop = 6) {
|
||||
var short = long
|
||||
const minLen = start + ellipses.length + stop
|
||||
if (short.length > minLen) {
|
||||
let prefix = short.substring(0, start)
|
||||
let suffix = short.substring(long.length - stop)
|
||||
short = `${prefix}${ellipses}${suffix}`
|
||||
}
|
||||
return short
|
||||
}
|
||||
|
||||
export function shortHex(long) {
|
||||
let shortened = ''
|
||||
let rest = long
|
||||
if (long.substring(0, 2) === '0x') {
|
||||
shortened = '0x'
|
||||
rest = long.substring(2)
|
||||
}
|
||||
shortened += short(rest, '..', 4, 4)
|
||||
return shortened
|
||||
}
|
||||
|
||||
export function slotId(requestId, slotIdx) {
|
||||
const types = 'tuple(bytes32, uint256)'
|
||||
const values = [requestId, slotIdx]
|
||||
const coder = AbiCoder.defaultAbiCoder()
|
||||
const encoding = coder.encode([types], [values])
|
||||
return keccak256(encoding)
|
||||
}
|
||||
16
src/views/RequestsView.vue
Normal file
16
src/views/RequestsView.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useEventsStore } from '../stores/events'
|
||||
import StorageRequests from '../components/StorageRequests.vue'
|
||||
import SkeletonLoading from '@/components/SkeletonLoading.vue'
|
||||
|
||||
const eventsStore = useEventsStore()
|
||||
const { loading } = storeToRefs(eventsStore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<SkeletonLoading v-if="loading" type="image" />
|
||||
<StorageRequests v-else />
|
||||
</main>
|
||||
</template>
|
||||
10
src/views/SlotsView.vue
Normal file
10
src/views/SlotsView.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<script setup>
|
||||
import Slots from '../components/Slots.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<Slots/>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
15
tailwind.config.js
Normal file
15
tailwind.config.js
Normal file
@ -0,0 +1,15 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
"./node_modules/flowbite/**/*.js"
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [
|
||||
require('flowbite/plugin')
|
||||
],
|
||||
}
|
||||
|
||||
18
vite.config.js
Normal file
18
vite.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import VueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
VueDevTools(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
}
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user