Compare commits

...

2 Commits

  1. 3
      src/api.ts
  2. 13
      src/assets/main.css
  3. 52
      src/components/DomainBar.vue
  4. 24
      src/components/DomainTable.vue
  5. 124
      src/components/SiteSettings.vue
  6. 6
      src/components/TemplatesList.vue
  7. 17
      src/components/Tooltip.vue
  8. 15
      src/components/ZonePricing.vue
  9. 67
      src/components/ZoneTable.vue
  10. 118
      src/result.ts
  11. 2
      src/router/index.ts
  12. 76
      src/views/Explore.vue
  13. 26
      src/views/Find.vue

3
src/api.ts

@ -12,16 +12,19 @@ export class Api {
public readonly tonscan_url: string; public readonly tonscan_url: string;
public agorata_adnl: string = public agorata_adnl: string =
"ed4f2afebb5e49dda9684a474c5771141be1f7d85a2fa39f1823844dd476c52d"; "ed4f2afebb5e49dda9684a474c5771141be1f7d85a2fa39f1823844dd476c52d";
public readonly tonviewer_url: string;
constructor() { constructor() {
if (process.env.NODE_ENV === "development" && false) { if (process.env.NODE_ENV === "development" && false) {
this.api_url = "http://localhost:5170/"; this.api_url = "http://localhost:5170/";
this.ton_api_url = "https://testnet.tonapi.io/v2/"; this.ton_api_url = "https://testnet.tonapi.io/v2/";
this.tonscan_url = "https://testnet.tonscan.org/"; this.tonscan_url = "https://testnet.tonscan.org/";
this.tonviewer_url = "https://testnet.tonviewer.com/";
} else { } else {
this.api_url = "https://agorata.io/api/"; this.api_url = "https://agorata.io/api/";
this.ton_api_url = "https://tonapi.io/v2/"; this.ton_api_url = "https://tonapi.io/v2/";
this.tonscan_url = "https://tonscan.org/"; this.tonscan_url = "https://tonscan.org/";
this.tonviewer_url = "https://testnet.tonviewer.com/";
} }
} }
} }

13
src/assets/main.css

