Browse Source

TON Connect is working

vue
Lev 2 years ago
parent
commit
9982c57bf9
  1. 33
      .idea/workspace.xml
  2. 9
      src/components/DarkLayout.vue
  3. 29
      src/components/Header.vue
  4. 83
      src/components/LoginModal.vue
  5. 6
      src/components/ZoneTable.vue
  6. 88
      src/main.ts
  7. 36
      src/utils.ts
  8. 36
      src/views/Checkout.vue
  9. 9
      src/views/Find.vue

33
.idea/workspace.xml

@ -1,20 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="ddb8afd5-d3ba-47b1-b6d0-227403f1abf7" name="Changes" comment="Adaptivity">
<change afterPath="$PROJECT_DIR$/public/tonconnect-manifest.json" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/collection.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/components/LoginModal.vue" afterDir="false" />
<list default="true" id="ddb8afd5-d3ba-47b1-b6d0-227403f1abf7" name="Changes" comment="TON Connect is working">
<change afterPath="$PROJECT_DIR$/src/utils.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/DarkLayout.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/DarkLayout.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/DomainResult.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/DomainResult.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/GetDomainBtn.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/GetDomainBtn.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/Header.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/Header.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/LoginModal.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/LoginModal.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ZoneTable.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ZoneTable.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/result.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/result.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/zone.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/zone.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/views/Checkout.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/views/Checkout.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/views/Find.vue" beforeDir="false" afterPath="$PROJECT_DIR$/src/views/Find.vue" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -24,8 +20,8 @@
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="TypeScript File" />
<option value="Vue Single File Component" />
<option value="TypeScript File" />
</list>
</option>
</component>
@ -90,7 +86,7 @@
<workItem from="1672337938860" duration="148000" />
<workItem from="1672483245701" duration="3321000" />
<workItem from="1672753446569" duration="4197000" />
<workItem from="1672782388693" duration="2160000" />
<workItem from="1672782388693" duration="8939000" />
</task>
<task id="LOCAL-00001" summary="Wrote the landing">
<created>1670844191163</created>
@ -204,7 +200,14 @@
<option name="project" value="LOCAL" />
<updated>1671893611233</updated>
</task>
<option name="localTasksCounter" value="17" />
<task id="LOCAL-00017" summary="Required collections + started implementing login">
<created>1672843430677</created>
<option name="number" value="00017" />
<option name="presentableId" value="LOCAL-00017" />
<option name="project" value="LOCAL" />
<updated>1672843430677</updated>
</task>
<option name="localTasksCounter" value="18" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -238,6 +241,8 @@
<MESSAGE value="Back button + stuff for deploy" />
<MESSAGE value="The screens for domain owners" />
<MESSAGE value="Adaptivity" />
<option name="LAST_COMMIT_MESSAGE" value="Adaptivity" />
<MESSAGE value="Required collections + started implementing login" />
<MESSAGE value="TON Connect is working" />
<option name="LAST_COMMIT_MESSAGE" value="TON Connect is working" />
</component>
</project>

9
src/components/DarkLayout.vue

@ -1,6 +1,6 @@
<template>
<main style="width: 100vw; min-height: 100vh; height: 100%;" id="dark_body">
<Header @login-modal="$refs.loginModal.openModal()">
<Header @login-modal="login()">
<slot name="header"></slot>
</Header>
<div class="center">
@ -18,7 +18,12 @@ import LoginModal from "./LoginModal.vue";
export default {
name: "DarkLayout",
components: {LoginModal, Header}
components: {LoginModal, Header},
methods: {
login() {
this.$refs.loginModal.openModal();
}
}
}
</script>

29
src/components/Header.vue

