Compare commits
20 Commits
Author | SHA1 | Date |
---|---|---|
Lev | 57a4be9f81 | 2 months ago |
Aleksandr Bautin | 2b06910a6f | 11 months ago |
Aleksandr Bautin | 2fa6006913 | 11 months ago |
Aleksandr Bautin | bb8500204e | 11 months ago |
Aleksandr Bautin | 0a69c37140 | 12 months ago |
Aleksandr Bautin | f9cac6b4de | 12 months ago |
Aleksandr Bautin | 3b97e20146 | 12 months ago |
Aleksandr Bautin | b1e3658eb7 | 1 year ago |
Aleksandr Bautin | 59e8a2b7a2 | 1 year ago |
Aleksandr Bautin | 34b767d631 | 1 year ago |
Aleksandr Bautin | 51730dc347 | 1 year ago |
Lev | 52ad71712e | 1 year ago |
Lev | f97fef7164 | 1 year ago |
Lev | e4ce4e189d | 1 year ago |
Lev | 3b28d9a1d5 | 1 year ago |
Aleksandr Bautin | 7a5e80722b | 1 year ago |
Aleksandr Bautin | 8279679046 | 1 year ago |
Aleksandr Bautin | 72a7d725f0 | 1 year ago |
Aleksandr Bautin | a8a105fc03 | 1 year ago |
Lev | 60b5b4e657 | 1 year ago |
39 changed files with 2554 additions and 818 deletions
@ -1,5 +1,5 @@ |
|||||||
{ |
{ |
||||||
"url": "https://agorata.io/", |
"url": "https://tonski-an7.pages.dev", |
||||||
"name": "Agorata", |
"name": "Agorata", |
||||||
"iconUrl": "https://front.agorata.io/favicon.png" |
"iconUrl": "https://front.agorata.io/favicon.png" |
||||||
} |
} |
||||||
|
@ -1,30 +1,71 @@ |
|||||||
import axios from 'axios' |
import axios from "axios"; |
||||||
|
|
||||||
declare var process : { |
declare var process: { |
||||||
env: { |
env: { |
||||||
NODE_ENV: string |
NODE_ENV: string; |
||||||
} |
}; |
||||||
} |
}; |
||||||
|
|
||||||
export class Api { |
export class Api { |
||||||
public readonly api_url: string; |
public readonly api_url: string; |
||||||
public agorata_adnl: string = "ed4f2afebb5e49dda9684a474c5771141be1f7d85a2fa39f1823844dd476c52d"; |
public readonly ton_api_url: string; |
||||||
|
public readonly tonscan_url: string; |
||||||
|
public agorata_adnl: string = |
||||||
|
"ed4f2afebb5e49dda9684a474c5771141be1f7d85a2fa39f1823844dd476c52d"; |
||||||
|
public readonly tonviewer_url: string; |
||||||
|
|
||||||
constructor() { |
constructor() { |
||||||
if (process.env.NODE_ENV === 'development') { |
if (process.env.NODE_ENV === "development") { |
||||||
this.api_url = 'http://localhost:5170/'; |
this.api_url = "http://localhost:5170/"; |
||||||
} else { |
this.ton_api_url = "https://testnet.tonapi.io/v2/"; |
||||||
this.api_url = 'https://api.agorata.io/'; |
this.tonscan_url = "https://testnet.tonscan.org/"; |
||||||
} |
this.tonviewer_url = "https://testnet.tonviewer.com/"; |
||||||
|
} else { |
||||||
|
this.api_url = "https://agorata.io/api/"; |
||||||
|
this.ton_api_url = "https://tonapi.io/v2/"; |
||||||
|
this.tonscan_url = "https://tonscan.org/"; |
||||||
|
this.tonviewer_url = "https://testnet.tonviewer.com/"; |
||||||
} |
} |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
export const config = new Api(); |
export const config = new Api(); |
||||||
|
|
||||||
export async function call_api(url: string) { |
export async function call_api(url: string) { |
||||||
return (await axios.get(config.api_url + url)).data; |
return (await axios.get(config.api_url + url)).data; |
||||||
} |
} |
||||||
|
|
||||||
export async function call_api_post(url: string, data: any) { |
export async function call_api_post(url: string, data: any) { |
||||||
return (await axios.post(config.api_url + url, data)).data; |
return (await axios.post(config.api_url + url, data)).data; |
||||||
|
} |
||||||
|
|
||||||
|
export async function get_templates(url: string) { |
||||||
|
return await new Promise((resolve) => |
||||||
|
resolve([ |
||||||
|
{ |
||||||
|
title: "", |
||||||
|
description: "", |
||||||
|
links: [{ telegram: "", mail: "", site: "" }], |
||||||
|
preview: "https://api.agorata.io/static/mountain.jpg", |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: "", |
||||||
|
description: "", |
||||||
|
links: [{ telegram: "", mail: "", site: "" }], |
||||||
|
preview: "https://api.agorata.io/static/mountain.jpg", |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: "", |
||||||
|
description: "", |
||||||
|
links: [{ telegram: "", mail: "", site: "" }], |
||||||
|
preview: "https://api.agorata.io/static/mountain.jpg", |
||||||
|
}, |
||||||
|
// {
|
||||||
|
// title: "",
|
||||||
|
// description: "",
|
||||||
|
// links: [{ telegram: "", mail: "", site: "" }],
|
||||||
|
// preview: "https://api.agorata.io/static/mountain.jpg",
|
||||||
|
// },
|
||||||
|
]) |
||||||
|
); |
||||||
} |
} |
||||||
|
@ -1,197 +1,196 @@ |
|||||||
@import './base.css'; |
@import "./base.css"; |
||||||
|
|
||||||
@tailwind base; |
@tailwind base; |
||||||
@tailwind components; |
@tailwind components; |
||||||
@tailwind utilities; |
@tailwind utilities; |
||||||
|
|
||||||
#app { |
#app { |
||||||
margin: 0 auto; |
margin: 0 auto; |
||||||
padding: 0; |
padding: 0; |
||||||
min-width: 100vw; |
min-width: 100vw; |
||||||
min-height: 100vh; |
min-height: 100vh; |
||||||
} |
} |
||||||
|
|
||||||
a, |
a, |
||||||
.green { |
.green { |
||||||
text-decoration: none; |
text-decoration: none; |
||||||
color: hsl(201, 100%, 37%); |
color: hsl(201, 100%, 37%); |
||||||
transition: 0.4s; |
transition: 0.4s; |
||||||
} |
} |
||||||
|
|
||||||
@media (min-width: 1024px) { |
@media (min-width: 1024px) { |
||||||
body { |
body { |
||||||
display: flex; |
display: flex; |
||||||
place-items: center; |
place-items: center; |
||||||
} |
} |
||||||
|
|
||||||
#app { |
#app { |
||||||
display: grid; |
display: grid; |
||||||
grid-template-columns: 1fr 1fr; |
grid-template-columns: 1fr 1fr; |
||||||
padding: 0; |
padding: 0; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
.b { |
.b { |
||||||
border: 2px solid transparent; |
border: 2px solid transparent; |
||||||
border-radius: 5px; |
border-radius: 5px; |
||||||
padding: 16px 26px; |
padding: 16px 26px; |
||||||
text-align: center; |
text-align: center; |
||||||
text-decoration: none; |
text-decoration: none; |
||||||
display: inline-block; |
display: inline-block; |
||||||
font-size: 1.55rem; |
font-size: 1.55rem; |
||||||
margin: 4px 2px; |
margin: 4px 2px; |
||||||
cursor: pointer; |
cursor: pointer; |
||||||
font-weight: bold; |
font-weight: bold; |
||||||
} |
} |
||||||
|
|
||||||
@media (max-width: 800px) { |
@media (max-width: 800px) { |
||||||
.b { |
.b { |
||||||
font-size: 1.4rem; |
font-size: 1.4rem; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
.b.white { |
.b.white { |
||||||
background-color: white; |
background-color: white; |
||||||
color: #282e46; |
color: #282e46; |
||||||
} |
} |
||||||
|
|
||||||
.b.white:hover { |
.b.white:hover { |
||||||
background-color: #282e46; |
background-color: #282e46; |
||||||
color: white; |
color: white; |
||||||
border: 2px solid white; |
border: 2px solid white; |
||||||
} |
} |
||||||
|
|
||||||
.b.blue { |
.b.blue { |
||||||
background-color: #0088cc; |
background-color: #0088cc; |
||||||
color: white; |
color: white; |
||||||
border-radius: 1rem; |
border-radius: 1rem; |
||||||
} |
} |
||||||
|
|
||||||
.b > img:first-child { |
.b > img:first-child { |
||||||
max-height: 1.4rem; |
max-height: 1.4rem; |
||||||
margin-right: 0.7rem; |
margin-right: 0.7rem; |
||||||
} |
} |
||||||
|
|
||||||
.b > img:not(:first-child) { |
.b > img:not(:first-child) { |
||||||
max-height: 1.4rem; |
max-height: 1.4rem; |
||||||
margin-left: 2rem; |
margin-left: 2rem; |
||||||
} |
} |
||||||
|
|
||||||
.wide.b { |
.wide.b { |
||||||
min-width: 16rem; |
min-width: 16rem; |
||||||
margin: 0.5rem; |
margin: 0.5rem; |
||||||
} |
} |
||||||
|
|
||||||
/* A #edeef1 box with rounded corners with black content color and content centered vertically and horizontally */ |
/* A #edeef1 box with rounded corners with black content color and content centered vertically and horizontally */ |
||||||
.rbox { |
.rbox { |
||||||
background-color: #edeef1; |
background-color: #edeef1; |
||||||
border-radius: 2rem; |
border-radius: 2rem; |
||||||
padding: 2.5rem 32px; |
padding: 2.5rem 32px; |
||||||
text-align: center; |
text-align: center; |
||||||
display: flex; |
display: flex; |
||||||
align-items: center; |
align-items: center; |
||||||
place-content: center; |
place-content: center; |
||||||
justify-content: center; |
justify-content: center; |
||||||
font-size: 1.6rem; |
font-size: 1.6rem; |
||||||
font-weight: bold; |
font-weight: bold; |
||||||
color: #282e46; |
color: #282e46; |
||||||
min-width: 16rem; |
min-width: 16rem; |
||||||
margin: 0.5rem 3rem; |
margin: 0.5rem 3rem; |
||||||
} |
} |
||||||
|
|
||||||
/* small margin on mobile */ |
/* small margin on mobile */ |
||||||
@media (max-width: 800px) { |
@media (max-width: 800px) { |
||||||
.rbox { |
.rbox { |
||||||
margin: 0.5rem 0.5rem; |
margin: 0.5rem 0.5rem; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
.rbox > p:not(:first-child) { |
.rbox > p:not(:first-child) { |
||||||
margin-top: 1rem; |
margin-top: 1rem; |
||||||
} |
} |
||||||
|
|
||||||
.center { |
.center { |
||||||
display: flex; |
display: flex; |
||||||
justify-content: center; |
justify-content: center; |
||||||
align-items: center; |
align-items: center; |
||||||
place-items: center; |
place-items: center; |
||||||
flex-direction: column; |
flex-direction: column; |
||||||
} |
} |
||||||
|
|
||||||
.b.darkish { |
.b.darkish { |
||||||
background-color: #4e5a88; |
background-color: #4e5a88; |
||||||
color: white; |
color: white; |
||||||
border-radius: 0.5rem; |
border-radius: 0.5rem; |
||||||
font-size: 1rem; |
font-size: 1rem; |
||||||
} |
} |
||||||
|
|
||||||
.mono { |
.mono { |
||||||
font-family: 'Inconsolata', monospace; |
font-family: "Inconsolata", monospace; |
||||||
} |
} |
||||||
|
|
||||||
:root { |
:root { |
||||||
--popper-theme-background-color: #fff; |
--popper-theme-background-color: #fff; |
||||||
--popper-theme-background-color-hover: #fff; |
--popper-theme-background-color-hover: #fff; |
||||||
--popper-theme-text-color: black; |
--popper-theme-text-color: black; |
||||||
--popper-theme-border-width: 0px; |
--popper-theme-border-width: 0px; |
||||||
--popper-theme-border-style: solid; |
--popper-theme-border-style: solid; |
||||||
--popper-theme-border-radius: 6px; |
--popper-theme-border-radius: 6px; |
||||||
--popper-theme-padding: 4px; |
--popper-theme-padding: 4px; |
||||||
--popper-theme-box-shadow: 0 6px 30px -6px rgba(0, 0, 0, 0.25); |
--popper-theme-box-shadow: 0 6px 30px -6px rgba(0, 0, 0, 0.25); |
||||||
} |
} |
||||||
|
|
||||||
.popper { |
.popper { |
||||||
font-size: 0.8rem; |
font-size: 0.8rem; |
||||||
font-family: 'Inconsolata', monospace; |
font-family: "Inconsolata", monospace; |
||||||
} |
} |
||||||
|
|
||||||
.mobile-scale { |
.mobile-scale { |
||||||
scale: 100%; |
scale: 100%; |
||||||
} |
} |
||||||
|
|
||||||
@media (max-width: 800px) { |
@media (max-width: 800px) { |
||||||
.mobile-scale { |
.mobile-scale { |
||||||
scale: 80%; |
scale: 80%; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
.get_b { |
.get_b { |
||||||
background-color: #e36464; |
background-color: #e36464; |
||||||
color: #363e5e; |
color: #363e5e; |
||||||
border-radius: 0.5rem; |
border-radius: 0.5rem; |
||||||
padding: 0.2rem 0.8rem; |
padding: 0.2rem 0.8rem; |
||||||
margin-left: 0.9rem; |
cursor: pointer; |
||||||
cursor: pointer; |
|
||||||
} |
} |
||||||
|
|
||||||
@media only screen and (max-width: 800px) { |
@media only screen and (max-width: 800px) { |
||||||
.get_b { |
.get_b { |
||||||
margin-left: 0.1rem; |
margin-left: 0.1rem; |
||||||
padding-left: 0.4rem; |
padding-left: 0.4rem; |
||||||
padding-right: 0.4rem; |
padding-right: 0.4rem; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
.flex { |
.flex { |
||||||
display: flex; |
display: flex; |
||||||
} |
} |
||||||
|
|
||||||
.b.back { |
.b.back { |
||||||
font-family: 'Inconsolata', monospace; |
font-family: "Inconsolata", monospace; |
||||||
font-size: 1.5rem; |
font-size: 1.5rem; |
||||||
padding: 0.8rem; |
padding: 0.8rem; |
||||||
} |
} |
||||||
|
|
||||||
.b.back > img { |
.b.back > img { |
||||||
max-height: 1rem; |
max-height: 1rem; |
||||||
margin-right: 0.4rem; |
margin-right: 0.4rem; |
||||||
} |
} |
||||||
|
|
||||||
.material-icons.language { |
.material-icons.language { |
||||||
position:relative; |
position: relative; |
||||||
display:inline-block; |
display: inline-block; |
||||||
} |
} |
||||||
|
|
||||||
.material-icons.language:after { |
.material-icons.language:after { |
||||||
content: "language"; |
content: "language"; |
||||||
} |
} |
||||||
|
@ -0,0 +1,98 @@ |
|||||||
|
<template> |
||||||
|
<div class="drag-drop-uploader"> |
||||||
|
<div class="drag-drop-area" @dragover="handleDragOver" @drop="handleDrop"> |
||||||
|
<template v-if="uploadedFiles.length === 0"> |
||||||
|
<button class="button" type="button" @click="openFileInput"> |
||||||
|
Add File |
||||||
|
</button> |
||||||
|
<p>...or drag and drop a file.</p> |
||||||
|
</template> |
||||||
|
<template v-else> |
||||||
|
<button |
||||||
|
class="button" |
||||||
|
type="button" |
||||||
|
v-if="uploadedFiles.length > 0" |
||||||
|
@click="openFileInput" |
||||||
|
> |
||||||
|
Change |
||||||
|
</button> |
||||||
|
<p>{{ uploadedFiles.at(-1).name }}</p> |
||||||
|
</template> |
||||||
|
</div> |
||||||
|
<input |
||||||
|
type="file" |
||||||
|
ref="fileInput" |
||||||
|
style="display: none" |
||||||
|
@change="handleFileInput" |
||||||
|
multiple |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
export default { |
||||||
|
data() { |
||||||
|
return { |
||||||
|
uploadedFiles: [], |
||||||
|
}; |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
handleDragOver(event) { |
||||||
|
event.preventDefault(); |
||||||
|
}, |
||||||
|
handleDrop(event) { |
||||||
|
event.preventDefault(); |
||||||
|
const files = event.dataTransfer.files; |
||||||
|
this.uploadFiles(files); |
||||||
|
}, |
||||||
|
openFileInput() { |
||||||
|
this.$refs.fileInput.click(); |
||||||
|
}, |
||||||
|
handleFileInput(event) { |
||||||
|
const files = event.target.files; |
||||||
|
this.uploadFiles(files); |
||||||
|
}, |
||||||
|
uploadFiles(files) { |
||||||
|
console.log(files); |
||||||
|
for (let i = 0; i < files.length; i++) { |
||||||
|
this.uploadedFiles.push(files[i]); |
||||||
|
} |
||||||
|
}, |
||||||
|
change() { |
||||||
|
this.openFileInput(); |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.drag-drop-uploader { |
||||||
|
margin: 0 auto; |
||||||
|
} |
||||||
|
|
||||||
|
.drag-drop-area { |
||||||
|
display: grid; |
||||||
|
align-content: center; |
||||||
|
border: 2px dashed #ccc; |
||||||
|
border-radius: 10px; |
||||||
|
padding: 20px; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
|
||||||
|
ul { |
||||||
|
margin-top: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
justify-self: center; |
||||||
|
border: 0; |
||||||
|
border-radius: 0.5rem; |
||||||
|
height: 3rem; |
||||||
|
background-color: #e36464; |
||||||
|
color: #363e5e; |
||||||
|
font-size: 1.3rem; |
||||||
|
font-weight: 500; |
||||||
|
padding: 0 1rem; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,198 @@ |
|||||||
|
<template> |
||||||
|
<table class="table_outer"> |
||||||
|
<tbody class="table_content"> |
||||||
|
<tr v-for="item in items"> |
||||||
|
<td> |
||||||
|
<div style="display: flex; align-items: center; gap: 12px"> |
||||||
|
<img |
||||||
|
style="border-radius: 10px" |
||||||
|
width="50" |
||||||
|
height="50" |
||||||
|
:src="item.previews[1].url" |
||||||
|
:alt="item?.metadata?.name" |
||||||
|
/> |
||||||
|
|
||||||
|
<Tooltip text="View in explorer"> |
||||||
|
<a |
||||||
|
target="_blank" |
||||||
|
:href="`${config.tonscan_url}address/${item?.address}`" |
||||||
|
> |
||||||
|
<img src="@/assets/icons/link.svg" /> |
||||||
|
</a> |
||||||
|
</Tooltip> |
||||||
|
<span |
||||||
|
style=" |
||||||
|
white-space: nowrap; |
||||||
|
overflow: hidden; |
||||||
|
text-overflow: ellipsis; |
||||||
|
" |
||||||
|
> |
||||||
|
{{ item?.metadata?.name }} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<Tooltip text="Go to site"> |
||||||
|
<a target="_blank" :href="item?.dns"> |
||||||
|
<img |
||||||
|
src="@/assets/icons/globe.svg" |
||||||
|
style="display: flex" |
||||||
|
cursor="pointer" |
||||||
|
/> |
||||||
|
</a> |
||||||
|
</Tooltip> |
||||||
|
</td> |
||||||
|
<td> |
||||||
|
<button @click="onManageClick(item)" class="button">Manage</button> |
||||||
|
</td> |
||||||
|
</tr> |
||||||
|
</tbody> |
||||||
|
</table> |
||||||
|
<div class="center"> |
||||||
|
<RotateSquare2 |
||||||
|
v-if="isLoading" |
||||||
|
style="width: 5rem; height: 5rem; margin-top: 3rem" |
||||||
|
/> |
||||||
|
<p v-if="error">{{ error }}</p> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import type { CollectionItem } from "@/types"; |
||||||
|
import axios, { AxiosError } from "axios"; |
||||||
|
import { computed, onMounted, ref, watch } from "vue"; |
||||||
|
import Tooltip from "./Tooltip.vue"; |
||||||
|
import { useRouter } from "vue-router"; |
||||||
|
import { get_zones } from "@/result"; |
||||||
|
import type { Zone } from "@/zone"; |
||||||
|
import { convertAddress } from "@/utils"; |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { config } from "@/api"; |
||||||
|
import RotateSquare2 from "../components/RotateSquare2.vue"; |
||||||
|
|
||||||
|
const store = useStore(); |
||||||
|
|
||||||
|
const { push } = useRouter(); |
||||||
|
|
||||||
|
const onManageClick = (item: CollectionItem) => { |
||||||
|
push({ name: "Explore", params: { domain: item.metadata.name } }); |
||||||
|
}; |
||||||
|
|
||||||
|
const zonesAddresses = ref<(string | undefined)[]>([]); |
||||||
|
|
||||||
|
const items = ref<CollectionItem[]>([]); |
||||||
|
|
||||||
|
const isLoading = ref(false); |
||||||
|
const error = ref(""); |
||||||
|
|
||||||
|
const address = computed(() => |
||||||
|
store.getters.address ? convertAddress(store.getters.address) : "" |
||||||
|
); |
||||||
|
|
||||||
|
const fetchDomains = async () => { |
||||||
|
if (address.value) { |
||||||
|
isLoading.value = true; |
||||||
|
|
||||||
|
const zones = (await get_zones()) as Zone[]; |
||||||
|
|
||||||
|
zonesAddresses.value = zones.map(({ address }) => address?.toLowerCase()); |
||||||
|
|
||||||
|
try { |
||||||
|
const [ |
||||||
|
resultNfts = { data: { nft_items: [] } }, |
||||||
|
resultTonNfts = { data: { nft_items: [] } }, |
||||||
|
] = await Promise.all([ |
||||||
|
axios.get<{ nft_items: CollectionItem[] }>( |
||||||
|
`${config.ton_api_url}accounts/${address.value}/nfts` |
||||||
|
), |
||||||
|
axios.get<{ nft_items: CollectionItem[] }>( |
||||||
|
`${config.ton_api_url}accounts/${address.value}/nfts`, |
||||||
|
{ |
||||||
|
params: { |
||||||
|
collection: "EQC3dNlesgVD8YbAazcauIrXBPfiVhMMr5YYk2in0Mtsz0Bz", |
||||||
|
}, |
||||||
|
} |
||||||
|
), |
||||||
|
]); |
||||||
|
|
||||||
|
items.value = [ |
||||||
|
...resultTonNfts.data.nft_items, |
||||||
|
...resultNfts.data.nft_items |
||||||
|
.map((item) => ({ |
||||||
|
...item, |
||||||
|
formattedCollectionAddress: item.collection?.address |
||||||
|
? convertAddress(item.collection?.address).toLowerCase() |
||||||
|
: "", |
||||||
|
})) |
||||||
|
.filter(({ formattedCollectionAddress }) => |
||||||
|
zonesAddresses.value.includes(formattedCollectionAddress) |
||||||
|
), |
||||||
|
]; |
||||||
|
} catch (e) { |
||||||
|
if ((e as AxiosError).response?.status) { |
||||||
|
error.value = "You have made too many requests, please try again later"; |
||||||
|
console.log("too many requests"); |
||||||
|
} |
||||||
|
} finally { |
||||||
|
isLoading.value = false; |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
onMounted(fetchDomains); |
||||||
|
|
||||||
|
watch(address, fetchDomains); |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.table_outer { |
||||||
|
background-color: #4e5a88; |
||||||
|
border-radius: 0.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
td { |
||||||
|
padding: 8px 16px; |
||||||
|
} |
||||||
|
|
||||||
|
td:first-child { |
||||||
|
max-width: 400px; |
||||||
|
} |
||||||
|
|
||||||
|
a { |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
@media only screen and (max-width: 800px) { |
||||||
|
td.zone { |
||||||
|
font-size: 1.1rem; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
th { |
||||||
|
padding: 4px 8px; |
||||||
|
} |
||||||
|
|
||||||
|
.table_content { |
||||||
|
padding: 1rem; |
||||||
|
min-height: 10rem; |
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
border: 0; |
||||||
|
border-radius: 0.5rem; |
||||||
|
height: 3rem; |
||||||
|
background-color: #e36464; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
place-items: center; |
||||||
|
color: #363e5e; |
||||||
|
font-size: 1.3rem; |
||||||
|
font-weight: 500; |
||||||
|
padding: 0 1rem; |
||||||
|
cursor: pointer; |
||||||
|
margin-left: 1rem; |
||||||
|
margin-top: 1rem; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,56 @@ |
|||||||
|
<template> |
||||||
|
<div class="constr-switcher"> |
||||||
|
<div |
||||||
|
v-for="item in items" |
||||||
|
@click="$emit('change', item)" |
||||||
|
:class="['constr-switch', { inactive: activeName !== item.name }]" |
||||||
|
> |
||||||
|
{{ item.name }} |
||||||
|
</div> |
||||||
|
<slot name="suffix" /> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
type Item = { name: string }; |
||||||
|
|
||||||
|
defineProps<{ items: Item[]; activeName: string }>(); |
||||||
|
|
||||||
|
defineEmits<{ |
||||||
|
(e: "change", item: Item): void; |
||||||
|
}>(); |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
/* The switcher with two buttons - create from template or use custom */ |
||||||
|
.constr-switcher { |
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
align-items: center; |
||||||
|
margin-bottom: 1rem; |
||||||
|
border-radius: 1.3rem; |
||||||
|
padding: 1rem; |
||||||
|
background-color: #4e5a88; |
||||||
|
} |
||||||
|
|
||||||
|
/* The button inside the switcher */ |
||||||
|
.constr-switch { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border-radius: 0.7rem; |
||||||
|
background-color: #cdcee8; |
||||||
|
color: #282e46; |
||||||
|
width: 13rem; |
||||||
|
cursor: default; |
||||||
|
} |
||||||
|
|
||||||
|
.constr-switch:not(:last-child) { |
||||||
|
margin-right: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.constr-switch.inactive { |
||||||
|
/*background-color: #5d5f79;*/ |
||||||
|
background-color: #6a6e95; |
||||||
|
color: #cecddb; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,294 @@ |
|||||||
|
<template> |
||||||
|
<div v-for="template in templates"> |
||||||
|
<div v-if="template.name === activeTemplateName"> |
||||||
|
<img class="preview" width="280" height="280" :src="template.preview" /> |
||||||
|
<div style="display: flex; width: 100%"> |
||||||
|
<p style="width: 9rem">Title:</p> |
||||||
|
<contenteditable |
||||||
|
class="record-inp" |
||||||
|
tag="div" |
||||||
|
:no-hl="true" |
||||||
|
:no-html="true" |
||||||
|
spellcheck="false" |
||||||
|
v-model="constructor_params.title" |
||||||
|
></contenteditable> |
||||||
|
</div> |
||||||
|
<div style="display: flex; width: 100%"> |
||||||
|
<p style="width: 9rem">Description:</p> |
||||||
|
<contenteditable |
||||||
|
class="record-inp" |
||||||
|
tag="div" |
||||||
|
:no-hl="true" |
||||||
|
:no-html="true" |
||||||
|
spellcheck="false" |
||||||
|
v-model="constructor_params.description" |
||||||
|
></contenteditable> |
||||||
|
</div> |
||||||
|
<div style="display: flex; width: 100%"> |
||||||
|
<p style="width: 9rem">Picture:</p> |
||||||
|
<contenteditable |
||||||
|
class="record-inp" |
||||||
|
tag="div" |
||||||
|
:no-hl="true" |
||||||
|
:no-html="true" |
||||||
|
spellcheck="false" |
||||||
|
v-model="constructor_params.picture" |
||||||
|
></contenteditable> |
||||||
|
</div> |
||||||
|
<div style="display: flex; width: 100%"> |
||||||
|
<p style="width: 9rem">Add link:</p> |
||||||
|
<div |
||||||
|
v-for="link_type in link_types" |
||||||
|
:key="link_type" |
||||||
|
class="link-type" |
||||||
|
@click="addLink(link_type)" |
||||||
|
> |
||||||
|
<i class="link-type-icon add" :class="link_icons[link_type]"></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<!-- The links themselves (editing constructor_params.contacts[link_type] for each key) --> |
||||||
|
<div v-for="contact in contacts" :key="contact[0]" class="link-type"> |
||||||
|
<div style="display: flex; width: 100%"> |
||||||
|
<!-- the icon as a label --> |
||||||
|
<i |
||||||
|
style="width: 9rem" |
||||||
|
class="link-type-icon" |
||||||
|
:class="link_icons[contact[0]]" |
||||||
|
></i> |
||||||
|
<!-- editing the link - constructor_params.contacts[link_type] --> |
||||||
|
<contenteditable |
||||||
|
class="record-inp" |
||||||
|
tag="div" |
||||||
|
:no-hl="true" |
||||||
|
:no-html="true" |
||||||
|
spellcheck="false" |
||||||
|
v-model="constructor_params.contacts[contact[0]]" |
||||||
|
></contenteditable> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="save_container center"> |
||||||
|
<div |
||||||
|
:class="{ |
||||||
|
'record-submit': true, |
||||||
|
get_b: true, |
||||||
|
inactive: !siteChanged, |
||||||
|
signing: signingSite, |
||||||
|
}" |
||||||
|
@click="$emit('save-constructor')" |
||||||
|
> |
||||||
|
<template v-if="!signingSite">Save and host</template> |
||||||
|
<template v-else> |
||||||
|
<Socket |
||||||
|
secondary-color="#a7aab3" |
||||||
|
color="#282e46" |
||||||
|
size="50px" |
||||||
|
style="min-width: 3rem" |
||||||
|
/> |
||||||
|
Confirm in the wallet... |
||||||
|
</template> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import Socket from "./Socket.vue"; |
||||||
|
import contenteditable from "vue-contenteditable"; |
||||||
|
import { config } from "../api"; |
||||||
|
import { default_links, SiteConstructorParams } from "../result"; |
||||||
|
import { link_types, link_icons } from "../result"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "TemplatesList", |
||||||
|
components: { Socket, contenteditable }, |
||||||
|
props: { |
||||||
|
site_rec_init: { |
||||||
|
default: null, |
||||||
|
}, |
||||||
|
siteChanged: { |
||||||
|
type: Boolean, |
||||||
|
default: false, |
||||||
|
}, |
||||||
|
constructorChanged: { |
||||||
|
type: Boolean, |
||||||
|
default: false, |
||||||
|
}, |
||||||
|
signingSite: { |
||||||
|
type: Boolean, |
||||||
|
default: false, |
||||||
|
}, |
||||||
|
templates: { |
||||||
|
default: [], |
||||||
|
}, |
||||||
|
activeTemplateName: "", |
||||||
|
}, |
||||||
|
data() { |
||||||
|
let site_rec = this.site_rec_init; |
||||||
|
if (!site_rec) site_rec = config.agorata_adnl; |
||||||
|
let constructor_site = |
||||||
|
site_rec.toLowerCase() === config.agorata_adnl.toLowerCase(); |
||||||
|
return { |
||||||
|
site_rec, |
||||||
|
constructor_site, |
||||||
|
constructor_params: new SiteConstructorParams(""), |
||||||
|
saved_constructor_params: new SiteConstructorParams(""), |
||||||
|
contacts: [], |
||||||
|
}; |
||||||
|
}, |
||||||
|
watch: { |
||||||
|
site_rec_patched() { |
||||||
|
this.$emit("change", this.site_rec_patched); |
||||||
|
}, |
||||||
|
constructor_params: { |
||||||
|
handler: function (newVal) { |
||||||
|
this.$emit("change-constructor", newVal); |
||||||
|
}, |
||||||
|
deep: true, |
||||||
|
}, |
||||||
|
saved_constructor_params: { |
||||||
|
handler: function (newVal, oldVal) { |
||||||
|
if (newVal === oldVal) return; |
||||||
|
this.constructor_params = newVal.copy(); |
||||||
|
this.contacts = Object.entries(newVal.contacts); |
||||||
|
}, |
||||||
|
deep: true, |
||||||
|
}, |
||||||
|
contacts: { |
||||||
|
handler: function (newVal) { |
||||||
|
this.constructor_params.contacts = new Map(); |
||||||
|
for (let contact of newVal) { |
||||||
|
this.constructor_params.contacts[contact[0]] = contact[1]; |
||||||
|
} |
||||||
|
}, |
||||||
|
deep: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
computed: { |
||||||
|
site_rec_patched() { |
||||||
|
if (this.constructor_site) { |
||||||
|
return config.agorata_adnl; |
||||||
|
} else { |
||||||
|
return this.site_rec; |
||||||
|
} |
||||||
|
}, |
||||||
|
link_types() { |
||||||
|
// return the types from link_types that are not in the constructor_params.contacts |
||||||
|
return link_types.filter( |
||||||
|
(link_type) => !(link_type in this.constructor_params.contacts) |
||||||
|
); |
||||||
|
}, |
||||||
|
link_icons() { |
||||||
|
return link_icons; |
||||||
|
}, |
||||||
|
// used_link_types() { |
||||||
|
// return Object.keys(this.constructor_params.contacts); |
||||||
|
// }, |
||||||
|
}, |
||||||
|
methods: { |
||||||
|
set_site_rec(site_rec) { |
||||||
|
this.site_rec = site_rec; |
||||||
|
}, |
||||||
|
addLink(link_type) { |
||||||
|
console.log("adding link", link_type); |
||||||
|
// If there's already a link of this type, don't add it |
||||||
|
if (link_type in this.constructor_params.contacts) return; |
||||||
|
this.contacts.push([link_type, default_links[link_type]]); |
||||||
|
this.constructor_params.contacts[link_type] = default_links[link_type]; |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
</script> |
||||||
|
|
||||||
|
<style scoped> |
||||||
|
.record-inp { |
||||||
|
padding: 0.3rem; |
||||||
|
margin-left: 0.5rem; |
||||||
|
border-radius: 0.3rem; |
||||||
|
background-color: #4e5a88; |
||||||
|
color: white; |
||||||
|
min-width: 50vw; |
||||||
|
width: 100%; |
||||||
|
} |
||||||
|
|
||||||
|
.rec-field:not(:last-child) { |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.get_b.inactive { |
||||||
|
opacity: 0.5; |
||||||
|
cursor: default; |
||||||
|
} |
||||||
|
|
||||||
|
.get_b.signing { |
||||||
|
/* Center elements inside horizontally, allow rows */ |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
flex-direction: column; |
||||||
|
background-color: white; |
||||||
|
font-size: 1.4rem; |
||||||
|
} |
||||||
|
|
||||||
|
.constructor-form > div > div > div:not(:last-child) { |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
/* The switcher with two buttons - create from template or use custom */ |
||||||
|
.constr-switcher { |
||||||
|
display: flex; |
||||||
|
justify-content: space-between; |
||||||
|
align-items: center; |
||||||
|
margin-bottom: 1rem; |
||||||
|
border-radius: 1.3rem; |
||||||
|
padding: 1rem; |
||||||
|
max-width: 35rem; |
||||||
|
background-color: #4e5a88; |
||||||
|
} |
||||||
|
|
||||||
|
/* The button inside the switcher */ |
||||||
|
.constr-switch { |
||||||
|
padding: 0.5rem 1rem; |
||||||
|
border-radius: 0.7rem; |
||||||
|
background-color: #cdcee8; |
||||||
|
color: #282e46; |
||||||
|
width: 13rem; |
||||||
|
cursor: default; |
||||||
|
} |
||||||
|
|
||||||
|
.constr-switch:not(:last-child) { |
||||||
|
margin-right: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.constr-switch.inactive { |
||||||
|
/*background-color: #5d5f79;*/ |
||||||
|
background-color: #6a6e95; |
||||||
|
color: #cecddb; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.site-record-field { |
||||||
|
font-family: "Inconsolata", monospace; |
||||||
|
} |
||||||
|
|
||||||
|
.link-type:not(:last-child) { |
||||||
|
margin-right: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.link-type-icon.add { |
||||||
|
font-size: 2rem; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.link-type-icon.add:hover { |
||||||
|
color: #e88484; |
||||||
|
} |
||||||
|
|
||||||
|
.link-type-icon { |
||||||
|
font-size: 2.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
.preview { |
||||||
|
border-radius: 1.5rem; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,78 @@ |
|||||||
|
<template> |
||||||
|
<div |
||||||
|
@mouseleave="onMouseLeave" |
||||||
|
@mouseenter="onMouseEnter" |
||||||
|
class="tooltip-container" |
||||||
|
> |
||||||
|
<slot /> |
||||||
|
<div |
||||||
|
v-if="showTooltip" |
||||||
|
:class="{ tooltip: true, top: position === 'top' }" |
||||||
|
:style="width ? `width: ${width}px` : ''" |
||||||
|
> |
||||||
|
<slot name="content" /> |
||||||
|
{{ text }} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script> |
||||||
|
import { ref } from "vue"; |
||||||
|
|
||||||
|
export default { |
||||||
|
name: "Tooltip", |
||||||
|
props: { |
||||||
|
text: { |
||||||
|
type: String, |
||||||
|
required: true, |
||||||
|
}, |
||||||
|
position: { |
||||||
|
type: String, |
||||||
|
}, |
||||||
|
width: { |
||||||
|
type: Number, |
||||||
|
}, |
||||||
|
}, |
||||||
|
setup(props) { |
||||||
|
const showTooltip = ref(false); |
||||||
|
|
||||||
|
function onMouseEnter() { |
||||||
|
showTooltip.value = true; |
||||||
|
} |
||||||
|
|
||||||
|
function onMouseLeave() { |
||||||
|
showTooltip.value = false; |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
showTooltip, |
||||||
|
onMouseEnter, |
||||||
|
onMouseLeave, |
||||||
|
}; |
||||||
|
}, |
||||||
|
}; |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
.tooltip-container { |
||||||
|
position: relative; |
||||||
|
} |
||||||
|
|
||||||
|
.tooltip { |
||||||
|
min-width: 100px; |
||||||
|
font-size: large; |
||||||
|
z-index: 1; |
||||||
|
position: absolute; |
||||||
|
top: 100%; |
||||||
|
left: 50%; |
||||||
|
transform: translateX(-50%); |
||||||
|
background-color: #333; |
||||||
|
color: #fff; |
||||||
|
padding: 0.5rem; |
||||||
|
border-radius: 0.25rem; |
||||||
|
|
||||||
|
&.top { |
||||||
|
transform: translate(-50%, calc(-100% - 24px)); |
||||||
|
} |
||||||
|
} |
||||||
|
</style> |
@ -1,100 +1,112 @@ |
|||||||
import {createApp} from 'vue' |
import { createApp } from "vue"; |
||||||
import App from './App.vue' |
import App from "./App.vue"; |
||||||
import router from './router' |
import router from "./router"; |
||||||
import {createStore} from 'vuex' |
import { createStore } from "vuex"; |
||||||
import TonConnect from '@tonconnect/sdk'; |
import TonConnect from "@tonconnect/sdk"; |
||||||
import {isWalletInfoInjected} from '@tonconnect/sdk'; |
import { isWalletInfoInjected } from "@tonconnect/sdk"; |
||||||
import type {WalletInfo, WalletInfoInjected, WalletInfoRemote} from '@tonconnect/sdk'; |
import type { |
||||||
import './assets/main.css' |
WalletInfo, |
||||||
|
WalletInfoInjected, |
||||||
|
WalletInfoRemote, |
||||||
|
} from "@tonconnect/sdk"; |
||||||
|
import "./assets/main.css"; |
||||||
|
|
||||||
const app = createApp(App) |
const app = createApp(App); |
||||||
|
|
||||||
class State { |
class State { |
||||||
connector: TonConnect; |
connector: TonConnect; |
||||||
walletList: WalletInfo[] = []; |
walletList: WalletInfo[] = []; |
||||||
initialization: Promise<void>; |
initialization: Promise<void>; |
||||||
|
|
||||||
constructor() { |
constructor() { |
||||||
this.connector = new TonConnect({manifestUrl: 'https://front.agorata.io/tonconnect-manifest.json'}); |
this.connector = new TonConnect({ |
||||||
this.initialization = this.initialize(); |
manifestUrl: "https://agorata.io/tonconnect-manifest.json", |
||||||
} |
}); |
||||||
|
this.initialization = this.initialize(); |
||||||
|
} |
||||||
|
|
||||||
async initialize() { |
async initialize() { |
||||||
await this.connector.restoreConnection(); |
await this.connector.restoreConnection(); |
||||||
this.walletList = await this.connector.getWallets(); |
this.walletList = await this.connector.getWallets(); |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
const store = createStore({ |
const store = createStore({ |
||||||
state() { |
state() { |
||||||
return new State() |
return new State(); |
||||||
|
}, |
||||||
|
mutations: { |
||||||
|
set_connector(state, connector) { |
||||||
|
state.connector = connector; |
||||||
}, |
}, |
||||||
mutations: { |
}, |
||||||
set_connector(state, connector) { |
getters: { |
||||||
state.connector = connector |
is_connected(state) { |
||||||
} |
return state.connector.connected; |
||||||
}, |
}, |
||||||
getters: { |
address(state) { |
||||||
is_connected(state) { |
if ( |
||||||
return state.connector.connected; |
state.connector.account !== null && |
||||||
}, |
state.connector.account.address !== undefined |
||||||
address(state) { |
) { |
||||||
if (state.connector.account !== null && state.connector.account.address !== undefined) { |
return state.connector.account.address; |
||||||
return state.connector.account.address; |
} else { |
||||||
} else { |
return ""; |
||||||
return ''; |
} |
||||||
} |
}, |
||||||
} |
connector(state) { |
||||||
|
return state.connector; |
||||||
}, |
}, |
||||||
actions: { |
}, |
||||||
async connect_embedded({state}) { |
actions: { |
||||||
const walletsList = await TonConnect.getWallets(); |
async connect_embedded({ state }) { |
||||||
let connector = state.connector; |
const walletsList = await TonConnect.getWallets(); |
||||||
const embeddedWallet = walletsList.find( |
let connector = state.connector; |
||||||
wallet => isWalletInfoInjected(wallet) && wallet.embedded |
const embeddedWallet = walletsList.find( |
||||||
) as WalletInfoInjected; |
(wallet) => isWalletInfoInjected(wallet) && wallet.embedded |
||||||
console.log(walletsList, embeddedWallet); |
) as WalletInfoInjected; |
||||||
|
console.log(walletsList, embeddedWallet); |
||||||
|
|
||||||
if (embeddedWallet) { |
if (embeddedWallet) { |
||||||
connector.connect({jsBridgeKey: embeddedWallet.jsBridgeKey}); |
connector.connect({ jsBridgeKey: embeddedWallet.jsBridgeKey }); |
||||||
} |
} |
||||||
}, |
}, |
||||||
/* Connect embedded wallet if it exists, otherwise get connection url for a QR code unless we're already connected */ |
/* Connect embedded wallet if it exists, otherwise get connection url for a QR code unless we're already connected */ |
||||||
async get_connection_url({state}): Promise<string | null> { |
async get_connection_url({ state }): Promise<string | null> { |
||||||
await state.initialization; |
await state.initialization; |
||||||
await this.dispatch('connect_embedded'); |
await this.dispatch("connect_embedded"); |
||||||
if (state.connector.connected) { |
if (state.connector.connected) { |
||||||
return null; |
return null; |
||||||
} |
} |
||||||
let walletList = this.state.walletList as WalletInfo[]; |
let walletList = this.state.walletList as WalletInfo[]; |
||||||
if (walletList.length === 0) { |
if (walletList.length === 0) { |
||||||
return null; |
return null; |
||||||
} |
} |
||||||
/* iterate through wallets and do try-catch */ |
/* iterate through wallets and do try-catch */ |
||||||
for (let wallet of walletList) { |
for (let wallet of walletList) { |
||||||
try { |
try { |
||||||
let wallet_r = wallet as WalletInfoRemote; |
let wallet_r = wallet as WalletInfoRemote; |
||||||
let wallet_desc = { |
let wallet_desc = { |
||||||
bridgeUrl: wallet_r.bridgeUrl, |
bridgeUrl: wallet_r.bridgeUrl, |
||||||
universalLink: wallet_r.universalLink |
universalLink: wallet_r.universalLink, |
||||||
} |
}; |
||||||
let res = await state.connector.connect(wallet_desc); |
let res = await state.connector.connect(wallet_desc); |
||||||
if (typeof res === 'string') { |
if (typeof res === "string") { |
||||||
return res; |
return res; |
||||||
} |
} |
||||||
} catch (e) { |
} catch (e) { |
||||||
console.log(wallet, e) |
console.log(wallet, e); |
||||||
} |
|
||||||
} |
|
||||||
return null; |
|
||||||
}, |
|
||||||
async disconnect({state}) { |
|
||||||
await state.connector.disconnect(); |
|
||||||
} |
} |
||||||
} |
} |
||||||
}) |
return null; |
||||||
|
}, |
||||||
|
async disconnect({ state }) { |
||||||
|
await state.connector.disconnect(); |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
app.use(router) |
app.use(router); |
||||||
app.use(store) |
app.use(store); |
||||||
|
|
||||||
app.mount('#app') |
app.mount("#app"); |
||||||
|
@ -0,0 +1,3 @@ |
|||||||
|
export const myDomains = "my-domains"; |
||||||
|
export const mintCollection = "mint-collection"; |
||||||
|
export const addTemplate = "add-template"; |
@ -0,0 +1,24 @@ |
|||||||
|
export type Collection = { |
||||||
|
address: string; |
||||||
|
metadata: { |
||||||
|
description: string; |
||||||
|
image: string; |
||||||
|
name: string; |
||||||
|
}; |
||||||
|
next_item_index: number; |
||||||
|
raw_collection_content: string; |
||||||
|
}; |
||||||
|
|
||||||
|
export type CollectionItem = { |
||||||
|
address: string; |
||||||
|
metadata: { |
||||||
|
name: string; |
||||||
|
description: string; |
||||||
|
image: string; |
||||||
|
}; |
||||||
|
collection: { |
||||||
|
address: string; |
||||||
|
}; |
||||||
|
dns: string; |
||||||
|
previews: { resolution: string; url: string }[]; |
||||||
|
}; |
@ -0,0 +1,134 @@ |
|||||||
|
<template> |
||||||
|
<DarkLayout> |
||||||
|
<form class="form"> |
||||||
|
<div class="input-wrapper"> |
||||||
|
<label for="template-name">Template name</label> |
||||||
|
<input v-model="name" class="input" id="template-name" type="text" /> |
||||||
|
</div> |
||||||
|
<DragDropUploader class="dnd" /> |
||||||
|
<div v-for="(_, i) in fields" class="input-wrapper"> |
||||||
|
<div class="fields-wrapper"> |
||||||
|
<div class="input-wrapper"> |
||||||
|
<label :for="`field-${i}`">Field {{ i + 1 }}</label> |
||||||
|
<input |
||||||
|
placeholder="Name" |
||||||
|
v-model="fields[i].name" |
||||||
|
class="input" |
||||||
|
:id="`field-${i}`" |
||||||
|
type="text" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<input |
||||||
|
placeholder="Value" |
||||||
|
v-model="fields[i].value" |
||||||
|
class="input" |
||||||
|
:id="`field-${i}`" |
||||||
|
type="text" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<button |
||||||
|
class="button button-plus" |
||||||
|
style="justify-self: start" |
||||||
|
type="button" |
||||||
|
@click="fields.push({ name: '', value: '' })" |
||||||
|
> |
||||||
|
+ |
||||||
|
</button> |
||||||
|
<button class="button" type="button">Submit</button> |
||||||
|
</form> |
||||||
|
<template v-slot:header> |
||||||
|
<router-link to="/find"> |
||||||
|
<button class="b darkish back"> |
||||||
|
<img src="@/assets/icons/ton_left.svg" alt="TON" /> |
||||||
|
Back |
||||||
|
</button> |
||||||
|
</router-link> |
||||||
|
</template> |
||||||
|
</DarkLayout> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { ref } from "vue"; |
||||||
|
import DarkLayout from "../components/DarkLayout.vue"; |
||||||
|
import DragDropUploader from "@/components/DnD.vue"; |
||||||
|
|
||||||
|
const name = ref(""); |
||||||
|
|
||||||
|
const fields = ref([{ name: "", value: "" }]); |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
.input-wrapper { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: start; |
||||||
|
} |
||||||
|
|
||||||
|
.input { |
||||||
|
display: block; |
||||||
|
width: 100%; |
||||||
|
min-height: 40px; |
||||||
|
border: 0; |
||||||
|
border-radius: 10px; |
||||||
|
padding: 12px 24px; |
||||||
|
background-color: #4e5a88; |
||||||
|
color: white; |
||||||
|
font-size: 24px; |
||||||
|
font-family: "Raleway", serif; |
||||||
|
} |
||||||
|
|
||||||
|
input::-webkit-outer-spin-button, |
||||||
|
input::-webkit-inner-spin-button { |
||||||
|
-webkit-appearance: none; |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.form { |
||||||
|
display: grid; |
||||||
|
gap: 24px; |
||||||
|
min-width: 600px; |
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
border: 0; |
||||||
|
border-radius: 0.5rem; |
||||||
|
height: 3rem; |
||||||
|
background-color: #e36464; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
place-items: center; |
||||||
|
justify-self: end; |
||||||
|
color: #363e5e; |
||||||
|
font-size: 1.3rem; |
||||||
|
font-weight: 500; |
||||||
|
padding: 0 1rem; |
||||||
|
cursor: pointer; |
||||||
|
} |
||||||
|
|
||||||
|
.button-plus { |
||||||
|
background-color: #4e5a88; |
||||||
|
color: white; |
||||||
|
} |
||||||
|
|
||||||
|
.info { |
||||||
|
width: 600px; |
||||||
|
font-size: 16px; |
||||||
|
text-align: right; |
||||||
|
margin-top: 40px; |
||||||
|
} |
||||||
|
|
||||||
|
.fields-wrapper { |
||||||
|
display: grid; |
||||||
|
grid-template-columns: 1fr 1fr; |
||||||
|
align-items: end; |
||||||
|
gap: 40px; |
||||||
|
} |
||||||
|
|
||||||
|
.dnd { |
||||||
|
width: 600px; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,139 @@ |
|||||||
|
<template> |
||||||
|
<DarkLayout> |
||||||
|
<form class="form"> |
||||||
|
<div class="input-wrapper"> |
||||||
|
<label for="collection-name">Collection name</label> |
||||||
|
<input v-model="name" class="input" id="collection-name" type="text" /> |
||||||
|
</div> |
||||||
|
<div class="input-wrapper"> |
||||||
|
<label for="first-domen-price">Domen price with 3 symbols</label> |
||||||
|
<input |
||||||
|
v-model="firstPrice" |
||||||
|
class="input" |
||||||
|
id="first-domen-price" |
||||||
|
type="number" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
<div class="input-wrapper"> |
||||||
|
<label for="second-domen-price">Domen price with 8 symbols</label> |
||||||
|
<input |
||||||
|
v-model="secondPrice" |
||||||
|
class="input" |
||||||
|
id="second-domen-price" |
||||||
|
type="number" |
||||||
|
/> |
||||||
|
</div> |
||||||
|
<p class="info" v-show="firstPrice && secondPrice && name"> |
||||||
|
By pressing the Mint button, you create an NFT collection with the name |
||||||
|
"{{ name }}" and the price: {{ firstPrice }} ({{ |
||||||
|
secondPrice |
||||||
|
}}).<br />Any users also will be able to buy domains from this |
||||||
|
collection. |
||||||
|
</p> |
||||||
|
<button @click="mint()" class="button" type="button">Mint</button> |
||||||
|
</form> |
||||||
|
<template v-slot:header> |
||||||
|
<router-link to="/find"> |
||||||
|
<button class="b darkish back"> |
||||||
|
<img src="@/assets/icons/ton_left.svg" alt="TON" /> |
||||||
|
Back |
||||||
|
</button> |
||||||
|
</router-link> |
||||||
|
</template> |
||||||
|
</DarkLayout> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script lang="ts" setup> |
||||||
|
import { ref } from "vue"; |
||||||
|
import DarkLayout from "../components/DarkLayout.vue"; |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { call_api } from "@/api"; |
||||||
|
|
||||||
|
const store = useStore(); |
||||||
|
|
||||||
|
const mint = async () => { |
||||||
|
const message = await call_api(`set-record/wallet/${store.getters.address}`); |
||||||
|
|
||||||
|
const d = new Date(); |
||||||
|
const validness = parseInt((d.getTime() / 1000).toFixed(0)) + 360000; |
||||||
|
const transaction = { |
||||||
|
validUntil: validness, |
||||||
|
messages: [ |
||||||
|
{ |
||||||
|
amount: (0.05 * 1000000000).toString(), |
||||||
|
address: store.getters.address, |
||||||
|
payload: message, |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
||||||
|
|
||||||
|
const result = await store.state.connector.sendTransaction(transaction); |
||||||
|
|
||||||
|
console.log(result); |
||||||
|
}; |
||||||
|
|
||||||
|
const name = ref(""); |
||||||
|
const firstPrice = ref(0); |
||||||
|
const secondPrice = ref(0); |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
.input-wrapper { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
align-items: start; |
||||||
|
} |
||||||
|
|
||||||
|
.input { |
||||||
|
display: block; |
||||||
|
width: 100%; |
||||||
|
min-height: 40px; |
||||||
|
border: 0; |
||||||
|
border-radius: 10px; |
||||||
|
padding: 12px 24px; |
||||||
|
background-color: #4e5a88; |
||||||
|
color: white; |
||||||
|
font-size: 24px; |
||||||
|
font-family: "Raleway", serif; |
||||||
|
} |
||||||
|
|
||||||
|
input::-webkit-outer-spin-button, |
||||||
|
input::-webkit-inner-spin-button { |
||||||
|
-webkit-appearance: none; |
||||||
|
margin: 0; |
||||||
|
} |
||||||
|
|
||||||
|
.form { |
||||||
|
display: grid; |
||||||
|
gap: 24px; |
||||||
|
min-width: 600px; |
||||||
|
} |
||||||
|
|
||||||
|
.button { |
||||||
|
border: 0; |
||||||
|
border-radius: 0.5rem; |
||||||
|
height: 3rem; |
||||||
|
background-color: #e36464; |
||||||
|
display: flex; |
||||||
|
justify-content: center; |
||||||
|
align-items: center; |
||||||
|
place-items: center; |
||||||
|
justify-self: end; |
||||||
|
min-width: 150px; |
||||||
|
color: #363e5e; |
||||||
|
font-size: 1.3rem; |
||||||
|
font-weight: 500; |
||||||
|
padding: 0 1rem; |
||||||
|
cursor: pointer; |
||||||
|
margin-left: 1rem; |
||||||
|
margin-top: 1rem; |
||||||
|
margin-bottom: 1rem; |
||||||
|
} |
||||||
|
|
||||||
|
.info { |
||||||
|
width: 600px; |
||||||
|
font-size: 16px; |
||||||
|
text-align: right; |
||||||
|
margin-top: 40px; |
||||||
|
} |
||||||
|
</style> |
@ -0,0 +1,18 @@ |
|||||||
|
<template> |
||||||
|
<DarkLayout> |
||||||
|
<DomainTable /> |
||||||
|
<template v-slot:header> |
||||||
|
<router-link to="/find"> |
||||||
|
<button class="b darkish back"> |
||||||
|
<img src="@/assets/icons/ton_left.svg" alt="TON" /> |
||||||
|
Back |
||||||
|
</button> |
||||||
|
</router-link> |
||||||
|
</template> |
||||||
|
</DarkLayout> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup> |
||||||
|
import DarkLayout from "../components/DarkLayout.vue"; |
||||||
|
import DomainTable from "@/components/DomainTable.vue"; |
||||||
|
</script> |
@ -1,14 +1,13 @@ |
|||||||
import { fileURLToPath, URL } from 'node:url' |
import { fileURLToPath, URL } from "node:url"; |
||||||
|
import { defineConfig } from "vite"; |
||||||
import { defineConfig } from 'vite' |
import vue from "@vitejs/plugin-vue"; |
||||||
import vue from '@vitejs/plugin-vue' |
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({ |
export default defineConfig({ |
||||||
plugins: [vue()], |
plugins: [vue()], |
||||||
resolve: { |
resolve: { |
||||||
alias: { |
alias: { |
||||||
'@': fileURLToPath(new URL('./src', import.meta.url)) |
"@": fileURLToPath(new URL("./src", import.meta.url)), |
||||||
} |
}, |
||||||
} |
}, |
||||||
}) |
}); |
||||||
|
Loading…
Reference in new issue