@ -1,4 +1,4 @@
@import './base.css'; @import "./base.css";
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@ -126,7 +126,7 @@ a,
} }
.mono { .mono {
font-family: 'Inconsolata', monospace; font-family: "Inconsolata", monospace;
} }
:root { :root {
@ -142,7 +142,7 @@ a,
.popper { .popper {
font-size: 0.8rem; font-size: 0.8rem;
font-family: 'Inconsolata', monospace; font-family: "Inconsolata", monospace;
} }
.mobile-scale { .mobile-scale {
@ -160,7 +160,6 @@ a,
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;
} }
@ -177,7 +176,7 @@ a,
} }
.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;
} }
@ -188,8 +187,8 @@ a,
} }
.material-icons.language { .material-icons.language {
position:relative; position: relative;
display:inline-block; display: inline-block;
} }
.material-icons.language:after { .material-icons.language:after {

52
src/components/DomainBar.vue

@ -1,8 +1,18 @@
<template> <template>
<div :class="{'domain-bar': true, 'center': !has_button}"> <div class="title">Find the domain of interest in the zone:</div>
<div :class="{ 'domain-bar': true, center: !has_button }">
<div style="display: flex" class="prompt-cont"> <div style="display: flex" class="prompt-cont">
<contenteditable :contenteditable="editable" tag="div" class="prompt" :no-nl="true" v-model="val" :no-html="true" <contenteditable
ref="domain_field" @returned="search()" spellcheck="false"></contenteditable> :contenteditable="editable"
tag="div"
class="prompt"
:no-nl="true"
v-model="val"
:no-html="true"
ref="domain_field"
@returned="search()"
spellcheck="false"
></contenteditable>
<div class="post-prompt">{{ zone }}</div> <div class="post-prompt">{{ zone }}</div>
</div> </div>
<div class="search" v-if="has_button" @click="search()">Search</div> <div class="search" v-if="has_button" @click="search()">Search</div>
@ -10,33 +20,33 @@
</template> </template>
<script> <script>
import contenteditable from 'vue-contenteditable'; import contenteditable from "vue-contenteditable";
export default { export default {
name: "DomainBar", name: "DomainBar",
components: {contenteditable}, components: { contenteditable },
props: { props: {
zone: { zone: {
type: String, type: String,
default: ".*" default: ".*",
}, },
has_button: { has_button: {
type: Boolean, type: Boolean,
default: true default: true,
}, },
value: { value: {
type: String, type: String,
default: "example" default: "example",
}, },
editable: { editable: {
type: Boolean, type: Boolean,
default: true default: true,
} },
}, },
data() { data() {
return { return {
val: this.value val: this.value,
} };
}, },
mounted() { mounted() {
this.$refs["domain_field"].$el.focus(); this.$refs["domain_field"].$el.focus();
@ -44,18 +54,18 @@ export default {
methods: { methods: {
search() { search() {
this.$emit("search", this.val); this.$emit("search", this.val);
} },
}, },
watch: { watch: {
value: function (val) { value: function (val) {
val = val.replace('\n', ''); val = val.replace("\n", "");
this.val = val; this.val = val;
}, },
val: function (val) { val: function (val) {
this.$emit("input_d", val); this.$emit("input_d", val);
} },
} },
} };
</script> </script>
<style scoped> <style scoped>
@ -87,9 +97,7 @@ export default {
width: 90vw; width: 90vw;
/* allow multiple rows with 0.5 spacing */ /* allow multiple rows with 0.5 spacing */
flex-wrap: wrap; flex-wrap: wrap;
} }
} }
.prompt { .prompt {
@ -131,6 +139,10 @@ export default {
} }
.post-prompt { .post-prompt {
font-family: 'Inconsolata', monospace; font-family: "Inconsolata", monospace;
}
.title {
margin-bottom: 6px;
} }
</style> </style>

24
src/components/DomainTable.vue

@ -85,11 +85,26 @@ onMounted(async () => {
zonesAddresses.value = zones.map(({ address }) => address?.toLowerCase()); zonesAddresses.value = zones.map(({ address }) => address?.toLowerCase());
if (address.value) { if (address.value) {
const { data } = await axios.get<{ nft_items: CollectionItem[] }>( 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` `${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 = data.nft_items items.value = [
...resultTonNfts.data.nft_items,
...resultNfts.data.nft_items
.map((item) => ({ .map((item) => ({
...item, ...item,
formattedCollectionAddress: item.collection?.address formattedCollectionAddress: item.collection?.address
@ -98,7 +113,8 @@ onMounted(async () => {
})) }))
.filter(({ formattedCollectionAddress }) => .filter(({ formattedCollectionAddress }) =>
zonesAddresses.value.includes(formattedCollectionAddress) zonesAddresses.value.includes(formattedCollectionAddress)
); ),
];
} }
}); });
</script> </script>

124
src/components/SiteSettings.vue

@ -1,24 +1,53 @@
<template> <template>
<div class="center"> <div class="center">
<div>Host domain by:</div>
<div class="constr-switcher"> <div class="constr-switcher">
<div <div
@click="constructor_site = true" @click="constructor_site = true"
:class="{ inactive: !constructor_site, 'constr-switch': true }" :class="{ inactive: !constructor_site, 'constr-switch': true }"
> >
By template Selected template
</div> </div>
<div <div
@click="constructor_site = false" @click="constructor_site = false"
:class="{ inactive: constructor_site, 'constr-switch': true }" :class="{ inactive: constructor_site, 'constr-switch': true }"
> >
Host your own Custom server
</div> </div>
</div> </div>
<div class="adnl-label" v-if="!constructor_site">
You can use a custom server with an address in ADNL, TON's networking
layer
<Tooltip position="top" :width="300" class="adnl-tooltip">
<template v-slot:content>
The easiest way to set up an ADNL server is to use
<a href="https://github.com/tonwhales/ton-proxy" target="_blank"
>TON Proxy</a
>
</template>
<svg
width="24px"
height="24px"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6 how-to-get"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z"
/>
</svg>
</Tooltip>
</div>
<div id="site_input" class="rec-field" v-if="!constructor_site"> <div id="site_input" class="rec-field" v-if="!constructor_site">
<div style="display: flex; width: 100%"> <div class="adnl-panel">
<p style="width: 9rem">Site:</p> <p class="label">Site:</p>
<contenteditable <contenteditable
class="record-inp site-record-field" class="record-inp site-record-field adnl-input"
tag="div" tag="div"
:no-hl="true" :no-hl="true"
:no-html="true" :no-html="true"
@ -27,14 +56,13 @@
></contenteditable> ></contenteditable>
<div <div
:class="{ :class="{
'record-submit': true,
get_b: true, get_b: true,
inactive: inactiveSave, inactive: inactiveSave,
signing: signingSite, signing: signingSite,
}" }"
@click="$emit('save')" @click="$emit('save')"
> >
<template v-if="!signingSite">Save</template> <template v-if="!signingSite">Save and host</template>
<template v-else> <template v-else>
<Socket <Socket
secondary-color="#a7aab3" secondary-color="#a7aab3"
@ -50,7 +78,12 @@
<div v-else class="constructor-form"> <div v-else class="constructor-form">
<Switcher <Switcher
:items="templates" :items="templates"
@change="(item) => (activeTemplateName = item.name)" @change="
(item) => {
activeTemplateName = item.name;
selectTemplate(item);
}
"
:active-name="activeTemplateName" :active-name="activeTemplateName"
> >
<template v-slot:suffix> <template v-slot:suffix>
@ -63,14 +96,20 @@
</template> </template>
</Switcher> </Switcher>
<TemplatesList <TemplatesList
:site-changed="siteChanged"
:templates="templates" :templates="templates"
:active-template-name="activeTemplateName" :active-template-name="activeTemplateName"
:site-changed="siteChanged"
:constructor-changed="constructorChanged" :constructor-changed="constructorChanged"
:site_rec_init="site_rec_init" :site_rec_init="site_rec_init"
:signing-site="signingSite" :signing-site="signingSite"
@change="site_rec = $event; $emit('change', site_rec_patched)" @change="
@change-constructor="constructor_params = $event; $emit('change-constructor', constructor_params)" site_rec = $event;
$emit('change', site_rec_patched);
"
@change-constructor="
constructor_params = $event;
$emit('change-constructor', constructor_params);
"
@save="$emit('save', $event)" @save="$emit('save', $event)"
@save-constructor="$emit('save-constructor', $event)" @save-constructor="$emit('save-constructor', $event)"
/> />
@ -86,10 +125,11 @@ import { default_links, SiteConstructorParams } from "../result";
import { link_types, link_icons } from "../result"; import { link_types, link_icons } from "../result";
import TemplatesList from "./TemplatesList.vue"; import TemplatesList from "./TemplatesList.vue";
import Switcher from "./Switcher.vue"; import Switcher from "./Switcher.vue";
import Tooltip from "./Tooltip.vue";
export default { export default {
name: "SiteSettings", name: "SiteSettings",
components: { Socket, contenteditable, TemplatesList, Switcher }, components: { Socket, contenteditable, TemplatesList, Switcher, Tooltip },
props: { props: {
site_rec_init: { site_rec_init: {
default: null, default: null,
@ -106,6 +146,9 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
templateId: {
type: String,
},
}, },
data() { data() {
let site_rec = this.site_rec_init; let site_rec = this.site_rec_init;
@ -152,6 +195,9 @@ export default {
}, },
deep: true, deep: true,
}, },
templateId() {
if (this.templateId) this.activeTemplateName = this.templateId;
},
}, },
computed: { computed: {
site_rec_patched() { site_rec_patched() {
@ -161,10 +207,11 @@ export default {
return this.site_rec; return this.site_rec;
} }
}, },
templateId() {
return this.templateId;
},
inactiveSave() { inactiveSave() {
return ( return !this.siteChanged && this.site_rec !== null;
!this.siteChanged && this.site_rec !== null
);
}, },
link_types() { link_types() {
// return the types from link_types that are not in the constructor_params.contacts // return the types from link_types that are not in the constructor_params.contacts
@ -197,10 +244,13 @@ export default {
name: `Template #${i + 1}`, name: `Template #${i + 1}`,
})); }));
}, },
selectTemplate(template) {
this.$emit("select-template", template);
},
}, },
mounted() { mounted() {
this.setTemplates(); this.setTemplates();
if (this.site_rec === null ) { if (this.site_rec === null) {
this.site_rec = config.agorata_adnl; this.site_rec = config.agorata_adnl;
this.$emit("change", this.site_rec); this.$emit("change", this.site_rec);
} }
@ -211,12 +261,9 @@ export default {
<style scoped> <style scoped>
.record-inp { .record-inp {
padding: 0.3rem; padding: 0.3rem;
margin-left: 0.5rem;
border-radius: 0.3rem; border-radius: 0.3rem;
background-color: #4e5a88; background-color: #4e5a88;
color: white; color: white;
min-width: 50vw;
width: 100%;
} }
.rec-field:not(:last-child) { .rec-field:not(:last-child) {
@ -238,10 +285,6 @@ export default {
font-size: 1.4rem; font-size: 1.4rem;
} }
.get_b {
max-width: 8rem;
}
.constructor-form > div > div:not(:last-child) { .constructor-form > div > div:not(:last-child) {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@ -263,8 +306,8 @@ export default {
border-radius: 0.7rem; border-radius: 0.7rem;
background-color: #cdcee8; background-color: #cdcee8;
color: #282e46; color: #282e46;
width: 13rem;
cursor: default; cursor: default;
width: 250px;
} }
.constr-switch:not(:last-child) { .constr-switch:not(:last-child) {
@ -319,4 +362,37 @@ export default {
margin-top: 1rem; margin-top: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.how-to-get {
cursor: pointer;
}
.adnl-panel {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 10px;
& .label {
display: grid;
justify-content: end;
}
}
.adnl-label {
display: grid;
grid-auto-flow: column;
align-items: center;
gap: 6px;
margin-bottom: 6px;
}
.adnl-tooltip {
display: grid;
align-content: center;
}
.adnl-input {
width: 826px;
}
</style> </style>

6
src/components/TemplatesList.vue

@ -65,7 +65,7 @@
}" }"
@click="$emit('save-constructor')" @click="$emit('save-constructor')"
> >
<template v-if="!signingSite">Save</template> <template v-if="!signingSite">Save and host</template>
<template v-else> <template v-else>
<Socket <Socket
secondary-color="#a7aab3" secondary-color="#a7aab3"
@ -219,10 +219,6 @@ export default {
font-size: 1.4rem; font-size: 1.4rem;
} }
.get_b {
max-width: 8rem;
}
.constructor-form > div > div > div:not(:last-child) { .constructor-form > div > div > div:not(:last-child) {
margin-bottom: 1rem; margin-bottom: 1rem;
} }

17
src/components/Tooltip.vue

@ -5,7 +5,12 @@
class="tooltip-container" class="tooltip-container"
> >
<slot /> <slot />
<div v-if="showTooltip" class="tooltip"> <div
v-if="showTooltip"
:class="{ tooltip: true, top: position === 'top' }"
:style="width ? `width: ${width}px` : ''"
>
<slot name="content" />
{{ text }} {{ text }}
</div> </div>
</div> </div>
@ -21,6 +26,12 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
position: {
type: String,
},
width: {
type: Number,
},
}, },
setup(props) { setup(props) {
const showTooltip = ref(false); const showTooltip = ref(false);
@ -59,5 +70,9 @@ export default {
color: #fff; color: #fff;
padding: 0.5rem; padding: 0.5rem;
border-radius: 0.25rem; border-radius: 0.25rem;
&.top {
transform: translate(-50%, calc(-100% - 24px));
}
} }
</style> </style>

15
src/components/ZonePricing.vue

@ -46,21 +46,22 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>{{ "*".repeat(zone.length_1) + "." + zone.zone }}</td> <td>{{ "*".repeat(zone.length_2) + "." + zone.zone }}</td>
<td v-if="zone.canAuction()"> <td v-if="zone.canAuction()">
<TonButton>{{ zone.price_auction_1 }}</TonButton> <TonButton>{{ zone.price_auction_2 }}</TonButton>
</td> </td>
<td v-if="zone.canBuy()"> <td v-if="zone.canBuy()">
<TonButton>{{ zone.price_buy_1 }}</TonButton> <TonButton>{{ zone.price_buy_2 }}</TonButton>
<small style="display: block">to</small>
</td> </td>
</tr> </tr>
<tr> <tr>
<td>{{ "*".repeat(zone.length_2) + "." + zone.zone }}</td> <td>{{ "*".repeat(zone.length_1) + "." + zone.zone }}</td>
<td v-if="zone.canAuction()"> <td v-if="zone.canAuction()">
<TonButton>{{ zone.price_auction_2 }}</TonButton> <TonButton>{{ zone.price_auction_1 }}</TonButton>
</td> </td>
<td v-if="zone.canBuy()"> <td v-if="zone.canBuy()">
<TonButton>{{ zone.price_buy_2 }}</TonButton> <TonButton>{{ zone.price_buy_1 }}</TonButton>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -122,6 +123,6 @@ tr > th {
} }
.get_b { .get_b {
margin-top: 2.1rem; margin-top: 3.1rem;
} }
</style> </style>

67
src/components/ZoneTable.vue

@ -1,24 +1,51 @@
<template> <template>
<div class="center" v-if="zones === []"> <div class="center" v-if="!zones.length">
<RotateSquare2 style="width: 5rem; height: 5rem; margin-top: 3rem;"/> <RotateSquare2 style="width: 5rem; height: 5rem; margin-top: 3rem" />
</div> </div>
<div style="overflow-x: auto; max-width: 98vw;" v-else> <div style="overflow-x: auto; max-width: 98vw" v-else>
<table class="table_outer"> <table class="table_outer">
<thead class="table_header"> <thead class="table_header">
<tr> <tr>
<th>Domain</th> <th>Zone</th>
<th>Terms</th> <th>
Prices
<Tooltip style="display: inline" text="Depends on length">
<svg
style="padding-top: 4px"
width="24px"
height="24px"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-6 h-6 how-to-get"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z"
/>
</svg>
</Tooltip>
</th>
</tr> </tr>
</thead> </thead>
<tbody class="table_content"> <tbody class="table_content">
<tr v-for="zone in zones" :key="zone.zone"> <tr v-for="zone in zones" :key="zone.zone">
<td class="zone"> <td class="zone">
<router-link :to="{name: 'GetZ', params: {zone: zone.zone}}" style="color: white"> <router-link
:to="{
name: 'GetZ',
params: { zone: zone.zone, domain_init: domain },
}"
style="color: white"
>
{{ zone.zone }} {{ zone.zone }}
</router-link> </router-link>
</td> </td>
<td style="display: flex; justify-content: flex-end"> <td style="display: flex; justify-content: flex-end">
<ZonePricing :zone="zone"/> <ZonePricing :zone="zone" />
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -27,20 +54,24 @@
</template> </template>
<script> <script>
import {Zone} from "../zone"; import { Zone } from "../zone";
import ZonePricing from "./ZonePricing.vue"; import ZonePricing from "./ZonePricing.vue";
import RotateSquare2 from "./RotateSquare2.vue"; import RotateSquare2 from "./RotateSquare2.vue";
import Tooltip from "./Tooltip.vue";
export default { export default {
name: "ZoneTable", name: "ZoneTable",
components: {ZonePricing, RotateSquare2}, components: { ZonePricing, RotateSquare2, Tooltip },
props: { props: {
zones: { zones: {
type: Array[Zone], type: Array[Zone],
default: [] default: [],
}
}, },
} domain: {
type: String,
},
},
};
</script> </script>
<style scoped> <style scoped>
@ -74,7 +105,8 @@ thead > tr {
display: flex; display: flex;
} }
tr > :last-child { /* place it on the right */ tr > :last-child {
/* place it on the right */
justify-content: flex-end; justify-content: flex-end;
text-align: center; text-align: center;
width: 100%; width: 100%;
@ -84,7 +116,8 @@ tr > th:first-child {
text-align: center; text-align: center;
} }
tr > :first-child { /* place it on the left */ tr > :first-child {
/* place it on the left */
text-align: left; text-align: left;
} }
@ -98,4 +131,10 @@ tr:not(:first-child) > td {
padding: 1rem; padding: 1rem;
min-height: 10rem; min-height: 10rem;
} }
.ton-coin-icon {
margin-left: 4px;
width: 15px;
height: 15px;
}
</style> </style>

118
src/result.ts

@ -1,9 +1,9 @@
// import {call_api} from "@/api"; // import {call_api} from "@/api";
import {Zone} from "@/zone"; import { Zone } from "@/zone";
import type {Message} from "@/utils"; import type { Message } from "@/utils";
import type {Collection} from "@/collection"; import type { Collection } from "@/collection";
import {call_api} from "@/api"; import { call_api } from "@/api";
// let ex_collection = () => new Collection("example.ton", "Example collection"); // let ex_collection = () => new Collection("example.ton", "Example collection");
@ -18,7 +18,17 @@ export class Result {
content_msg: Message | null = null; content_msg: Message | null = null;
nft_info?: any; nft_info?: any;
constructor(domain: string, buy_price?: number, auction_price?: number, owner?: string, collection_required: Collection | null = null, condition_fulfilled: boolean | null = null, buy_msg: Message | null = null, content_msg: Message | null = null, nft_info?: any) { constructor(
domain: string,
buy_price?: number,
auction_price?: number,
owner?: string,
collection_required: Collection | null = null,
condition_fulfilled: boolean | null = null,
buy_msg: Message | null = null,
content_msg: Message | null = null,
nft_info?: any
) {
this.domain = domain; this.domain = domain;
this.buy_price = buy_price; this.buy_price = buy_price;
this.auction_price = auction_price; this.auction_price = auction_price;
@ -41,27 +51,45 @@ export class Result {
let content_msg = data.content_msg; let content_msg = data.content_msg;
let nft_info = data.nft_info; let nft_info = data.nft_info;
return new Result(domain, buy_price, auction_price, owner, collection_required, condition_fulfilled, buy_msg, content_msg, nft_info); return new Result(
domain,
buy_price,
auction_price,
owner,
collection_required,
condition_fulfilled,
buy_msg,
content_msg,
nft_info
);
} }
getRouteParams(): any { getRouteParams(): any {
return { return {
domain_init: /* domain up to . */ this.domain.split('.')[0], domain_init: /* domain up to . */ this.domain.split(".")[0],
domain: /* domain up to . */ this.domain.split('.')[0], domain: /* domain up to . */ this.domain.split(".")[0],
zone: /* domain after . */ this.domain.split('.').slice(1).join('.') zone: /* domain after . */ this.domain.split(".").slice(1).join("."),
} };
} }
canAuction(): boolean { canAuction(): boolean {
return this.auction_price !== undefined && this.auction_price !== null && this.owner === undefined; return (
this.auction_price !== undefined &&
this.auction_price !== null &&
this.owner === undefined
);
} }
canBuy(): boolean { canBuy(): boolean {
return this.buy_price !== undefined && this.buy_price !== null && this.owner === undefined; return (
this.buy_price !== undefined &&
this.buy_price !== null &&
this.owner === undefined
);
} }
zone(): string { zone(): string {
return this.domain.split('.').slice(1).join('.'); return this.domain.split(".").slice(1).join(".");
} }
} }
@ -70,7 +98,7 @@ export class Result {
// } // }
export async function get_search_results(query: string) { export async function get_search_results(query: string) {
return Result.fromBackend(await call_api('find/' + query)); return Result.fromBackend(await call_api("find/" + query));
/*await sleep(200); /*await sleep(200);
return [ return [
new Result(query + '.ton', 5, 3), new Result(query + '.ton', 5, 3),
@ -81,7 +109,7 @@ export async function get_search_results(query: string) {
} }
export async function get_domain_result(domain: string, _address?: string) { export async function get_domain_result(domain: string, _address?: string) {
return Result.fromBackend(await call_api('get/' + domain)); return Result.fromBackend(await call_api("get/" + domain));
/*await sleep(100); /*await sleep(100);
if (domain === 'test.ton') { if (domain === 'test.ton') {
return new Result(domain); return new Result(domain);
@ -105,12 +133,26 @@ export async function get_domain_result(domain: string, _address?: string) {
} }
export async function get_records(address: string) { export async function get_records(address: string) {
return await call_api('records/' + address); return await call_api("records/" + address);
} }
export async function get_zones() { export async function get_zones() {
let zones_back = await call_api('zones'); let zones_back = await call_api("zones");
return zones_back.map((zone: any) => new Zone(zone.zone, zone.price_buy_1, zone.price_buy_2, zone.collection_required, zone.price_auction_1, zone.price_auction_2, zone.min_length, zone.length_1, zone.length_2, zone.address)); return zones_back.map(
(zone: any) =>
new Zone(
zone.zone,
zone.price_buy_1,
zone.price_buy_2,
zone.collection_required,
zone.price_auction_1,
zone.price_auction_2,
zone.min_length,
zone.length_1,
zone.length_2,
zone.address
)
);
/*await sleep(10); /*await sleep(10);
return [ return [
new Zone("example.ton", 3, 5, ex_collection()), new Zone("example.ton", 3, 5, ex_collection()),
@ -141,24 +183,42 @@ export class TonLink {
// Get the link for buying a domain // Get the link for buying a domain
export function get_ton_link(res: Result) { export function get_ton_link(res: Result) {
return new TonLink(res.zone(), 'b/' + res.domain, res.buy_price!); return new TonLink(res.zone(), "b/" + res.domain, res.buy_price!);
} }
export let link_types = ['telegram', /*'website',*/ 'getgems', 'email']; export let link_types = ["telegram", /*'website',*/ "getgems", "email"];
export let link_icons = {'telegram': 'fab fa-telegram', 'website': 'material-icons language', 'getgems': 'fas fa-gem', 'email': 'fas fa-envelope'}; export let link_icons = {
export let default_links = {'telegram': 'https://t.me/', 'website': 'https://', 'getgems': 'https://getgems.org/', 'email': 'example@example.org'}; telegram: "fab fa-telegram",
website: "material-icons language",
getgems: "fas fa-gem",
email: "fas fa-envelope",
};
export let default_links = {
telegram: "https://t.me/",
website: "https://",
getgems: "https://getgems.org/",
email: "example@example.org",
};
export class SiteConstructorParams { export class SiteConstructorParams {
domain: string; domain: string;
title: string; title: string;
description: string; description: string;
contacts: Map<string, string> = new Map<string, string>(); contacts: Map<string, string> = new Map<string, string>();
template_id: string;
constructor(domain: string, title: string = '', description: string = '', contacts: Map<string, string> = new Map<string, string>()) {
constructor(
domain: string,
title: string = "",
description: string = "",
contacts: Map<string, string> = new Map<string, string>(),
template_id: string = ""
) {
this.domain = domain; this.domain = domain;
this.title = title; this.title = title;
this.description = description; this.description = description;
this.contacts = contacts; this.contacts = contacts;
this.template_id = template_id;
} }
copy(): SiteConstructorParams { copy(): SiteConstructorParams {
@ -167,6 +227,12 @@ export class SiteConstructorParams {
} }
export async function get_constr_params(domain: string) { export async function get_constr_params(domain: string) {
let res = await call_api('get-site-data/' + domain); let res = await call_api("get-site-data/" + domain);
return new SiteConstructorParams(res.domain, res.title, res.description, res.contacts); return new SiteConstructorParams(
res.domain,
res.title,
res.description,
res.contacts,
res.template_id
);
} }

2
src/router/index.ts

@ -38,7 +38,7 @@ const router = createRouter({
component: () => import("../views/TonDns.vue"), component: () => import("../views/TonDns.vue"),
}, },
{ {
path: "/get/:zone", path: "/get/:zone/:domain_init",
name: "GetZ", name: "GetZ",
component: () => import("../views/Get.vue"), component: () => import("../views/Get.vue"),
props: true, props: true,

76
src/views/Explore.vue

@ -42,9 +42,13 @@
</router-link> </router-link>
</div> </div>
<br /> <br />
<div>
You can associate your domain with your wallet<br />to receive
incoming transaction at the domain address
</div>
<div id="wallet_input" class="rec-field"> <div id="wallet_input" class="rec-field">
<div style="display: flex; width: 100%"> <div class="wallet-panel">
<p style="width: 9rem">Wallet:</p> <p class="label">Wallet:</p>
<contenteditable <contenteditable
class="record-inp wallet-record-field" class="record-inp wallet-record-field"
tag="div" tag="div"
@ -53,6 +57,15 @@
spellcheck="false" spellcheck="false"
v-model="wallet_rec" v-model="wallet_rec"
></contenteditable> ></contenteditable>
<div
:class="`use-my-wallet ${
wallet_rec === $store.getters.address ? 'disabled' : ''
}`"
:disabled="wallet_rec === $store.getters.address"
@click="useMyWallet"
>
Use my wallet
</div>
<div <div
:class="{ :class="{
'record-submit': true, 'record-submit': true,
@ -79,8 +92,10 @@
ref="site_settings" ref="site_settings"
@save="saveSite()" @save="saveSite()"
@save-constructor="saveSiteConstr()" @save-constructor="saveSiteConstr()"
@select-template="selectTemplate"
@change="site_rec = $event" @change="site_rec = $event"
@change-constructor="constructor_params = $event; constructor_params.domain = domain" @change-constructor="constructor_params = $event"
:template-id="constructor_params.template_id"
:site-changed="siteChanged" :site-changed="siteChanged"
:signing-site="signingSite" :signing-site="signingSite"
/> />
@ -116,7 +131,6 @@ import contenteditable from "vue-contenteditable";
import { call_api, call_api_post, config } from "../api"; import { call_api, call_api_post, config } from "../api";
import Socket from "../components/Socket.vue"; import Socket from "../components/Socket.vue";
import SiteSettings from "../components/SiteSettings.vue"; import SiteSettings from "../components/SiteSettings.vue";
import { mintCollection } from "@/router/routes";
export default { export default {
name: "Explore", name: "Explore",
@ -134,7 +148,7 @@ export default {
}, },
}, },
data() { data() {
let saved_constructor_params = new SiteConstructorParams( const saved_constructor_params = new SiteConstructorParams(
this.domain, this.domain,
this.domain this.domain
); );
@ -200,7 +214,10 @@ export default {
}, },
isMine() { isMine() {
console.log(this.result); console.log(this.result);
return this.result.owner === this.$store.getters.address; return (
this.result.owner === this.$store.getters.address ||
this.result.nft_info.owner.address === this.$store.getters.address
);
}, },
walletChanged() { walletChanged() {
return this.records && this.wallet_rec !== this.records.wallet; return this.records && this.wallet_rec !== this.records.wallet;
@ -212,7 +229,10 @@ export default {
this.constructor_params !== this.saved_constructor_params; this.constructor_params !== this.saved_constructor_params;
} }
return ( return (
(this.records && (this.site_rec !== this.records.site || constr_change)) || (!this.records && constr_change) || (this.records !== null && this.site_rec === null) (this.records &&
(this.site_rec !== this.records.site || constr_change)) ||
(!this.records && constr_change) ||
(this.records !== null && this.site_rec === null)
); );
}, },
settingsCompLoaded() { settingsCompLoaded() {
@ -234,7 +254,7 @@ export default {
messages: [ messages: [
{ {
amount: (0.05 * 1000000000).toString(), amount: (0.05 * 1000000000).toString(),
address: this.result.nft_info.address, address: this.wallet_rec ?? this.result.nft_info.address,
payload: message, payload: message,
}, },
], ],
@ -302,6 +322,15 @@ export default {
this.saved_constructor_params; this.saved_constructor_params;
} }
}, },
selectTemplate(template) {
this.constructor_params = {
...this.constructor_params,
template_id: template.name,
};
},
useMyWallet() {
this.wallet_rec = this.$store.getters.address;
},
}, },
watch: { watch: {
records: function (val) { records: function (val) {
@ -368,16 +397,13 @@ export default {
.record-inp { .record-inp {
padding: 0.3rem; padding: 0.3rem;
margin-left: 0.5rem;
border-radius: 0.3rem; border-radius: 0.3rem;
background-color: #4e5a88; background-color: #4e5a88;
color: white; color: white;
min-width: 50vw;
width: 100%;
} }
.rec-field:not(:last-child) { .rec-field:not(:last-child) {
margin-bottom: 1rem; margin-bottom: 4rem;
} }
.get_b.inactive { .get_b.inactive {
@ -423,4 +449,30 @@ export default {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.wallet-panel {
display: grid;
grid-template-columns: auto 1fr 183px auto;
align-items: center;
gap: 10px;
& .label {
display: grid;
justify-content: end;
}
}
.use-my-wallet {
border-radius: 0.5rem;
padding: 0.2rem 0.8rem;
cursor: pointer;
background-color: #cdcee8;
color: #282e46;
&.disabled {
cursor: not-allowed;
background-color: #6a6e95;
color: #cecddb;
}
}
</style> </style>

26
src/views/Find.vue

@ -1,9 +1,9 @@
<template> <template>
<DarkLayout> <DarkLayout>
<div class="center"> <div class="center">
<DomainBar :value="query" @search="search()" @input_d="query = $event"/> <DomainBar :value="query" @search="search()" @input_d="query = $event" />
</div> </div>
<ZoneTable :zones="zones"/> <ZoneTable :domain="query" :zones="zones" />
</DarkLayout> </DarkLayout>
</template> </template>
@ -11,28 +11,24 @@
import DomainBar from "../components/DomainBar.vue"; import DomainBar from "../components/DomainBar.vue";
import DarkLayout from "../components/DarkLayout.vue"; import DarkLayout from "../components/DarkLayout.vue";
import ZoneTable from "../components/ZoneTable.vue"; import ZoneTable from "../components/ZoneTable.vue";
import {get_zones} from "../result"; import { get_zones } from "../result";
export default { export default {
name: "Find", name: "Find",
data () { data() {
return { return {
query: 'example', query: "example",
zones: [] zones: [],
} };
}, },
async mounted() { async mounted() {
this.zones = await get_zones(); this.zones = await get_zones();
}, },
methods: { methods: {
search() { search() {
this.$router.push({name: 'FindQ', params: {query: this.query}}); this.$router.push({ name: "FindQ", params: { query: this.query } });
}
}, },
components: {ZoneTable, DarkLayout, DomainBar} },
} components: { ZoneTable, DarkLayout, DomainBar },
};
</script> </script>
<style scoped>
</style>
Loading…
Cancel
Save