@ -11,9 +11,14 @@
</div>
<!-- right-aligned login button -->
<div class="login-b">
<button class="b blue wide" @click="$emit('login-modal')">
<button class="b blue wide" @click="$emit('login-modal')" v-if="!$store.getters.is_connected">
<span>Login</span>
</button>
<div v-else>
<div class="address">
<span>{{address}}</span>
</div>
</div>
</div>
</nav>
</div>
@ -22,7 +27,19 @@
<script>
export default {
name: "Header"
name: "Header",
computed: {
address() {
if (!this.$store.getters.is_connected) {
return '';
}
let address = this.$store.getters.address;
if (address === undefined) {
return '';
}
return address.slice(0, 5) + '...' + address.slice(-4);
}
}
}
</script>
@ -51,6 +68,14 @@ nav {
text-align: center;
}
.address {
color: white;
background-color: #4e5a88;
border-radius: 0.5rem;
padding: 0.5rem 1rem;
font-family: 'Inconsolata', monospace;
}
@media (min-width: 1024px) {
header {
display: flex;

83
src/components/LoginModal.vue

@ -1,27 +1,25 @@
<template>
<transition name="fade">
<div class="modal" v-if="show">
<div class="modal" :class="{'hide': !show}">
<div class="modal__backdrop" @click="closeModal()"/>
<div class="modal__dialog">
<div class="modal__header">
<slot name="header"/>
<button type="button" class="modal__close" @click="closeModal()">
<div type="button" class="modal__close" @click="closeModal()">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512">
<path
fill="currentColor"
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
fill="#fff"
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
></path>
</svg>
</button>
</div>
</div>
<div class="modal__body">
<slot name="body"/>
</div>
<div class="modal__footer">
<slot name="footer"/>
<div class="modal__body center">
<div class="qr center">
<div ref="loginqr" class="center qr-in"></div>
</div>
</div>
</div>
</div>
@ -29,11 +27,16 @@
</template>
<script>
import {qr_options} from "../utils";
import QRCodeStyling from 'qr-code-styling';
export default {
name: "LoginModal",
data() {
return {
show: false
show: false,
qr_code: null,
connection_url: null
};
},
methods: {
@ -41,9 +44,32 @@ export default {
this.show = false;
document.querySelector("body").classList.remove("overflow-hidden");
},
openModal() {
async openModal() {
this.connection_url = await this.$store.dispatch("get_connection_url");
console.log(this.connection_url, this.$store.getters.is_connected);
if (this.$store.getters.is_connected) {
return;
}
this.show = true;
document.querySelector("body").classList.add("overflow-hidden");
this.$refs["loginqr"].innerHTML = "";
this.setQR();
this.$store.state.connector.onStatusChange(
wallet => {
console.log(wallet);
this.closeModal()
}
)
},
setQR() {
this.qr_code = new QRCodeStyling(this.qr)
console.log(this.$refs["loginqr"], this.$refs)
this.qr_code.append(this.$refs["loginqr"])
}
},
computed: {
qr() {
return qr_options(this.connection_url, 0.2, 3, 0.2);
}
}
};
@ -60,6 +86,7 @@ export default {
bottom: 0;
left: 0;
z-index: 9;
&__backdrop {
background-color: rgba(0, 0, 0, 0.3);
position: fixed;
@ -69,29 +96,38 @@ export default {
left: 0;
z-index: 1;
}
&__dialog {
background-color: #4e5a88;
position: relative;
width: 600px;
margin: 50px auto;
display: flex;
padding-bottom: 2rem;
flex-direction: column;
border-radius: 5px;
border-radius: 1rem;
z-index: 2;
@media screen and (max-width: 992px) {
width: 90%;
}
}
&__close {
width: 30px;
height: 30px;
cursor: pointer;
//background-color: white;
padding: 7px;
border-radius: 4px;
}
&__header {
padding: 20px 20px 10px;
display: flex;
align-items: flex-start;
justify-content: space-between;
}
&__body {
padding: 10px 20px 10px;
overflow: auto;
@ -99,16 +135,35 @@ export default {
flex-direction: column;
align-items: stretch;
}
&__footer {
padding: 10px 20px 20px;
}
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.hide {
display: none;
}
.qr > div > svg {
border-radius: 2.5rem;
padding: 1rem;
}
.qr-in {
padding: 1rem;
background-color: white;
border-radius: 1.5rem;
}
</style>

6
src/components/ZoneTable.vue

@ -29,7 +29,6 @@
<script>
import {Zone} from "../zone";
import ZonePricing from "./ZonePricing.vue";
import {get_zones} from "../result";
import RotateSquare2 from "./RotateSquare2.vue";
export default {
@ -41,11 +40,6 @@ export default {
default: []
}
},
mounted() {
get_zones().then(zones => {
this.zones = zones;
});
}
}
</script>

88
src/main.ts

@ -1,11 +1,97 @@
import { createApp } from 'vue'
import {createApp} from 'vue'
import App from './App.vue'
import router from './router'
import {createStore} from 'vuex'
import TonConnect from '@tonconnect/sdk';
import {isWalletInfoInjected} from '@tonconnect/sdk';
import type {WalletInfo, WalletInfoInjected, WalletInfoRemote} from '@tonconnect/sdk';
import './assets/main.css'
const app = createApp(App)
class State {
connector: TonConnect;
walletList: WalletInfo[] = [];
initialization: Promise<void>;
constructor() {
this.connector = new TonConnect({manifestUrl: 'https://front.agorata.io/tonconnect-manifest.json'});
this.initialization = this.initialize();
}
async initialize() {
await this.connector.restoreConnection();
this.walletList = await this.connector.getWallets();
}
}
const store = createStore({
state() {
return new State()
},
mutations: {
set_connector(state, connector) {
state.connector = connector
}
},
getters: {
is_connected(state) {
return state.connector.connected;
},
address(state) {
if (state.connector.account !== null && state.connector.account.address !== undefined) {
return state.connector.account.address;
} else {
return '';
}
}
},
actions: {
async connect_embedded({state}) {
const walletsList = await TonConnect.getWallets();
let connector = state.connector;
const embeddedWallet = walletsList.find(
wallet => isWalletInfoInjected(wallet) && wallet.embedded
) as WalletInfoInjected;
if (embeddedWallet) {
connector.connect({jsBridgeKey: embeddedWallet.jsBridgeKey});
}
},
/* 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> {
await state.initialization;
await this.dispatch('connect_embedded');
if (state.connector.connected) {
return null;
}
let walletList = this.state.walletList as WalletInfo[];
if (walletList.length === 0) {
return null;
}
/* iterate through wallets and do try-catch */
for (let wallet of walletList) {
try {
let wallet_r = wallet as WalletInfoRemote;
let wallet_desc = {
bridgeUrl: wallet_r.bridgeUrl,
universalLink: wallet_r.universalLink
}
let res = await state.connector.connect(wallet_desc);
if (typeof res === 'string') {
return res;
}
} catch (e) {
console.log(wallet, e)
}
}
return null;
}
}
})
app.use(router)
app.use(store)
app.mount('#app')

36
src/utils.ts

@ -0,0 +1,36 @@
export function qr_options(url: string, imageSize: number = 0.4, imageMargin: number = 3, margin: number = 10): any {
return {
width: 300,
height: 300,
type: 'svg',
data: url,
image: '/logo_mono.png',
margin: margin,
qrOptions: {
typeNumber: 0,
mode: 'Byte',
errorCorrectionLevel: 'Q'
},
imageOptions: {
hideBackgroundDots: true,
imageSize: imageSize,
margin: imageMargin,
crossOrigin: 'anonymous',
},
dotsOptions: {
color: '#282e46',
type: 'rounded'
},
backgroundOptions: {
color: '#ffffff',
},
cornersSquareOptions: {
color: '#282e46',
type: 'extra-rounded',
},
cornersDotOptions: {
color: '#282e46',
type: 'dot',
}
}
}

36
src/views/Checkout.vue

@ -27,6 +27,7 @@ import DomainBar from "../components/DomainBar.vue";
import {get_domain_result, get_ton_link} from "../result";
import QRCodeStyling from 'qr-code-styling';
import RotateSquare2 from "../components/RotateSquare2.vue";
import {qr_options} from "../utils";
export default {
name: "Checkout",
@ -68,40 +69,7 @@ export default {
if (!this.loaded) {
return null;
}
return {
width: 300,
height: 300,
type: 'svg',
data: this.ton_link.getLink(),
image: '/logo_mono.png',
margin: 10,
qrOptions: {
typeNumber: 0,
mode: 'Byte',
errorCorrectionLevel: 'Q'
},
imageOptions: {
hideBackgroundDots: true,
imageSize: 0.4,
margin: 3,
crossOrigin: 'anonymous',
},
dotsOptions: {
color: '#282e46',
type: 'rounded'
},
backgroundOptions: {
color: '#ffffff',
},
cornersSquareOptions: {
color: '#282e46',
type: 'extra-rounded',
},
cornersDotOptions: {
color: '#282e46',
type: 'dot',
}
};
return qr_options(this.ton_link.getLink());
}
}
}

9
src/views/Find.vue

@ -3,7 +3,7 @@
<div class="center">
<DomainBar :value="query" @search="search()" @input_d="query = $event"/>
</div>
<ZoneTable/>
<ZoneTable :zones="zones"/>
</DarkLayout>
</template>
@ -11,14 +11,19 @@
import DomainBar from "../components/DomainBar.vue";
import DarkLayout from "../components/DarkLayout.vue";
import ZoneTable from "../components/ZoneTable.vue";
import {get_zones} from "../result";
export default {
name: "Find",
data () {
return {
query: 'example'
query: 'example',
zones: []
}
},
async mounted() {
this.zones = await get_zones();
},
methods: {
search() {
this.$router.push({name: 'FindQ', params: {query: this.query}});

Loading…
Cancel
Save