Compare commits
No commits in common. 'master' and 'main' have entirely different histories.
@ -1,341 +1,64 @@
|
||||
<?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="Fixed the contact editor"> |
||||
<list default="true" id="697fb6df-d905-400a-8b28-aa6fb4ab2dd2" name="Changes" comment=""> |
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> |
||||
<change beforePath="$PROJECT_DIR$/public/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/public/index.html" afterDir="false" /> |
||||
<change beforePath="$PROJECT_DIR$/src/App.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.css" afterDir="false" /> |
||||
<change beforePath="$PROJECT_DIR$/src/App.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.tsx" afterDir="false" /> |
||||
</list> |
||||
<option name="SHOW_DIALOG" value="false" /> |
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" /> |
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> |
||||
<option name="LAST_RESOLUTION" value="IGNORE" /> |
||||
</component> |
||||
<component name="FileTemplateManagerImpl"> |
||||
<option name="RECENT_TEMPLATES"> |
||||
<list> |
||||
<option value="TypeScript File" /> |
||||
<option value="Vue Single File Component" /> |
||||
</list> |
||||
</option> |
||||
</component> |
||||
<component name="Git.Settings"> |
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> |
||||
</component> |
||||
<component name="MarkdownSettingsMigration"> |
||||
<option name="stateVersion" value="1" /> |
||||
</component> |
||||
<component name="ProjectId" id="2IoEN3itUjwell4xJk5JfAfTqN5" /> |
||||
<component name="ProjectId" id="2HrsD8siGBzwftkpwuTkBesxHO2" /> |
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" /> |
||||
<component name="ProjectViewState"> |
||||
<option name="hideEmptyMiddlePackages" value="true" /> |
||||
<option name="showLibraryContents" value="true" /> |
||||
</component> |
||||
<component name="PropertiesComponent">{ |
||||
"keyToString": { |
||||
"RunOnceActivity.OpenProjectViewOnStart": "true", |
||||
"RunOnceActivity.ShowReadmeOnStart": "true", |
||||
"WebServerToolWindowFactoryState": "false", |
||||
"last_opened_file_path": "/home/ennucore/dev/agorata/frontend", |
||||
"nodejs_package_manager_path": "npm", |
||||
"settings.editor.selected.configurable": "preferences.pluginManager", |
||||
"ts.external.directory.path": "/home/ennucore/dev/agorata/frontend/node_modules/typescript/lib", |
||||
"vue.rearranger.settings.migration": "true" |
||||
<component name="PropertiesComponent"><![CDATA[{ |
||||
"keyToString": { |
||||
"RunOnceActivity.OpenProjectViewOnStart": "true", |
||||
"RunOnceActivity.ShowReadmeOnStart": "true", |
||||
"WebServerToolWindowFactoryState": "false", |
||||
"last_opened_file_path": "/Users/lionarr/WebstormProjects/agorata/src/assets", |
||||
"nodejs_interpreter_path": "node", |
||||
"nodejs_package_manager_path": "npm", |
||||
"ts.external.directory.path": "/Users/lionarr/WebstormProjects/agorata/node_modules/typescript/lib", |
||||
"vue.rearranger.settings.migration": "true" |
||||
} |
||||
}</component> |
||||
}]]></component> |
||||
<component name="RecentsManager"> |
||||
<key name="MoveFile.RECENT_KEYS"> |
||||
<recent name="$PROJECT_DIR$/src" /> |
||||
<recent name="$PROJECT_DIR$" /> |
||||
<recent name="$PROJECT_DIR$/src/assets/headers" /> |
||||
<recent name="$PROJECT_DIR$/src/assets/images" /> |
||||
<recent name="$PROJECT_DIR$/src/assets/icons" /> |
||||
</key> |
||||
<key name="es6.move.members.recent.items"> |
||||
<recent name="$PROJECT_DIR$/src/utils.ts" /> |
||||
<recent name="$PROJECT_DIR$/src/api.ts" /> |
||||
<key name="CopyFile.RECENT_KEYS"> |
||||
<recent name="$PROJECT_DIR$/src/assets" /> |
||||
</key> |
||||
</component> |
||||
<component name="RunManager"> |
||||
<configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true"> |
||||
<package-json value="$PROJECT_DIR$/package.json" /> |
||||
<command value="run" /> |
||||
<scripts> |
||||
<script value="dev" /> |
||||
</scripts> |
||||
<node-interpreter value="project" /> |
||||
<envs /> |
||||
<configuration name="Debug Application" type="JavascriptDebugType" uri="http://localhost:3000"> |
||||
<method v="2" /> |
||||
</configuration> |
||||
</component> |
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> |
||||
<component name="TaskManager"> |
||||
<task active="true" id="Default" summary="Default task"> |
||||
<changelist id="ddb8afd5-d3ba-47b1-b6d0-227403f1abf7" name="Changes" comment="" /> |
||||
<created>1670839492682</created> |
||||
<changelist id="697fb6df-d905-400a-8b28-aa6fb4ab2dd2" name="Changes" comment="" /> |
||||
<created>1669054415332</created> |
||||
<option name="number" value="Default" /> |
||||
<option name="presentableId" value="Default" /> |
||||
<updated>1670839492682</updated> |
||||
<workItem from="1670839496719" duration="10862000" /> |
||||
<workItem from="1670927344373" duration="29000" /> |
||||
<workItem from="1670927391338" duration="2277000" /> |
||||
<workItem from="1671024025708" duration="19806000" /> |
||||
<workItem from="1671204365793" duration="23468000" /> |
||||
<workItem from="1671490155427" duration="1029000" /> |
||||
<workItem from="1671554049942" duration="6375000" /> |
||||
<workItem from="1671797380442" duration="603000" /> |
||||
<workItem from="1671887059754" duration="6359000" /> |
||||
<workItem from="1672337938860" duration="148000" /> |
||||
<workItem from="1672483245701" duration="3321000" /> |
||||
<workItem from="1672753446569" duration="4197000" /> |
||||
<workItem from="1672782388693" duration="27397000" /> |
||||
<workItem from="1673123515646" duration="82000" /> |
||||
<workItem from="1673195751389" duration="2458000" /> |
||||
<workItem from="1673683299593" duration="12019000" /> |
||||
<workItem from="1673906442760" duration="1896000" /> |
||||
<workItem from="1674161982371" duration="16180000" /> |
||||
<workItem from="1674453019641" duration="3282000" /> |
||||
<workItem from="1674576152071" duration="10420000" /> |
||||
<workItem from="1674812957519" duration="2794000" /> |
||||
<workItem from="1675542984242" duration="370000" /> |
||||
<workItem from="1677945869469" duration="608000" /> |
||||
<workItem from="1678007103478" duration="1012000" /> |
||||
<workItem from="1678136230438" duration="23821000" /> |
||||
<workItem from="1678294988188" duration="3137000" /> |
||||
<workItem from="1678453431365" duration="7802000" /> |
||||
<workItem from="1678966587213" duration="1715000" /> |
||||
<workItem from="1679134080413" duration="6305000" /> |
||||
<workItem from="1679494839005" duration="12422000" /> |
||||
</task> |
||||
<task id="LOCAL-00001" summary="Wrote the landing"> |
||||
<created>1670844191163</created> |
||||
<option name="number" value="00001" /> |
||||
<option name="presentableId" value="LOCAL-00001" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1670844191164</updated> |
||||
</task> |
||||
<task id="LOCAL-00002" summary="TON Web page"> |
||||
<created>1670847897936</created> |
||||
<option name="number" value="00002" /> |
||||
<option name="presentableId" value="LOCAL-00002" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1670847897937</updated> |
||||
</task> |
||||
<task id="LOCAL-00003" summary="Adaptivity + more assets"> |
||||
<created>1671026238851</created> |
||||
<option name="number" value="00003" /> |
||||
<option name="presentableId" value="LOCAL-00003" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671026238851</updated> |
||||
</task> |
||||
<task id="LOCAL-00004" summary="DarkLayout + Domain Bar"> |
||||
<created>1671029892636</created> |
||||
<option name="number" value="00004" /> |
||||
<option name="presentableId" value="LOCAL-00004" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671029892636</updated> |
||||
</task> |
||||
<task id="LOCAL-00005" summary="Started writing ZoneTable + improvements to the search bar"> |
||||
<created>1671100962105</created> |
||||
<option name="number" value="00005" /> |
||||
<option name="presentableId" value="LOCAL-00005" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671100962105</updated> |
||||
</task> |
||||
<task id="LOCAL-00006" summary="ZoneTable with ZonePricing"> |
||||
<created>1671116726928</created> |
||||
<option name="number" value="00006" /> |
||||
<option name="presentableId" value="LOCAL-00006" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671116726928</updated> |
||||
</task> |
||||
<task id="LOCAL-00007" summary="/get/:zone/:domain"> |
||||
<created>1671214435706</created> |
||||
<option name="number" value="00007" /> |
||||
<option name="presentableId" value="LOCAL-00007" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671214435706</updated> |
||||
</task> |
||||
<task id="LOCAL-00008" summary="Changed zone repr + results, api, and Find"> |
||||
<created>1671225120000</created> |
||||
<option name="number" value="00008" /> |
||||
<option name="presentableId" value="LOCAL-00008" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671225120000</updated> |
||||
</task> |
||||
<task id="LOCAL-00009" summary="Displaying results"> |
||||
<created>1671225896559</created> |
||||
<option name="number" value="00009" /> |
||||
<option name="presentableId" value="LOCAL-00009" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671225896559</updated> |
||||
</task> |
||||
<task id="LOCAL-00010" summary="Checkout, Explore"> |
||||
<created>1671227655693</created> |
||||
<option name="number" value="00010" /> |
||||
<option name="presentableId" value="LOCAL-00010" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671227655693</updated> |
||||
</task> |
||||
<task id="LOCAL-00011" summary=""All zones" button + some other stuff"> |
||||
<created>1671240411281</created> |
||||
<option name="number" value="00011" /> |
||||
<option name="presentableId" value="LOCAL-00011" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671240411281</updated> |
||||
</task> |
||||
<task id="LOCAL-00012" summary="Awesome QR at checkout"> |
||||
<created>1671302241009</created> |
||||
<option name="number" value="00012" /> |
||||
<option name="presentableId" value="LOCAL-00012" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671302241009</updated> |
||||
</task> |
||||
<task id="LOCAL-00013" summary="Add (almost) monochrome logo + loading QR on checkout"> |
||||
<created>1671302856594</created> |
||||
<option name="number" value="00013" /> |
||||
<option name="presentableId" value="LOCAL-00013" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671302856594</updated> |
||||
</task> |
||||
<task id="LOCAL-00014" summary="Back button + stuff for deploy"> |
||||
<created>1671398124125</created> |
||||
<option name="number" value="00014" /> |
||||
<option name="presentableId" value="LOCAL-00014" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671398124125</updated> |
||||
<updated>1669054415332</updated> |
||||
<workItem from="1669054416534" duration="4979000" /> |
||||
</task> |
||||
<task id="LOCAL-00015" summary="The screens for domain owners"> |
||||
<created>1671892446204</created> |
||||
<option name="number" value="00015" /> |
||||
<option name="presentableId" value="LOCAL-00015" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671892446204</updated> |
||||
</task> |
||||
<task id="LOCAL-00016" summary="Adaptivity"> |
||||
<created>1671893611233</created> |
||||
<option name="number" value="00016" /> |
||||
<option name="presentableId" value="LOCAL-00016" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1671893611233</updated> |
||||
</task> |
||||
<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> |
||||
<task id="LOCAL-00018" summary="TON Connect is working"> |
||||
<created>1672850074714</created> |
||||
<option name="number" value="00018" /> |
||||
<option name="presentableId" value="LOCAL-00018" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1672850074714</updated> |
||||
</task> |
||||
<task id="LOCAL-00019" summary="Working on the buying process"> |
||||
<created>1672868732315</created> |
||||
<option name="number" value="00019" /> |
||||
<option name="presentableId" value="LOCAL-00019" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1672868732315</updated> |
||||
</task> |
||||
<task id="LOCAL-00020" summary="Checkout"> |
||||
<created>1672913681110</created> |
||||
<option name="number" value="00020" /> |
||||
<option name="presentableId" value="LOCAL-00020" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1672913681110</updated> |
||||
</task> |
||||
<task id="LOCAL-00021" summary="Managing records"> |
||||
<created>1674596285797</created> |
||||
<option name="number" value="00021" /> |
||||
<option name="presentableId" value="LOCAL-00021" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1674596285797</updated> |
||||
</task> |
||||
<task id="LOCAL-00022" summary="SiteSettings"> |
||||
<created>1678286612958</created> |
||||
<option name="number" value="00022" /> |
||||
<option name="presentableId" value="LOCAL-00022" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1678286612958</updated> |
||||
</task> |
||||
<task id="LOCAL-00023" summary="Site constructor"> |
||||
<created>1678291060051</created> |
||||
<option name="number" value="00023" /> |
||||
<option name="presentableId" value="LOCAL-00023" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1678291060051</updated> |
||||
</task> |
||||
<task id="LOCAL-00024" summary="Adding links (contacts)"> |
||||
<created>1678614272869</created> |
||||
<option name="number" value="00024" /> |
||||
<option name="presentableId" value="LOCAL-00024" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1678614272869</updated> |
||||
</task> |
||||
<task id="LOCAL-00025" summary="Fix domain result when it's not available + remove failed icon"> |
||||
<created>1679216484001</created> |
||||
<option name="number" value="00025" /> |
||||
<option name="presentableId" value="LOCAL-00025" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1679216484001</updated> |
||||
</task> |
||||
<task id="LOCAL-00026" summary="Updated the address"> |
||||
<created>1679234696595</created> |
||||
<option name="number" value="00026" /> |
||||
<option name="presentableId" value="LOCAL-00026" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1679234696595</updated> |
||||
</task> |
||||
<task id="LOCAL-00027" summary="Fixed the contact editor"> |
||||
<created>1679584324701</created> |
||||
<option name="number" value="00027" /> |
||||
<option name="presentableId" value="LOCAL-00027" /> |
||||
<option name="project" value="LOCAL" /> |
||||
<updated>1679584324701</updated> |
||||
</task> |
||||
<option name="localTasksCounter" value="28" /> |
||||
<servers /> |
||||
</component> |
||||
<component name="TypeScriptGeneratedFilesManager"> |
||||
<option name="version" value="3" /> |
||||
</component> |
||||
<component name="Vcs.Log.Tabs.Properties"> |
||||
<option name="TAB_STATES"> |
||||
<map> |
||||
<entry key="MAIN"> |
||||
<value> |
||||
<State /> |
||||
</value> |
||||
</entry> |
||||
</map> |
||||
</option> |
||||
</component> |
||||
<component name="VcsManagerConfiguration"> |
||||
<MESSAGE value="Adaptivity + more assets" /> |
||||
<MESSAGE value="DarkLayout + Domain Bar" /> |
||||
<MESSAGE value="Started writing ZoneTable + improvements to the search bar" /> |
||||
<MESSAGE value="ZoneTable with ZonePricing" /> |
||||
<MESSAGE value="/get/:zone/:domain" /> |
||||
<MESSAGE value="Changed zone repr + results, api, and Find" /> |
||||
<MESSAGE value="Displaying results" /> |
||||
<MESSAGE value="Checkout, Explore" /> |
||||
<MESSAGE value=""All zones" button + some other stuff" /> |
||||
<MESSAGE value="Awesome QR at checkout" /> |
||||
<MESSAGE value="Add (almost) monochrome logo + loading QR on checkout" /> |
||||
<MESSAGE value="Back button + stuff for deploy" /> |
||||
<MESSAGE value="The screens for domain owners" /> |
||||
<MESSAGE value="Adaptivity" /> |
||||
<MESSAGE value="Required collections + started implementing login" /> |
||||
<MESSAGE value="TON Connect is working" /> |
||||
<MESSAGE value="Working on the buying process" /> |
||||
<MESSAGE value="Checkout" /> |
||||
<MESSAGE value="Managing records" /> |
||||
<MESSAGE value="SiteSettings" /> |
||||
<MESSAGE value="Site constructor" /> |
||||
<MESSAGE value="Adding links (contacts)" /> |
||||
<MESSAGE value="Fix domain result when it's not available + remove failed icon" /> |
||||
<MESSAGE value="Updated the address" /> |
||||
<MESSAGE value="Fixed the contact editor" /> |
||||
<option name="LAST_COMMIT_MESSAGE" value="Fixed the contact editor" /> |
||||
</component> |
||||
</project> |
@ -1,40 +1,46 @@
|
||||
# agorata |
||||
# Getting Started with Create React App |
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. |
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). |
||||
|
||||
## Recommended IDE Setup |
||||
## Available Scripts |
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). |
||||
In the project directory, you can run: |
||||
|
||||
## Type Support for `.vue` Imports in TS |
||||
### `npm start` |
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. |
||||
Runs the app in the development mode.\ |
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. |
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: |
||||
The page will reload if you make edits.\ |
||||
You will also see any lint errors in the console. |
||||
|
||||
1. Disable the built-in TypeScript Extension |
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette |
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` |
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. |
||||
### `npm test` |
||||
|
||||
## Customize configuration |
||||
Launches the test runner in the interactive watch mode.\ |
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. |
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/). |
||||
### `npm run build` |
||||
|
||||
## Project Setup |
||||
Builds the app for production to the `build` folder.\ |
||||
It correctly bundles React in production mode and optimizes the build for the best performance. |
||||
|
||||
```sh |
||||
npm install |
||||
``` |
||||
The build is minified and the filenames include the hashes.\ |
||||
Your app is ready to be deployed! |
||||
|
||||
### Compile and Hot-Reload for Development |
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. |
||||
|
||||
```sh |
||||
npm run dev |
||||
``` |
||||
### `npm run eject` |
||||
|
||||
### Type-Check, Compile and Minify for Production |
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!** |
||||
|
||||
```sh |
||||
npm run build |
||||
``` |
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. |
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. |
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. |
||||
|
||||
## Learn More |
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). |
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/). |
||||
|
@ -1,30 +0,0 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<link rel="icon" href="/favicon.png"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Raleway"> |
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inconsolata"> |
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"> |
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" /> |
||||
<title>Agorata</title> |
||||
</head> |
||||
<body> |
||||
<div id="app"></div> |
||||
<script type="module" src="/src/main.ts"></script> |
||||
|
||||
</body> |
||||
<style> |
||||
body { |
||||
margin: 0; |
||||
padding: 0; |
||||
font-family: 'Raleway', serif; |
||||
font-size: 1.5rem; |
||||
background: white; |
||||
} |
||||
html { |
||||
padding: 0; |
||||
} |
||||
</style> |
||||
</html> |
@ -1,41 +1,43 @@
|
||||
{ |
||||
"name": "agorata", |
||||
"version": "0.0.0", |
||||
"version": "0.1.0", |
||||
"private": true, |
||||
"dependencies": { |
||||
"@testing-library/jest-dom": "^5.16.5", |
||||
"@testing-library/react": "^13.4.0", |
||||
"@testing-library/user-event": "^13.5.0", |
||||
"@types/jest": "^27.5.2", |
||||
"@types/node": "^16.18.3", |
||||
"@types/react": "^18.0.25", |
||||
"@types/react-dom": "^18.0.9", |
||||
"react": "^18.2.0", |
||||
"react-dom": "^18.2.0", |
||||
"react-scripts": "5.0.1", |
||||
"typescript": "^4.9.3", |
||||
"web-vitals": "^2.1.4" |
||||
}, |
||||
"scripts": { |
||||
"dev": "vite", |
||||
"build": "run-p type-check build-only", |
||||
"preview": "vite preview", |
||||
"build-only": "vite build", |
||||
"type-check": "vue-tsc --noEmit" |
||||
"start": "react-scripts start", |
||||
"build": "react-scripts build", |
||||
"test": "react-scripts test", |
||||
"eject": "react-scripts eject" |
||||
}, |
||||
"dependencies": { |
||||
"@popperjs/core": "^2.11.6", |
||||
"@tonconnect/sdk": "^2.0.4", |
||||
"axios": "^1.2.1", |
||||
"bulma": "^0.9.4", |
||||
"qr-code-styling": "^1.6.0-rc.1", |
||||
"sass": "^1.56.2", |
||||
"semver": "^7.3.8", |
||||
"tonweb": "^0.0.59", |
||||
"vue": "^3.2.45", |
||||
"vue-contenteditable": "^4.1.0", |
||||
"vue-router": "^4.1.6", |
||||
"vue3-popper": "^1.5.0", |
||||
"vuex": "^4.0.2" |
||||
"eslintConfig": { |
||||
"extends": [ |
||||
"react-app", |
||||
"react-app/jest" |
||||
] |
||||
}, |
||||
"devDependencies": { |
||||
"@babel/cli": "^7.20.7", |
||||
"@babel/core": "^7.20.12", |
||||
"@babel/preset-env": "^7.20.2", |
||||
"@types/node": "^18.11.16", |
||||
"@vitejs/plugin-vue": "^4.0.0", |
||||
"@vue/tsconfig": "^0.1.3", |
||||
"buffer": "^6.0.3", |
||||
"npm-run-all": "^4.1.5", |
||||
"tailwindcss": "^3.2.4", |
||||
"typescript": "~4.7.4", |
||||
"vite": "^4.0.0", |
||||
"vue-tsc": "^1.0.12" |
||||
"browserslist": { |
||||
"production": [ |
||||
">0.2%", |
||||
"not dead", |
||||
"not op_mini all" |
||||
], |
||||
"development": [ |
||||
"last 1 chrome version", |
||||
"last 1 firefox version", |
||||
"last 1 safari version" |
||||
] |
||||
} |
||||
} |
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 138 KiB |
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="utf-8" /> |
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
||||
<meta name="theme-color" content="#000000" /> |
||||
<meta |
||||
name="description" |
||||
content="Web site created using create-react-app" |
||||
/> |
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> |
||||
<!-- |
||||
manifest.json provides metadata used when your web app is installed on a |
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ |
||||
--> |
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> |
||||
<!-- |
||||
Notice the use of %PUBLIC_URL% in the tags above. |
||||
It will be replaced with the URL of the `public` folder during the build. |
||||
Only files inside the `public` folder can be referenced from the HTML. |
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will |
||||
work correctly both with client-side routing and a non-root public URL. |
||||
Learn how to configure a non-root public URL by running `npm run build`. |
||||
--> |
||||
<title>React App</title> |
||||
</head> |
||||
<body> |
||||
<noscript>You need to enable JavaScript to run this app.</noscript> |
||||
<div id="root"></div> |
||||
</body> |
||||
</html> |
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 154 KiB |
@ -0,0 +1,25 @@
|
||||
{ |
||||
"short_name": "React App", |
||||
"name": "Create React App Sample", |
||||
"icons": [ |
||||
{ |
||||
"src": "favicon.ico", |
||||
"sizes": "64x64 32x32 24x24 16x16", |
||||
"type": "image/x-icon" |
||||
}, |
||||
{ |
||||
"src": "logo192.png", |
||||
"type": "image/png", |
||||
"sizes": "192x192" |
||||
}, |
||||
{ |
||||
"src": "logo512.png", |
||||
"type": "image/png", |
||||
"sizes": "512x512" |
||||
} |
||||
], |
||||
"start_url": ".", |
||||
"display": "standalone", |
||||
"theme_color": "#000000", |
||||
"background_color": "#ffffff" |
||||
} |
@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html |
||||
User-agent: * |
||||
Disallow: |
@ -1,5 +0,0 @@
|
||||
{ |
||||
"url": "https://tonski-an7.pages.dev", |
||||
"name": "Agorata", |
||||
"iconUrl": "https://front.agorata.io/favicon.png" |
||||
} |
@ -0,0 +1,44 @@
|
||||
.App { |
||||
text-align: center; |
||||
} |
||||
|
||||
.App-logo { |
||||
height: 40vmin; |
||||
pointer-events: none; |
||||
} |
||||
|
||||
@media (prefers-reduced-motion: no-preference) { |
||||
.App-logo { |
||||
animation: App-logo-spin infinite 20s linear; |
||||
} |
||||
} |
||||
|
||||
.App-header { |
||||
background-color: #282c34; |
||||
min-height: 100vh; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
justify-content: center; |
||||
font-size: calc(10px + 2vmin); |
||||
color: white; |
||||
} |
||||
|
||||
.App-link { |
||||
color: #61dafb; |
||||
} |
||||
|
||||
p { |
||||
font-family: "Inter", sans-serif; |
||||
padding: 0; |
||||
margin: 0; |
||||
} |
||||
|
||||
@keyframes App-logo-spin { |
||||
from { |
||||
transform: rotate(0deg); |
||||
} |
||||
to { |
||||
transform: rotate(360deg); |
||||
} |
||||
} |
@ -0,0 +1,9 @@
|
||||
import React from 'react'; |
||||
import { render, screen } from '@testing-library/react'; |
||||
import App from './App'; |
||||
|
||||
test('renders learn react link', () => { |
||||
render(<App />); |
||||
const linkElement = screen.getByText(/learn react/i); |
||||
expect(linkElement).toBeInTheDocument(); |
||||
}); |
@ -0,0 +1,15 @@
|
||||
import React from 'react'; |
||||
import './App.css'; |
||||
import Preview from "./components/Preview"; |
||||
import Header from "./components/Header"; |
||||
|
||||
function App() { |
||||
return ( |
||||
<div className="App"> |
||||
<Header></Header> |
||||
<Preview></Preview> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export default App; |
@ -1,16 +0,0 @@
|
||||
<script setup lang="ts"> |
||||
import { RouterLink, RouterView } from 'vue-router' |
||||
</script> |
||||
|
||||
<template> |
||||
<RouterView /> |
||||
</template> |
||||
|
||||
<style> |
||||
body { |
||||
margin: 0; |
||||
padding: 0; |
||||
font-family: 'Raleway',serif; |
||||
font-size: 1.5rem; |
||||
} |
||||
</style> |
@ -1,71 +0,0 @@
|
||||
import axios from "axios"; |
||||
|
||||
declare var process: { |
||||
env: { |
||||
NODE_ENV: string; |
||||
}; |
||||
}; |
||||
|
||||
export class Api { |
||||
public readonly api_url: string; |
||||
public readonly ton_api_url: string; |
||||
public readonly tonscan_url: string; |
||||
public agorata_adnl: string = |
||||
"ed4f2afebb5e49dda9684a474c5771141be1f7d85a2fa39f1823844dd476c52d"; |
||||
public readonly tonviewer_url: string; |
||||
|
||||
constructor() { |
||||
if (process.env.NODE_ENV === "development") { |
||||
this.api_url = "http://localhost:5170/"; |
||||
this.ton_api_url = "https://testnet.tonapi.io/v2/"; |
||||
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 async function call_api(url: string) { |
||||
return (await axios.get(config.api_url + url)).data; |
||||
} |
||||
|
||||
export async function call_api_post(url: string, data: any) { |
||||
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,74 +0,0 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */ |
||||
:root { |
||||
--vt-c-white: #ffffff; |
||||
--vt-c-white-soft: #f8f8f8; |
||||
--vt-c-white-mute: #f2f2f2; |
||||
|
||||
--vt-c-black: #181818; |
||||
--vt-c-black-soft: #222222; |
||||
--vt-c-black-mute: #282828; |
||||
|
||||
--vt-c-indigo: #2c3e50; |
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29); |
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12); |
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); |
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); |
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo); |
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66); |
||||
--vt-c-text-dark-1: var(--vt-c-white); |
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64); |
||||
} |
||||
|
||||
/* semantic color variables for this project */ |
||||
:root { |
||||
--color-background: var(--vt-c-white); |
||||
--color-background-soft: var(--vt-c-white-soft); |
||||
--color-background-mute: var(--vt-c-white-mute); |
||||
|
||||
--color-border: var(--vt-c-divider-light-2); |
||||
--color-border-hover: var(--vt-c-divider-light-1); |
||||
|
||||
--color-heading: var(--vt-c-text-light-1); |
||||
--color-text: var(--vt-c-text-light-1); |
||||
|
||||
--section-gap: 160px; |
||||
} |
||||
|
||||
@media (prefers-color-scheme: dark) { |
||||
:root { |
||||
--color-background: var(--vt-c-black); |
||||
--color-background-soft: var(--vt-c-black-soft); |
||||
--color-background-mute: var(--vt-c-black-mute); |
||||
|
||||
--color-border: var(--vt-c-divider-dark-2); |
||||
--color-border-hover: var(--vt-c-divider-dark-1); |
||||
|
||||
--color-heading: var(--vt-c-text-dark-1); |
||||
--color-text: var(--vt-c-text-dark-2); |
||||
} |
||||
} |
||||
|
||||
*, |
||||
*::before, |
||||
*::after { |
||||
box-sizing: border-box; |
||||
margin: 0; |
||||
position: relative; |
||||
font-weight: normal; |
||||
} |
||||
|
||||
body { |
||||
min-height: 100vh; |
||||
color: var(--color-text); |
||||
background: var(--color-background); |
||||
transition: color 0.5s, background-color 0.5s; |
||||
line-height: 1.6; |
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, |
||||
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; |
||||
font-size: 15px; |
||||
text-rendering: optimizeLegibility; |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
} |
Before Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 818 KiB |
Before Width: | Height: | Size: 370 KiB |
Before Width: | Height: | Size: 128 KiB |
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 808 KiB |
Before Width: | Height: | Size: 138 KiB |
@ -1,196 +0,0 @@
|
||||
@import "./base.css"; |
||||
|
||||
@tailwind base; |
||||
@tailwind components; |
||||
@tailwind utilities; |
||||
|
||||
#app { |
||||
margin: 0 auto; |
||||
padding: 0; |
||||
min-width: 100vw; |
||||
min-height: 100vh; |
||||
} |
||||
|
||||
a, |
||||
.green { |
||||
text-decoration: none; |
||||
color: hsl(201, 100%, 37%); |
||||
transition: 0.4s; |
||||
} |
||||
|
||||
@media (min-width: 1024px) { |
||||
body { |
||||
display: flex; |
||||
place-items: center; |
||||
} |
||||
|
||||
#app { |
||||
display: grid; |
||||
grid-template-columns: 1fr 1fr; |
||||
padding: 0; |
||||
} |
||||
} |
||||
|
||||
.b { |
||||
border: 2px solid transparent; |
||||
border-radius: 5px; |
||||
padding: 16px 26px; |
||||
text-align: center; |
||||
text-decoration: none; |
||||
display: inline-block; |
||||
font-size: 1.55rem; |
||||
margin: 4px 2px; |
||||
cursor: pointer; |
||||
font-weight: bold; |
||||
} |
||||
|
||||
@media (max-width: 800px) { |
||||
.b { |
||||
font-size: 1.4rem; |
||||
} |
||||
} |
||||
|
||||
.b.white { |
||||
background-color: white; |
||||
color: #282e46; |
||||
} |
||||
|
||||
.b.white:hover { |
||||
background-color: #282e46; |
||||
color: white; |
||||
border: 2px solid white; |
||||
} |
||||
|
||||
.b.blue { |
||||
background-color: #0088cc; |
||||
color: white; |
||||
border-radius: 1rem; |
||||
} |
||||
|
||||
.b > img:first-child { |
||||
max-height: 1.4rem; |
||||
margin-right: 0.7rem; |
||||
} |
||||
|
||||
.b > img:not(:first-child) { |
||||
max-height: 1.4rem; |
||||
margin-left: 2rem; |
||||
} |
||||
|
||||
.wide.b { |
||||
min-width: 16rem; |
||||
margin: 0.5rem; |
||||
} |
||||
|
||||
/* A #edeef1 box with rounded corners with black content color and content centered vertically and horizontally */ |
||||
.rbox { |
||||
background-color: #edeef1; |
||||
border-radius: 2rem; |
||||
padding: 2.5rem 32px; |
||||
text-align: center; |
||||
display: flex; |
||||
align-items: center; |
||||
place-content: center; |
||||
justify-content: center; |
||||
font-size: 1.6rem; |
||||
font-weight: bold; |
||||
color: #282e46; |
||||
min-width: 16rem; |
||||
margin: 0.5rem 3rem; |
||||
} |
||||
|
||||
/* small margin on mobile */ |
||||
@media (max-width: 800px) { |
||||
.rbox { |
||||
margin: 0.5rem 0.5rem; |
||||
} |
||||
} |
||||
|
||||
.rbox > p:not(:first-child) { |
||||
margin-top: 1rem; |
||||
} |
||||
|
||||
.center { |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
place-items: center; |
||||
flex-direction: column; |
||||
} |
||||
|
||||
.b.darkish { |
||||
background-color: #4e5a88; |
||||
color: white; |
||||
border-radius: 0.5rem; |
||||
font-size: 1rem; |
||||
} |
||||
|
||||
.mono { |
||||
font-family: "Inconsolata", monospace; |
||||
} |
||||
|
||||
:root { |
||||
--popper-theme-background-color: #fff; |
||||
--popper-theme-background-color-hover: #fff; |
||||
--popper-theme-text-color: black; |
||||
--popper-theme-border-width: 0px; |
||||
--popper-theme-border-style: solid; |
||||
--popper-theme-border-radius: 6px; |
||||
--popper-theme-padding: 4px; |
||||
--popper-theme-box-shadow: 0 6px 30px -6px rgba(0, 0, 0, 0.25); |
||||
} |
||||
|
||||
.popper { |
||||
font-size: 0.8rem; |
||||
font-family: "Inconsolata", monospace; |
||||
} |
||||
|
||||
.mobile-scale { |
||||
scale: 100%; |
||||
} |
||||
|
||||
@media (max-width: 800px) { |
||||
.mobile-scale { |
||||
scale: 80%; |
||||
} |
||||
} |
||||
|
||||
.get_b { |
||||
background-color: #e36464; |
||||
color: #363e5e; |
||||
border-radius: 0.5rem; |
||||
padding: 0.2rem 0.8rem; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
.get_b { |
||||
margin-left: 0.1rem; |
||||
padding-left: 0.4rem; |
||||
padding-right: 0.4rem; |
||||
} |
||||
} |
||||
|
||||
.flex { |
||||
display: flex; |
||||
} |
||||
|
||||
.b.back { |
||||
font-family: "Inconsolata", monospace; |
||||
font-size: 1.5rem; |
||||
padding: 0.8rem; |
||||
} |
||||
|
||||
.b.back > img { |
||||
max-height: 1rem; |
||||
margin-right: 0.4rem; |
||||
} |
||||
|
||||
.material-icons.language { |
||||
position: relative; |
||||
display: inline-block; |
||||
} |
||||
|
||||
.material-icons.language:after { |
||||
content: "language"; |
||||
} |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 93 KiB |
@ -1,11 +0,0 @@
|
||||
export class Collection { |
||||
address: string; |
||||
name: string; |
||||
logo_url?: string; |
||||
|
||||
constructor(address: string, name: string, logo_url?: string) { |
||||
this.address = address; |
||||
this.name = name; |
||||
this.logo_url = logo_url; |
||||
} |
||||
} |
@ -0,0 +1,9 @@
|
||||
.container { |
||||
padding: 15px 30px; |
||||
background: white; |
||||
filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.2)); |
||||
} |
||||
|
||||
.container p { |
||||
font-size: 32px; |
||||
} |
@ -0,0 +1,18 @@
|
||||
import React from 'react'; |
||||
import style from './button.module.css'; |
||||
|
||||
type TButton = { |
||||
onClick: () => void; |
||||
text: string; |
||||
}; |
||||
|
||||
function Button({text, onClick}: TButton) { |
||||
|
||||
return ( |
||||
<div className={style.container} onClick={onClick}> |
||||
<p>{text}</p> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default Button; |
@ -1,48 +0,0 @@
|
||||
<template> |
||||
<main style="width: 100vw; min-height: 100vh; height: 100%;" id="dark_body"> |
||||
<Header @login-modal="login()" @logout="logout()"> |
||||
<slot name="header"></slot> |
||||
</Header> |
||||
<div class="center"> |
||||
<div class="content center"> |
||||
<slot></slot> |
||||
</div> |
||||
</div> |
||||
<LoginModal ref="loginModal"/> |
||||
</main> |
||||
</template> |
||||
|
||||
<script> |
||||
import Header from "../components/Header.vue"; |
||||
import LoginModal from "./LoginModal.vue"; |
||||
|
||||
export default { |
||||
name: "DarkLayout", |
||||
components: {LoginModal, Header}, |
||||
methods: { |
||||
login() { |
||||
this.$refs.loginModal.openModal(); |
||||
}, |
||||
logout() { |
||||
this.$store.dispatch('disconnect') |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
#dark_body { |
||||
background-color: #282e46; |
||||
} |
||||
|
||||
.content { |
||||
padding: 2.5rem 32px; |
||||
text-align: center; |
||||
display: inline-block; |
||||
font-size: 1.6rem; |
||||
color: white; |
||||
min-height: 50vh; |
||||
min-width: 16rem; |
||||
margin: 0.5rem 3rem; |
||||
} |
||||
</style> |
@ -1,98 +0,0 @@
|
||||
<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> |
@ -1,148 +0,0 @@
|
||||
<template> |
||||
<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"> |
||||
<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> |
||||
<div class="search" v-if="has_button" @click="search()">Search</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import contenteditable from "vue-contenteditable"; |
||||
|
||||
export default { |
||||
name: "DomainBar", |
||||
components: { contenteditable }, |
||||
props: { |
||||
zone: { |
||||
type: String, |
||||
default: ".*", |
||||
}, |
||||
has_button: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
value: { |
||||
type: String, |
||||
default: "example", |
||||
}, |
||||
editable: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
val: this.value, |
||||
}; |
||||
}, |
||||
mounted() { |
||||
this.$refs["domain_field"].$el.focus(); |
||||
}, |
||||
methods: { |
||||
search() { |
||||
this.$emit("search", this.val); |
||||
}, |
||||
}, |
||||
watch: { |
||||
value: function (val) { |
||||
val = val.replace("\n", ""); |
||||
this.val = val; |
||||
}, |
||||
val: function (val) { |
||||
this.$emit("input_d", val); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.domain-bar { |
||||
border-radius: 1rem; |
||||
min-width: 30rem; |
||||
min-height: 5rem; |
||||
padding: 0 1rem; |
||||
background-color: #4e5a88; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
margin-bottom: 1rem; |
||||
} |
||||
|
||||
/* flex container with vertical centering */ |
||||
.prompt-cont { |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
place-items: center; |
||||
margin-top: 1rem; |
||||
margin-bottom: 1rem; |
||||
} |
||||
|
||||
/* the bar is narrower on mobile (90% of the screen) */ |
||||
@media only screen and (max-width: 800px) { |
||||
.domain-bar { |
||||
min-width: revert; |
||||
width: 90vw; |
||||
/* allow multiple rows with 0.5 spacing */ |
||||
flex-wrap: wrap; |
||||
} |
||||
} |
||||
|
||||
.prompt { |
||||
border-radius: 0.5rem; |
||||
min-width: 8rem; |
||||
height: 3rem; |
||||
background-color: #363e5e; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
place-items: center; |
||||
color: white; |
||||
font-size: 1.5rem; |
||||
font-weight: 500; |
||||
padding: 0 1rem; |
||||
outline: none; |
||||
user-select: none; |
||||
cursor: text; |
||||
margin-right: 0.4rem; |
||||
} |
||||
|
||||
/* #e36464 search button with #363e5e text */ |
||||
.search { |
||||
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; |
||||
} |
||||
|
||||
.post-prompt { |
||||
font-family: "Inconsolata", monospace; |
||||
} |
||||
|
||||
.title { |
||||
margin-bottom: 6px; |
||||
} |
||||
</style> |
@ -1,116 +0,0 @@
|
||||
<template> |
||||
<div class="center" v-if="result === null"> |
||||
<RotateSquare2 style="width: 5rem; height: 5rem; margin-top: 3rem;"/> |
||||
</div> |
||||
<GetDomainBtn v-else-if="result.canBuy() && result.condition_fullfilled !== false && result.buy_price > 0" |
||||
:domain="domain" :price="result.buy_price" |
||||
:collection_required="result.collection_required" |
||||
@click="buy()"/> |
||||
<!-- todo: differentiate between auction and buy --> |
||||
<GetDomainBtn v-else-if="result.canAuction() && result.condition_fullfilled !== false" |
||||
:domain="domain" :price="result.auction_price" |
||||
@click="buy()"/> |
||||
<div class="owned" v-else-if="result.canBuy() && result.condition_fullfilled === false" style="cursor: initial;"> |
||||
<p>To buy</p> |
||||
<p><span class="domain">*.{{ zone }}</span></p> |
||||
<p>you need to be a holder of the collection</p> |
||||
<div class="collection">{{ result.collection_required.name }}</div> |
||||
</div> |
||||
<div class="owned" v-else-if="result.canBuy() && result.buy_price === 0" style="cursor: initial;"> |
||||
<p>The domain</p> |
||||
<p class="domain">{{ domain }}</p> |
||||
<p>is unavailable.</p> |
||||
<p>Check the domain length or forbidden characters.</p> |
||||
</div> |
||||
<div v-else class="owned" @click="$router.push({name: 'Explore', params: {domain: domain}})"> |
||||
<p class="domain">{{ domain }}</p> |
||||
<p>is owned.</p> |
||||
<p>Click here to explore or manage</p> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import RotateSquare2 from "./RotateSquare2.vue"; |
||||
import {get_domain_result} from "../result"; |
||||
import GetDomainBtn from "./GetDomainBtn.vue"; |
||||
import {parse_zone} from "../utils"; |
||||
|
||||
export default { |
||||
name: "DomainResult", |
||||
components: {GetDomainBtn, RotateSquare2}, |
||||
props: { |
||||
domain: { |
||||
type: String, |
||||
} |
||||
}, |
||||
data() { |
||||
return {result: null} |
||||
}, |
||||
mounted() { |
||||
this.get_result(); |
||||
}, |
||||
methods: { |
||||
async get_result() { |
||||
this.result = await get_domain_result(this.domain, this.$store.getters.address); |
||||
}, |
||||
buy() { |
||||
if (this.$store.getters.is_connected) { |
||||
this.$router.push({name: 'Checkout', params: this.result.getRouteParams()}) |
||||
// todo |
||||
} else { |
||||
this.$emit('login'); |
||||
} |
||||
} |
||||
}, |
||||
watch: { |
||||
domain: function () { |
||||
this.result = null; |
||||
this.get_result(); |
||||
}, |
||||
is_connected: function () { |
||||
this.result = null; |
||||
this.get_result(); |
||||
} |
||||
}, |
||||
computed: { |
||||
zone() { |
||||
return parse_zone(this.domain); |
||||
}, |
||||
is_connected() { |
||||
return this.$store.getters.is_connected; |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
|
||||
.domain { |
||||
font-family: 'Inconsolata', monospace; |
||||
font-weight: bold; |
||||
font-size: 2.5rem; |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
.domain { |
||||
font-size: 1.7rem; |
||||
} |
||||
} |
||||
|
||||
.owned { |
||||
border-radius: 1rem; |
||||
background-color: #363e5e; |
||||
color: white; |
||||
font-size: 1.5rem; |
||||
text-align: center; |
||||
padding: 2rem; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
|
||||
.collection { |
||||
border-radius: 10px; |
||||
background-color: white; |
||||
color: #0088cc; |
||||
} |
||||
</style> |
@ -1,198 +0,0 @@
|
||||
<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> |
@ -1,91 +0,0 @@
|
||||
<template> |
||||
<div class="get-domain"> |
||||
Get<br /> |
||||
<p class="domain">{{ domain }}</p> |
||||
for<br /> |
||||
<span class="price"> |
||||
{{ price }} |
||||
<img src="@/assets/icons/ton_bottom.svg" class="ton_img" alt="TON" /> |
||||
</span> |
||||
<template |
||||
v-if="collection_required !== undefined && collection_required !== null" |
||||
> |
||||
<br /> |
||||
<div class="collection_required"> |
||||
for owners of |
||||
<div class="collection">{{ collection_required.name }}</div> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: "GetDomainBtn", |
||||
props: { |
||||
domain: { |
||||
type: String, |
||||
required: true, |
||||
}, |
||||
price: { |
||||
type: Number, |
||||
required: true, |
||||
}, |
||||
collection_required: { |
||||
// type: Collection | null, |
||||
default: null, |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
/* #0088cc rounded box with cursor: pointer */ |
||||
.get-domain { |
||||
border-radius: 1rem; |
||||
background-color: #0088cc; |
||||
color: white; |
||||
font-size: 1.5rem; |
||||
text-align: center; |
||||
padding: 2rem; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
.get-domain { |
||||
width: 90vw; |
||||
/* allow multiple rows with 0.5 spacing */ |
||||
flex-wrap: wrap; |
||||
} |
||||
} |
||||
|
||||
.domain { |
||||
font-family: "Inconsolata", monospace; |
||||
font-weight: bold; |
||||
font-size: 2.5rem; |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
.domain { |
||||
font-size: 1.7rem; |
||||
} |
||||
} |
||||
|
||||
.price { |
||||
font-size: 3rem; |
||||
font-weight: bold; |
||||
font-family: "Inconsolata", monospace; |
||||
} |
||||
|
||||
.price > img { |
||||
width: 2rem; |
||||
height: 2rem; |
||||
margin-left: -0.3rem; |
||||
} |
||||
|
||||
.collection { |
||||
border-radius: 10px; |
||||
background-color: white; |
||||
color: #0088cc; |
||||
} |
||||
</style> |
@ -1,177 +0,0 @@
|
||||
<template> |
||||
<header> |
||||
<div class="wrapper flex" style="width: 100%"> |
||||
<router-link to="/"> |
||||
<img class="logo" src="@/assets/logo.png" alt="Agorata" /> |
||||
</router-link> |
||||
<nav style="display: flex"> |
||||
<!-- <router-link to="/">Home</router-link>--> |
||||
<!-- <router-link to="/about">About</router-link>--> |
||||
<!-- <router-link to="/contact">Contact</router-link>--> |
||||
<div> |
||||
<slot></slot> |
||||
</div> |
||||
<!-- right-aligned login button --> |
||||
<div class="login-b" v-if="showLogin"> |
||||
<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> |
||||
<img |
||||
src="@/assets/icons/logout.svg" |
||||
alt="Logout" |
||||
@click="$emit('logout')" |
||||
class="logout-icon" |
||||
/> |
||||
<div class="menu"> |
||||
<router-link to="/my-domains"> |
||||
<span class="menu-item">My domains</span> |
||||
</router-link> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</nav> |
||||
</div> |
||||
</header> |
||||
</template> |
||||
|
||||
<script> |
||||
import { convertAddress } from "../utils.ts"; |
||||
|
||||
export default { |
||||
name: "Header", |
||||
props: { |
||||
showLogin: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
}, |
||||
computed: { |
||||
address() { |
||||
if (!this.$store.getters.is_connected) { |
||||
return ""; |
||||
} |
||||
let address_raw = this.$store.getters.address; |
||||
if (address_raw === undefined) { |
||||
return ""; |
||||
} |
||||
let address = convertAddress(address_raw); |
||||
return address.slice(0, 5) + "..." + address.slice(-4); |
||||
}, |
||||
}, |
||||
async mounted() { |
||||
this.$store.getters.connector.restoreConnection(); |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
header { |
||||
line-height: 1.5; |
||||
max-height: 10rem; |
||||
padding-left: 1rem; |
||||
padding-top: 1rem; |
||||
margin-bottom: 2rem; |
||||
} |
||||
|
||||
div.wrapper { |
||||
flex-wrap: wrap; |
||||
} |
||||
|
||||
.logo { |
||||
display: block; |
||||
margin: 0 auto 2rem; |
||||
width: 17rem; |
||||
} |
||||
|
||||
nav { |
||||
width: 100%; |
||||
font-size: 12px; |
||||
text-align: center; |
||||
} |
||||
|
||||
.menu { |
||||
background-color: #4e5a88; |
||||
position: absolute; |
||||
bottom: -30px; |
||||
right: 0; |
||||
text-align: center; |
||||
left: 0; |
||||
display: none; |
||||
border-bottom-left-radius: 0.5rem; |
||||
border-bottom-right-radius: 0.5rem; |
||||
border-top: 1px solid #363e5e; |
||||
} |
||||
|
||||
.menu-item { |
||||
display: block; |
||||
padding: 4px 16px; |
||||
color: white; |
||||
} |
||||
|
||||
.address { |
||||
color: white; |
||||
background-color: #4e5a88; |
||||
border-radius: 0.5rem; |
||||
padding: 0.5rem 1rem; |
||||
font-family: "Inconsolata", monospace; |
||||
position: relative; |
||||
|
||||
&:hover { |
||||
border-bottom-left-radius: 0; |
||||
border-bottom-right-radius: 0; |
||||
& .menu { |
||||
display: block; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@media (min-width: 1024px) { |
||||
header { |
||||
display: flex; |
||||
place-items: center; |
||||
padding-right: calc(var(--section-gap) / 2); |
||||
} |
||||
|
||||
.logo { |
||||
margin: 0 2rem 0 0; |
||||
} |
||||
|
||||
header .wrapper { |
||||
display: flex; |
||||
place-items: flex-start; |
||||
flex-wrap: initial; |
||||
} |
||||
|
||||
nav { |
||||
text-align: left; |
||||
margin-left: -1rem; |
||||
font-size: 1rem; |
||||
|
||||
padding: 1rem 0; |
||||
} |
||||
} |
||||
|
||||
/* right-aligned login button */ |
||||
.login-b { |
||||
margin-left: auto; |
||||
} |
||||
|
||||
.logout-icon { |
||||
width: 1rem; |
||||
margin-left: 0.5rem; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.address { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
</style> |
@ -0,0 +1,28 @@
|
||||
.container { |
||||
width: 100%; |
||||
height: 100px; |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.logo { |
||||
flex-grow: 1; |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.logo p { |
||||
font-size: 32px; |
||||
font-weight: 600; |
||||
} |
||||
|
||||
.menu { |
||||
flex-grow: 2; |
||||
display: flex; |
||||
list-style: none; |
||||
} |
||||
|
||||
.menu p { |
||||
font-size: 24px; |
||||
font-width: 400; |
||||
} |
@ -0,0 +1,22 @@
|
||||
import React from 'react'; |
||||
import style from './header.module.css'; |
||||
import logo from '../../assets/logo.png'; |
||||
|
||||
function Header() { |
||||
|
||||
return ( |
||||
<div className={style.container}> |
||||
<div className={style.logo}> |
||||
<img src={logo} alt="logotype"/> |
||||
<p>Agorata</p> |
||||
</div> |
||||
<ul className={style.menu}> |
||||
<li><p>how it work?</p></li> |
||||
<li><p>Community</p></li> |
||||
<li><p>Contacts</p></li> |
||||
</ul> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default Header; |
@ -1,179 +0,0 @@
|
||||
<template> |
||||
<transition name="fade"> |
||||
<div class="modal" :class="{'hide': !show}"> |
||||
<div class="modal__backdrop" @click="closeModal()"/> |
||||
|
||||
<div class="modal__dialog"> |
||||
<div class="modal__header"> |
||||
<slot name="header"/> |
||||
<div type="button" class="modal__close" @click="closeModal()"> |
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512"> |
||||
<path |
||||
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> |
||||
</div> |
||||
</div> |
||||
|
||||
<div class="modal__body content center"> |
||||
<div class="text">To log in, scan this:</div> |
||||
<div class="qr center"> |
||||
<div ref="loginqr" class="center qr-in"></div> |
||||
</div> |
||||
<a :href="connection_url" class="b blue wide">Or click here</a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</transition> |
||||
</template> |
||||
|
||||
<script> |
||||
import {qr_options} from "../utils"; |
||||
import QRCodeStyling from 'qr-code-styling'; |
||||
|
||||
export default { |
||||
name: "LoginModal", |
||||
data() { |
||||
return { |
||||
show: false, |
||||
qr_code: null, |
||||
connection_url: null |
||||
}; |
||||
}, |
||||
methods: { |
||||
closeModal() { |
||||
this.show = false; |
||||
document.querySelector("body").classList.remove("overflow-hidden"); |
||||
}, |
||||
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); |
||||
} |
||||
} |
||||
}; |
||||
</script> |
||||
|
||||
|
||||
<style lang="scss" scoped> |
||||
.modal { |
||||
overflow-x: hidden; |
||||
overflow-y: auto; |
||||
position: fixed; |
||||
top: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
left: 0; |
||||
z-index: 9; |
||||
|
||||
&__backdrop { |
||||
background-color: rgba(0, 0, 0, 0.3); |
||||
position: fixed; |
||||
top: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
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: 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; |
||||
display: flex; |
||||
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; |
||||
} |
||||
|
||||
.content { |
||||
padding: 2.5rem 32px; |
||||
text-align: center; |
||||
display: inline-block; |
||||
font-size: 1.6rem; |
||||
color: white; |
||||
} |
||||
</style> |
@ -0,0 +1,28 @@
|
||||
import React from 'react'; |
||||
import style from './preview.module.css'; |
||||
import Button from "../Button"; |
||||
|
||||
function Preview() { |
||||
|
||||
return ( |
||||
<div className={style.container}> |
||||
<h1>The World Of <br/> Web3 Domains</h1> |
||||
<div className={style.buttons}> |
||||
<Button |
||||
text='I have a domain' |
||||
onClick={() => ''} |
||||
></Button> |
||||
<Button |
||||
text='I want a domain' |
||||
onClick={() => ''} |
||||
></Button> |
||||
</div> |
||||
<div className={style.domainButton}> |
||||
<div className={style.question}><p>i</p></div> |
||||
<p>What domain?</p> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default Preview; |
@ -0,0 +1,48 @@
|
||||
.container { |
||||
width: 100vw; |
||||
height: calc(100vh - 100px); |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
flex-direction: column; |
||||
} |
||||
|
||||
.container h1 { |
||||
padding: 0; |
||||
margin: 0; |
||||
font-size: 100px; |
||||
font-family: "Inter", sans-serif; |
||||
text-align: center; |
||||
font-weight: 500; |
||||
} |
||||
|
||||
.buttons { |
||||
display: flex; |
||||
width: 620px; |
||||
justify-content: space-between; |
||||
margin: 50px 0; |
||||
} |
||||
|
||||
.domainButton { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.domainButton p { |
||||
font-size: 24px; |
||||
} |
||||
|
||||
.question { |
||||
margin-right: 10px; |
||||
width: 40px; |
||||
height: 40px; |
||||
border-radius: 50%; |
||||
border: 1px solid black; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
|
||||
.question p { |
||||
font-size: 24px; |
||||
} |
@ -1,88 +0,0 @@
|
||||
<template> |
||||
<div v-bind:style="styles" class="spinner spinner--rotate-square-2"></div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
props: { |
||||
size: { |
||||
default: '40px' |
||||
}, |
||||
background: { |
||||
default: '#0000' |
||||
} |
||||
}, |
||||
computed: { |
||||
styles () { |
||||
return { |
||||
width: this.size, |
||||
height: this.size, |
||||
backgroundColor: this.background |
||||
} |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.spinner { |
||||
position: relative; |
||||
* { |
||||
line-height: 0; |
||||
box-sizing: border-box; |
||||
} |
||||
&:before { |
||||
content: ''; |
||||
width: 100%; |
||||
height: 20%; |
||||
min-width: 5px; |
||||
background: #000; |
||||
opacity: 0.1; |
||||
position: absolute; |
||||
bottom: 0%; |
||||
left: 0; |
||||
border-radius: 50%; |
||||
animation: rotate-square-2-shadow .5s linear infinite; |
||||
} |
||||
&:after { |
||||
content: ''; |
||||
width: 100%; |
||||
height: 100%; |
||||
background: #834c6e; |
||||
animation: rotate-square-2-animate .5s linear infinite; |
||||
position: absolute; |
||||
bottom:40%; |
||||
left: 0; |
||||
border-radius: 3px; |
||||
} |
||||
} |
||||
|
||||
@keyframes rotate-square-2-animate { |
||||
17% { |
||||
border-bottom-right-radius: 3px; |
||||
} |
||||
25% { |
||||
transform: translateY(20%) rotate(22.5deg); |
||||
} |
||||
50% { |
||||
transform: translateY(40%) scale(1, .9) rotate(45deg); |
||||
border-bottom-right-radius: 50%; |
||||
} |
||||
75% { |
||||
transform: translateY(20%) rotate(67.5deg); |
||||
} |
||||
100% { |
||||
transform: translateY(0) rotate(90deg); |
||||
} |
||||
} |
||||
|
||||
|
||||
@keyframes rotate-square-2-shadow { |
||||
0%, 100% { |
||||
transform: scale(1, 1); |
||||
} |
||||
50% { |
||||
transform: scale(1.2, 1); |
||||
} |
||||
} |
||||
</style> |
@ -1,400 +0,0 @@
|
||||
<template> |
||||
<div class="center"> |
||||
<div>Host domain by:</div> |
||||
<div class="constr-switcher"> |
||||
<div |
||||
@click="constructor_site = true" |
||||
:class="{ inactive: !constructor_site, 'constr-switch': true }" |
||||
> |
||||
Selected template |
||||
</div> |
||||
<div |
||||
@click="constructor_site = false" |
||||
:class="{ inactive: constructor_site, 'constr-switch': true }" |
||||
> |
||||
Custom server |
||||
</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 class="adnl-panel"> |
||||
<p class="label">Site:</p> |
||||
<contenteditable |
||||
class="record-inp site-record-field adnl-input" |
||||
tag="div" |
||||
:no-hl="true" |
||||
:no-html="true" |
||||
spellcheck="false" |
||||
v-model="site_rec" |
||||
></contenteditable> |
||||
<div |
||||
:class="{ |
||||
get_b: true, |
||||
inactive: inactiveSave, |
||||
signing: signingSite, |
||||
}" |
||||
@click="$emit('save')" |
||||
> |
||||
<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 v-else class="constructor-form"> |
||||
<Switcher |
||||
:items="templates" |
||||
@change=" |
||||
(item) => { |
||||
activeTemplateName = item.name; |
||||
selectTemplate(item); |
||||
} |
||||
" |
||||
:active-name="activeTemplateName" |
||||
> |
||||
<template v-slot:suffix> |
||||
<router-link |
||||
:to="`/add-template/${$route?.params?.domain}`" |
||||
class="button" |
||||
> |
||||
Add |
||||
</router-link> |
||||
</template> |
||||
</Switcher> |
||||
<TemplatesList |
||||
:site-changed="siteChanged" |
||||
:templates="templates" |
||||
:active-template-name="activeTemplateName" |
||||
:constructor-changed="constructorChanged" |
||||
:site_rec_init="site_rec_init" |
||||
:signing-site="signingSite" |
||||
@change=" |
||||
site_rec = $event; |
||||
$emit('change', site_rec_patched); |
||||
" |
||||
@change-constructor=" |
||||
constructor_params = $event; |
||||
$emit('change-constructor', constructor_params); |
||||
" |
||||
@save="$emit('save', $event)" |
||||
@save-constructor="$emit('save-constructor', $event)" |
||||
/> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import Socket from "./Socket.vue"; |
||||
import contenteditable from "vue-contenteditable"; |
||||
import { config, get_templates } from "../api"; |
||||
import { default_links, SiteConstructorParams } from "../result"; |
||||
import { link_types, link_icons } from "../result"; |
||||
import TemplatesList from "./TemplatesList.vue"; |
||||
import Switcher from "./Switcher.vue"; |
||||
import Tooltip from "./Tooltip.vue"; |
||||
|
||||
export default { |
||||
name: "SiteSettings", |
||||
components: { Socket, contenteditable, TemplatesList, Switcher, Tooltip }, |
||||
props: { |
||||
site_rec_init: { |
||||
default: null, |
||||
}, |
||||
siteChanged: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
constructorChanged: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
signingSite: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
templateId: { |
||||
type: String, |
||||
}, |
||||
}, |
||||
data() { |
||||
let site_rec = this.site_rec_init; |
||||
if (!site_rec) { |
||||
site_rec = config.agorata_adnl; |
||||
this.$emit("change", this.site_rec); |
||||
} |
||||
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: [], |
||||
templates: [], |
||||
activeTemplateName: "Template #1", |
||||
}; |
||||
}, |
||||
watch: { |
||||
site_rec_patched() { |
||||
this.$emit("change", this.site_rec_patched); |
||||
}, |
||||
constructor_params: { |
||||
handler: function (newVal) { |
||||
this.$emit("change-constructor", newVal); |
||||
this.constructor_params.template_id = this.activeTemplateName; |
||||
}, |
||||
deep: true, |
||||
}, |
||||
saved_constructor_params: { |
||||
handler: function (newVal, oldVal) { |
||||
if (newVal === oldVal) return; |
||||
// this.constructor_params.template_id = this.activeTemplateName; |
||||
// this.constructor_params = newVal.copy(); |
||||
this.contacts = newVal.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, |
||||
}, |
||||
templateId() { |
||||
if (this.templateId) this.activeTemplateName = this.templateId; |
||||
}, |
||||
}, |
||||
computed: { |
||||
site_rec_patched() { |
||||
if (this.constructor_site) { |
||||
return config.agorata_adnl; |
||||
} else { |
||||
return this.site_rec; |
||||
} |
||||
}, |
||||
templateId() { |
||||
return this.templateId; |
||||
}, |
||||
inactiveSave() { |
||||
return !this.siteChanged && this.site_rec !== null; |
||||
}, |
||||
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]; |
||||
}, |
||||
async setTemplates() { |
||||
this.templates = await get_templates(); |
||||
this.templates = this.templates.map((template, i) => ({ |
||||
...template, |
||||
name: `Template #${i + 1}`, |
||||
})); |
||||
}, |
||||
selectTemplate(template) { |
||||
this.$emit("select-template", template); |
||||
}, |
||||
}, |
||||
mounted() { |
||||
this.setTemplates(); |
||||
if (this.site_rec === null) { |
||||
this.site_rec = config.agorata_adnl; |
||||
this.$emit("change", this.site_rec); |
||||
} |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.record-inp { |
||||
padding: 0.3rem; |
||||
border-radius: 0.3rem; |
||||
background-color: #4e5a88; |
||||
color: white; |
||||
} |
||||
|
||||
.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: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; |
||||
background-color: #4e5a88; |
||||
} |
||||
|
||||
/* The button inside the switcher */ |
||||
.constr-switch { |
||||
padding: 0.5rem 1rem; |
||||
border-radius: 0.7rem; |
||||
background-color: #cdcee8; |
||||
color: #282e46; |
||||
cursor: default; |
||||
width: 250px; |
||||
} |
||||
|
||||
.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; |
||||
} |
||||
|
||||
.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; |
||||
} |
||||
|
||||
.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> |
@ -1,539 +0,0 @@
|
||||
<template> |
||||
<div v-bind:style="styles" class="spinner spinner--socker"> |
||||
<div v-bind:style="innerStyles" class="spinner-inner"> |
||||
<div class="gel center-gel"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c1 r1"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c2 r1"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c3 r1"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c4 r1"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c5 r1"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c6 r1"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
|
||||
<div class="gel c7 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
|
||||
<div class="gel c8 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c9 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c10 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c11 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c12 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c13 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c14 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c15 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c16 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c17 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c18 r2"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c19 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c20 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c21 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c22 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c23 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c24 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c25 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c26 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c28 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c29 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c30 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c31 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c32 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c33 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c34 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c35 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c36 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
<div class="gel c37 r3"> |
||||
<div class="hex-brick h1"></div> |
||||
<div class="hex-brick h2"></div> |
||||
<div class="hex-brick h3"></div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
props: { |
||||
size: { |
||||
default: '100px' |
||||
}, |
||||
color: { |
||||
default: '#ec6068' // '#834c6e' |
||||
}, |
||||
secondaryColor: { |
||||
default: '#7074f4' // '#286b4b' |
||||
} |
||||
}, |
||||
computed: { |
||||
innerStyles() { |
||||
let size = parseInt(this.size) |
||||
return { |
||||
transform: 'scale(' + (size / 220) + ')', |
||||
'--bg-color': this.color, |
||||
'--secondary-color': this.secondaryColor |
||||
} |
||||
}, |
||||
styles() { |
||||
return { |
||||
width: this.size, |
||||
height: this.size |
||||
} |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.spinner { |
||||
overflow: hidden; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
|
||||
* { |
||||
line-height: 0; |
||||
box-sizing: border-box; |
||||
} |
||||
} |
||||
|
||||
.spinner-inner { |
||||
transform-origin: center center; |
||||
width: 200px; |
||||
height: 200px; |
||||
position: relative; |
||||
} |
||||
|
||||
.hex-brick { |
||||
background: var(--bg-color); |
||||
width: 30px; |
||||
height: 17px; |
||||
position: absolute; |
||||
top: 5px; |
||||
animation-name: socket-fade; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
} |
||||
|
||||
.h2 { |
||||
transform: rotate(60deg); |
||||
} |
||||
|
||||
.h3 { |
||||
transform: rotate(-60deg); |
||||
} |
||||
|
||||
.gel { |
||||
height: 30px; |
||||
width: 30px; |
||||
transition: all .3s; |
||||
position: absolute; |
||||
top: 50%; |
||||
left: 50%; |
||||
} |
||||
|
||||
.center-gel { |
||||
margin-left: -15px; |
||||
margin-top: -15px; |
||||
|
||||
animation-name: socket-pulse; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
} |
||||
|
||||
.c1 { |
||||
margin-left: -47px; |
||||
margin-top: -15px; |
||||
} |
||||
|
||||
.c2 { |
||||
margin-left: -31px; |
||||
margin-top: -43px; |
||||
} |
||||
|
||||
.c3 { |
||||
margin-left: 1px; |
||||
margin-top: -43px; |
||||
} |
||||
|
||||
.c4 { |
||||
margin-left: 17px; |
||||
margin-top: -15px; |
||||
} |
||||
|
||||
.c5 { |
||||
margin-left: -31px; |
||||
margin-top: 13px; |
||||
} |
||||
|
||||
.c6 { |
||||
margin-left: 1px; |
||||
margin-top: 13px; |
||||
} |
||||
|
||||
.c7 { |
||||
margin-left: -63px; |
||||
margin-top: -43px; |
||||
} |
||||
|
||||
.c8 { |
||||
margin-left: 33px; |
||||
margin-top: -43px; |
||||
} |
||||
|
||||
.c9 { |
||||
margin-left: -15px; |
||||
margin-top: 41px; |
||||
} |
||||
|
||||
.c10 { |
||||
margin-left: -63px; |
||||
margin-top: 13px; |
||||
} |
||||
|
||||
.c11 { |
||||
margin-left: 33px; |
||||
margin-top: 13px; |
||||
} |
||||
|
||||
.c12 { |
||||
margin-left: -15px; |
||||
margin-top: -71px; |
||||
} |
||||
|
||||
.c13 { |
||||
margin-left: -47px; |
||||
margin-top: -71px; |
||||
} |
||||
|
||||
.c14 { |
||||
margin-left: 17px; |
||||
margin-top: -71px; |
||||
} |
||||
|
||||
.c15 { |
||||
margin-left: -47px; |
||||
margin-top: 41px; |
||||
} |
||||
|
||||
.c16 { |
||||
margin-left: 17px; |
||||
margin-top: 41px; |
||||
} |
||||
|
||||
.c17 { |
||||
margin-left: -79px; |
||||
margin-top: -15px; |
||||
} |
||||
|
||||
.c18 { |
||||
margin-left: 49px; |
||||
margin-top: -15px; |
||||
} |
||||
|
||||
.c19 { |
||||
margin-left: -63px; |
||||
margin-top: -99px; |
||||
} |
||||
|
||||
.c20 { |
||||
margin-left: 33px; |
||||
margin-top: -99px; |
||||
} |
||||
|
||||
.c21 { |
||||
margin-left: 1px; |
||||
margin-top: -99px; |
||||
} |
||||
|
||||
.c22 { |
||||
margin-left: -31px; |
||||
margin-top: -99px; |
||||
} |
||||
|
||||
.c23 { |
||||
margin-left: -63px; |
||||
margin-top: 69px; |
||||
} |
||||
|
||||
.c24 { |
||||
margin-left: 33px; |
||||
margin-top: 69px; |
||||
} |
||||
|
||||
.c25 { |
||||
margin-left: 1px; |
||||
margin-top: 69px; |
||||
} |
||||
|
||||
.c26 { |
||||
margin-left: -31px; |
||||
margin-top: 69px; |
||||
} |
||||
|
||||
.c27 { |
||||
margin-left: -79px; |
||||
margin-top: -15px; |
||||
} |
||||
|
||||
.c28 { |
||||
margin-left: -95px; |
||||
margin-top: -43px; |
||||
} |
||||
|
||||
.c29 { |
||||
margin-left: -95px; |
||||
margin-top: 13px; |
||||
} |
||||
|
||||
.c30 { |
||||
margin-left: 49px; |
||||
margin-top: 41px; |
||||
} |
||||
|
||||
.c31 { |
||||
margin-left: -79px; |
||||
margin-top: -71px; |
||||
} |
||||
|
||||
.c32 { |
||||
margin-left: -111px; |
||||
margin-top: -15px; |
||||
} |
||||
|
||||
.c33 { |
||||
margin-left: 65px; |
||||
margin-top: -43px; |
||||
} |
||||
|
||||
.c34 { |
||||
margin-left: 65px; |
||||
margin-top: 13px; |
||||
} |
||||
|
||||
.c35 { |
||||
margin-left: -79px; |
||||
margin-top: 41px; |
||||
} |
||||
|
||||
.c36 { |
||||
margin-left: 49px; |
||||
margin-top: -71px; |
||||
} |
||||
|
||||
.c37 { |
||||
margin-left: 81px; |
||||
margin-top: -15px; |
||||
} |
||||
|
||||
.r1 { |
||||
animation-name: socket-pulse; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
animation-delay: .2s; |
||||
} |
||||
|
||||
.r2 { |
||||
animation-name: socket-pulse; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
animation-delay: .4s; |
||||
} |
||||
|
||||
.r3 { |
||||
animation-name: socket-pulse; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
animation-delay: .6s; |
||||
} |
||||
|
||||
.r1 > .hex-brick { |
||||
animation-name: socket-fade; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
animation-delay: .2s; |
||||
} |
||||
|
||||
.r2 > .hex-brick { |
||||
animation-name: socket-fade; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
animation-delay: .4s; |
||||
} |
||||
|
||||
.r3 > .hex-brick { |
||||
animation-name: socket-fade; |
||||
animation-duration: 2s; |
||||
animation-iteration-count: infinite; |
||||
animation-delay: .6s; |
||||
} |
||||
|
||||
|
||||
@keyframes socket-pulse { |
||||
0% { |
||||
transform: scale(1); |
||||
} |
||||
|
||||
50% { |
||||
transform: scale(0.01); |
||||
} |
||||
|
||||
100% { |
||||
transform: scale(1); |
||||
} |
||||
} |
||||
|
||||
@keyframes socket-fade { |
||||
0% { |
||||
background: var(--bg-color); |
||||
} |
||||
|
||||
50% { |
||||
background: var(--secondary-color); |
||||
} |
||||
|
||||
100% { |
||||
background: var(--bg-color); |
||||
} |
||||
} |
||||
</style> |
@ -1,56 +0,0 @@
|
||||
<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> |
@ -1,294 +0,0 @@
|
||||
<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> |
@ -1,37 +0,0 @@
|
||||
<template> |
||||
<button class="b darkish mobile-scale"> |
||||
<span><slot></slot></span> |
||||
<img src="@/assets/icons/ton_bottom.svg" alt="TON" /> |
||||
</button> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
name: "TonButton", |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
button.b.darkish { |
||||
width: 4rem; |
||||
height: 2rem; |
||||
padding: 0.3rem; |
||||
font-family: "Inconsolata", monospace; |
||||
font-weight: bold; |
||||
font-size: 1.15rem; |
||||
cursor: default; |
||||
} |
||||
|
||||
button > img:not(:first-child) { |
||||
width: 0.8rem; |
||||
height: 0.8rem; |
||||
margin-left: 0.4rem; |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
button.b.darkish { |
||||
margin: 0.15rem; |
||||
padding: 0.15rem; |
||||
} |
||||
} |
||||
</style> |
@ -1,78 +0,0 @@
|
||||
<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,68 +0,0 @@
|
||||
<template> |
||||
<div style="width: 100vw"> |
||||
<Header :show-login="false" /> |
||||
<div class="header-logo"> |
||||
<slot name="header"></slot> |
||||
</div> |
||||
<div> |
||||
<div class="rbox" style="min-height: 50vh"> |
||||
<slot name="content"></slot> |
||||
</div> |
||||
</div> |
||||
<div class="buttons columns is-center is-centered"> |
||||
<slot name="buttons"> |
||||
<router-link :to="next"> |
||||
<!-- "Next" button of class "bblue wide" with @/assets/icons/ton_right.svg icon on the right --> |
||||
<button class="b blue wide"> |
||||
<span>{{ nexttext }}</span> |
||||
<img src="@/assets/icons/ton_right.svg" alt="Next" /> |
||||
</button> |
||||
</router-link> |
||||
</slot> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import Header from "../components/Header.vue"; |
||||
|
||||
export default { |
||||
name: "WhiteLayout", |
||||
components: { Header }, |
||||
props: { |
||||
next: { |
||||
type: String, |
||||
}, |
||||
nexttext: { |
||||
type: String, |
||||
default: "Next", |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.header-logo { |
||||
display: block; |
||||
text-align: center; |
||||
justify-content: center; |
||||
height: 4rem; |
||||
margin: 0 auto 1.5rem; |
||||
} |
||||
|
||||
.buttons { |
||||
justify-content: center; |
||||
margin: 1rem 0; |
||||
width: 100%; |
||||
display: flex; |
||||
/* Center the buttons */ |
||||
align-items: center; |
||||
place-items: center; |
||||
/* Center the buttons */ |
||||
} |
||||
|
||||
.rbox { |
||||
min-width: 90%; |
||||
margin: auto 3%; |
||||
} |
||||
</style> |
@ -1,128 +0,0 @@
|
||||
<template> |
||||
<!-- div with vertical centering flex --> |
||||
<div style="display: flex; align-items: center; place-items: center"> |
||||
<!-- |
||||
It's a table: |
||||
| | {icon @/assets/icons/buy.svg} | {icon @/assets/icons/instant_buy.svg} | |
||||
| ---------- | ---------------------------- | ------------------------------------- | |
||||
| {{'*' * zone.length_1 + zone.zone}} | <TonButton>{{zone.price_auction_1}}</TonButton> | <TonButton>{{zone.price_buy_1}}</TonButton> | |
||||
| {{'*' * zone.length_2 + zone.zone}} | <TonButton>{{zone.price_auction_2}}</TonButton> | <TonButton>{{zone.price_buy_2}}</TonButton> | |
||||
--> |
||||
<table> |
||||
<thead> |
||||
<tr> |
||||
<th></th> |
||||
<th v-if="zone.canAuction()"> |
||||
<Popper |
||||
content="Buy on auction" |
||||
placement="left" |
||||
:hover="true" |
||||
:arrow="true" |
||||
class="mobile-scale" |
||||
> |
||||
<img |
||||
src="@/assets/icons/buy.svg" |
||||
class="buy_img" |
||||
alt="Buy on auction" |
||||
/> |
||||
</Popper> |
||||
</th> |
||||
<th v-if="zone.canBuy()"> |
||||
<Popper |
||||
content="Buy instantly" |
||||
placement="right" |
||||
:hover="true" |
||||
:arrow="true" |
||||
class="mobile-scale" |
||||
> |
||||
<img |
||||
src="@/assets/icons/instant_buy.svg" |
||||
class="buy_img" |
||||
alt="Instant Buy" |
||||
/> |
||||
</Popper> |
||||
</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td>{{ "*".repeat(zone.length_2) + "." + zone.zone }}</td> |
||||
<td v-if="zone.canAuction()"> |
||||
<TonButton>{{ zone.price_auction_2 }}</TonButton> |
||||
</td> |
||||
<td v-if="zone.canBuy()"> |
||||
<TonButton>{{ zone.price_buy_2 }}</TonButton> |
||||
<small style="display: block">to</small> |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>{{ "*".repeat(zone.length_1) + "." + zone.zone }}</td> |
||||
<td v-if="zone.canAuction()"> |
||||
<TonButton>{{ zone.price_auction_1 }}</TonButton> |
||||
</td> |
||||
<td v-if="zone.canBuy()"> |
||||
<TonButton>{{ zone.price_buy_1 }}</TonButton> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
<router-link |
||||
:to="{ name: 'Get', params: { domain_init: 'example', zone: zone.zone } }" |
||||
> |
||||
<div v-if="has_get" class="get_b mobile-scale">Get</div> |
||||
</router-link> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { Zone } from "../zone"; |
||||
import Popper from "vue3-popper"; |
||||
import TonButton from "./TonButton.vue"; |
||||
|
||||
export default { |
||||
name: "ZonePricing", |
||||
components: { TonButton, Popper }, |
||||
props: { |
||||
zone: { |
||||
type: Zone, |
||||
default: new Zone("example.ton", 3, 5), |
||||
}, |
||||
// Whether to show the "Get" button |
||||
has_get: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
tr > td:first-child { |
||||
font-family: "Inconsolata", monospace; |
||||
padding-right: 1rem; |
||||
padding-left: 3rem; |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
tr > td:first-child { |
||||
font-size: 0.9rem; |
||||
padding: 0; |
||||
} |
||||
|
||||
tr > td { |
||||
padding: 0; |
||||
} |
||||
} |
||||
|
||||
tr > th { |
||||
padding-bottom: -10px; |
||||
} |
||||
|
||||
.buy_img { |
||||
margin-bottom: -15px; |
||||
} |
||||
|
||||
.get_b { |
||||
margin-top: 3.1rem; |
||||
} |
||||
</style> |
@ -1,143 +0,0 @@
|
||||
<template> |
||||
<div class="center" v-if="!zones.length"> |
||||
<RotateSquare2 style="width: 5rem; height: 5rem; margin-top: 3rem" /> |
||||
</div> |
||||
<div style="overflow-x: auto; max-width: 98vw" v-else> |
||||
<table class="table_outer"> |
||||
<thead class="table_header"> |
||||
<tr> |
||||
<th>Zone</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> |
||||
</thead> |
||||
<tbody class="table_content"> |
||||
<tr v-for="zone in zones" :key="zone.zone"> |
||||
<td v-if="zone.zone !== 'ton'" class="zone"> |
||||
<router-link |
||||
:to="{ |
||||
name: 'GetZ', |
||||
params: { zone: zone.zone, domain_init: domain }, |
||||
}" |
||||
style="color: white" |
||||
> |
||||
{{ zone.zone }} |
||||
</router-link> |
||||
</td> |
||||
<td |
||||
v-if="zone.zone !== 'ton'" |
||||
style="display: flex; justify-content: flex-end" |
||||
> |
||||
<ZonePricing :zone="zone" /> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { Zone } from "../zone"; |
||||
import ZonePricing from "./ZonePricing.vue"; |
||||
import RotateSquare2 from "./RotateSquare2.vue"; |
||||
import Tooltip from "./Tooltip.vue"; |
||||
|
||||
export default { |
||||
name: "ZoneTable", |
||||
components: { ZonePricing, RotateSquare2, Tooltip }, |
||||
props: { |
||||
zones: { |
||||
type: Array[Zone], |
||||
default: [], |
||||
}, |
||||
domain: { |
||||
type: String, |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
/* |
||||
We want a #4e5a88 rounded box with a #363e5e box inside - so that only the header is #363e5e |
||||
|
||||
The outer rounded box is table_outer |
||||
The inner box is table_content, it takes all of the space except for the header |
||||
*/ |
||||
.table_outer { |
||||
border-radius: 1rem; |
||||
min-height: 5rem; |
||||
background-color: #4e5a88; |
||||
display: flex; |
||||
justify-content: space-between; |
||||
flex-direction: column; |
||||
min-width: max(30rem, 50vw); |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
td.zone { |
||||
font-size: 1.1rem; |
||||
} |
||||
} |
||||
|
||||
.table_header { |
||||
padding: 0.5rem 1.3rem; |
||||
} |
||||
|
||||
thead > tr { |
||||
display: flex; |
||||
} |
||||
|
||||
tr > :last-child { |
||||
/* place it on the right */ |
||||
justify-content: flex-end; |
||||
text-align: center; |
||||
width: 100%; |
||||
} |
||||
|
||||
tr > th:first-child { |
||||
text-align: center; |
||||
} |
||||
|
||||
tr > :first-child { |
||||
/* place it on the left */ |
||||
text-align: left; |
||||
} |
||||
|
||||
tr:not(:first-child) > td { |
||||
border-top: 2px solid white; |
||||
} |
||||
|
||||
.table_content { |
||||
background-color: #363e5e; |
||||
border-radius: 1rem; |
||||
padding: 1rem; |
||||
min-height: 10rem; |
||||
} |
||||
|
||||
.ton-coin-icon { |
||||
margin-left: 4px; |
||||
width: 15px; |
||||
height: 15px; |
||||
} |
||||
</style> |
@ -0,0 +1,13 @@
|
||||
body { |
||||
margin: 0; |
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', |
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', |
||||
sans-serif; |
||||
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
} |
||||
|
||||
code { |
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', |
||||
monospace; |
||||
} |
@ -0,0 +1,19 @@
|
||||
import React from 'react'; |
||||
import ReactDOM from 'react-dom/client'; |
||||
import './index.css'; |
||||
import App from './App'; |
||||
import reportWebVitals from './reportWebVitals'; |
||||
|
||||
const root = ReactDOM.createRoot( |
||||
document.getElementById('root') as HTMLElement |
||||
); |
||||
root.render( |
||||
<React.StrictMode> |
||||
<App /> |
||||
</React.StrictMode> |
||||
); |
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals(); |
@ -1,112 +0,0 @@
|
||||
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://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 ""; |
||||
} |
||||
}, |
||||
connector(state) { |
||||
return state.connector; |
||||
}, |
||||
}, |
||||
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; |
||||
console.log(walletsList, embeddedWallet); |
||||
|
||||
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; |
||||
}, |
||||
async disconnect({ state }) { |
||||
await state.connector.disconnect(); |
||||
}, |
||||
}, |
||||
}); |
||||
|
||||
app.use(router); |
||||
app.use(store); |
||||
|
||||
app.mount("#app"); |
@ -0,0 +1,18 @@
|
||||
import React from 'react'; |
||||
import style from './button.module.css'; |
||||
|
||||
type TButton = { |
||||
onClick: () => void; |
||||
text: string; |
||||
}; |
||||
|
||||
function Button({text, onClick}: TButton) { |
||||
|
||||
return ( |
||||
<div className={style.container} onClick={onClick}> |
||||
<p>{text}</p> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default Button; |
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
@ -0,0 +1,15 @@
|
||||
import { ReportHandler } from 'web-vitals'; |
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => { |
||||
if (onPerfEntry && onPerfEntry instanceof Function) { |
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { |
||||
getCLS(onPerfEntry); |
||||
getFID(onPerfEntry); |
||||
getFCP(onPerfEntry); |
||||
getLCP(onPerfEntry); |
||||
getTTFB(onPerfEntry); |
||||
}); |
||||
} |
||||
}; |
||||
|
||||
export default reportWebVitals; |
@ -1,269 +0,0 @@
|
||||
// import {call_api} from "@/api";
|
||||
|
||||
import { Zone } from "@/zone"; |
||||
import type { Message } from "@/utils"; |
||||
import type { Collection } from "@/collection"; |
||||
import { call_api } from "@/api"; |
||||
|
||||
// let ex_collection = () => new Collection("example.ton", "Example collection");
|
||||
|
||||
export class Result { |
||||
domain: string; |
||||
buy_price?: number; |
||||
auction_price?: number; |
||||
owner?: string; |
||||
collection_required: Collection | 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.buy_price = buy_price; |
||||
this.auction_price = auction_price; |
||||
this.owner = owner; |
||||
this.collection_required = collection_required; |
||||
this.condition_fulfilled = condition_fulfilled; |
||||
this.buy_msg = buy_msg; |
||||
this.content_msg = content_msg; |
||||
this.nft_info = nft_info; |
||||
} |
||||
|
||||
static fromBackend(data: any): Result { |
||||
let domain = data.domain; |
||||
let buy_price = data.buy_price; |
||||
let auction_price = data.auction_price; |
||||
let owner = data.owner; |
||||
let collection_required = data.collection_required; |
||||
let condition_fulfilled = data.condition_fulfilled; |
||||
let buy_msg = data.buy_msg; |
||||
let content_msg = data.content_msg; |
||||
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 |
||||
); |
||||
} |
||||
|
||||
getRouteParams(): any { |
||||
return { |
||||
domain_init: /* domain up to . */ this.domain.split(".")[0], |
||||
domain: /* domain up to . */ this.domain.split(".")[0], |
||||
zone: /* domain after . */ this.domain.split(".").slice(1).join("."), |
||||
}; |
||||
} |
||||
|
||||
canAuction(): boolean { |
||||
return ( |
||||
this.auction_price !== undefined && |
||||
this.auction_price !== null && |
||||
this.owner === undefined |
||||
); |
||||
} |
||||
|
||||
canBuy(): boolean { |
||||
return ( |
||||
this.buy_price !== undefined && |
||||
this.buy_price !== null && |
||||
this.owner === undefined |
||||
); |
||||
} |
||||
|
||||
zone(): string { |
||||
return this.domain.split(".").slice(1).join("."); |
||||
} |
||||
} |
||||
|
||||
// const sleep = (milliseconds: number) => {
|
||||
// return new Promise(resolve => setTimeout(resolve, milliseconds))
|
||||
// }
|
||||
|
||||
export async function get_search_results(query: string) { |
||||
const res = await call_api("find/" + query); |
||||
|
||||
return res.map((item: any) => ({ |
||||
...item, |
||||
getRouteParams: () => ({ |
||||
domain_init: /* domain up to . */ item.domain.split(".")[0], |
||||
domain: /* domain up to . */ item.domain.split(".")[0], |
||||
zone: /* domain after . */ item.domain.split(".").slice(1).join("."), |
||||
}), |
||||
canAuction: () => |
||||
item.auction_price !== undefined && |
||||
item.auction_price !== null && |
||||
item.owner === undefined, |
||||
canBuy: () => |
||||
item.buy_price !== undefined && |
||||
item.buy_price !== null && |
||||
item.owner === undefined, |
||||
zone: () => item.domain.split(".").slice(1).join("."), |
||||
})); |
||||
|
||||
// canAuction
|
||||
// canBuy
|
||||
// zone
|
||||
// return {...res, };};
|
||||
/*await sleep(200); |
||||
return [ |
||||
new Result(query + '.ton', 5, 3), |
||||
new Result(query + '.ton', 1), |
||||
new Result(query + '.ton', undefined, 2), |
||||
new Result(query + '.ton', undefined, undefined, '123') |
||||
];*/ |
||||
} |
||||
|
||||
export async function get_domain_result(domain: string, _address?: string) { |
||||
return Result.fromBackend(await call_api("get/" + domain)); |
||||
/*await sleep(100); |
||||
if (domain === 'test.ton') { |
||||
return new Result(domain); |
||||
} |
||||
let exc = ex_collection(); |
||||
if (domain.startsWith('owned')) { |
||||
return new Result(domain, 5, 3, '123'); |
||||
} |
||||
if (parse_zone(domain) === 'example.ton') { |
||||
return new Result(domain, 5, 3, undefined, exc); |
||||
} |
||||
if (parse_zone(domain) === 'testtesttest.ton') { |
||||
console.log(address) |
||||
if (address !== undefined && address !== '') { |
||||
return new Result(domain, 5, 3, undefined, exc, false); |
||||
} else { |
||||
return new Result(domain, 5, 3, undefined, exc); |
||||
} |
||||
} |
||||
return new Result(domain, 5, 3);*/ |
||||
} |
||||
|
||||
export async function get_records(address: string) { |
||||
return await call_api("records/" + address); |
||||
} |
||||
|
||||
export async function get_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 |
||||
) |
||||
); |
||||
/*await sleep(10); |
||||
return [ |
||||
new Zone("example.ton", 3, 5, ex_collection()), |
||||
new Zone("agorata.ton", 3, 5), |
||||
new Zone("testtesttest.ton", 1, 1, ex_collection())];*/ |
||||
} |
||||
|
||||
export class TonLink { |
||||
address: string; |
||||
sum?: number; |
||||
message: string; |
||||
|
||||
constructor(address: string, message: string, sum?: number) { |
||||
this.address = address; |
||||
this.sum = sum; |
||||
this.message = message; |
||||
} |
||||
|
||||
getLink(): string { |
||||
// todo: use tonapi to run dnsresolve on the address (or move the task to the backend) - domains don't work well in links
|
||||
let link = `ton://transfer/${this.address}?message=${this.message}`; |
||||
if (this.sum !== undefined) { |
||||
link += `&amount=${this.sum}`; |
||||
} |
||||
return link; |
||||
} |
||||
} |
||||
|
||||
// Get the link for buying a domain
|
||||
export function get_ton_link(res: Result) { |
||||
return new TonLink(res.zone(), "b/" + res.domain, res.buy_price!); |
||||
} |
||||
|
||||
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 default_links = { |
||||
telegram: "https://t.me/", |
||||
website: "https://", |
||||
getgems: "https://getgems.org/", |
||||
email: "example@example.org", |
||||
}; |
||||
|
||||
export class SiteConstructorParams { |
||||
domain: string; |
||||
title: string; |
||||
description: string; |
||||
contacts: Map<string, string> = new Map<string, string>(); |
||||
template_id: string; |
||||
picture: string; |
||||
blog: string[]; |
||||
|
||||
constructor( |
||||
domain: string, |
||||
title: string = "", |
||||
description: string = "", |
||||
contacts: Map<string, string> = new Map<string, string>(), |
||||
template_id: string = "", |
||||
picture: string = "", |
||||
blog = [] |
||||
) { |
||||
this.domain = domain; |
||||
this.title = title; |
||||
this.description = description; |
||||
this.contacts = contacts; |
||||
this.template_id = template_id; |
||||
this.picture = picture; |
||||
this.blog = blog; |
||||
} |
||||
|
||||
copy(): SiteConstructorParams { |
||||
return new SiteConstructorParams(this.domain, this.title); |
||||
} |
||||
} |
||||
|
||||
export async function get_constr_params(domain: string) { |
||||
let res = await call_api("get-site-data/" + domain); |
||||
return new SiteConstructorParams( |
||||
res.domain, |
||||
res.title, |
||||
res.description, |
||||
res.contacts, |
||||
res.template_id, |
||||
res.picture, |
||||
res.blog |
||||
); |
||||
} |
@ -1,96 +0,0 @@
|
||||
import { createRouter, createWebHistory } from "vue-router"; |
||||
import Landing from "../views/Landing.vue"; |
||||
import { addTemplate, mintCollection, myDomains } from "./routes"; |
||||
|
||||
/* |
||||
/find - bar + table |
||||
/find/query - search results |
||||
/get/zone - get a domain in a zone (should be able to go to /find) |
||||
/get/zone/domain - get a specific domain |
||||
*/ |
||||
|
||||
const router = createRouter({ |
||||
history: createWebHistory(import.meta.env.BASE_URL), |
||||
routes: [ |
||||
{ |
||||
path: "/", |
||||
name: "home", |
||||
component: Landing, |
||||
}, |
||||
{ |
||||
path: "/tonweb", |
||||
name: "tonweb", |
||||
component: () => import("../views/TonWeb.vue"), |
||||
}, |
||||
{ |
||||
path: "/i-know", |
||||
name: "IKnow", |
||||
component: () => import("../views/IKnow.vue"), |
||||
}, |
||||
{ |
||||
path: "/i-have", |
||||
name: "IHave", |
||||
component: () => import("../views/IHave.vue"), |
||||
}, |
||||
{ |
||||
path: "/tondns", |
||||
name: "tondns", |
||||
component: () => import("../views/TonDns.vue"), |
||||
}, |
||||
{ |
||||
path: "/get/:zone/:domain_init", |
||||
name: "GetZ", |
||||
component: () => import("../views/Get.vue"), |
||||
props: true, |
||||
}, |
||||
{ |
||||
path: "/get/:zone/:domain_init", |
||||
name: "Get", |
||||
component: () => import("../views/Get.vue"), |
||||
props: true, |
||||
}, |
||||
{ |
||||
path: "/find", |
||||
name: "Find", |
||||
component: () => import("../views/Find.vue"), |
||||
}, |
||||
{ |
||||
path: "/checkout/:zone/:domain", |
||||
name: "Checkout", |
||||
component: () => import("../views/Checkout.vue"), |
||||
props: true, |
||||
}, |
||||
{ |
||||
path: "/find/:query", |
||||
name: "FindQ", |
||||
component: () => import("../views/FindQ.vue"), |
||||
props: true, |
||||
}, |
||||
{ |
||||
path: "/explore/:domain", |
||||
name: "Explore", |
||||
component: () => import("../views/Explore.vue"), |
||||
props: true, |
||||
}, |
||||
{ |
||||
path: "/my-domains", |
||||
name: myDomains, |
||||
component: () => import("../views/MyDomains.vue"), |
||||
props: true, |
||||
}, |
||||
{ |
||||
path: "/mint-collection/:domain", |
||||
name: mintCollection, |
||||
component: () => import("../views/MintCollection.vue"), |
||||
props: true, |
||||
}, |
||||
{ |
||||
path: "/add-template/:domain", |
||||
name: addTemplate, |
||||
component: () => import("../views/AddTemplate.vue"), |
||||
props: true, |
||||
}, |
||||
], |
||||
}); |
||||
|
||||
export default router; |
@ -1,3 +0,0 @@
|
||||
export const myDomains = "my-domains"; |
||||
export const mintCollection = "mint-collection"; |
||||
export const addTemplate = "add-template"; |
@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom'; |
@ -1,24 +0,0 @@
|
||||
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 }[]; |
||||
}; |
@ -1,126 +0,0 @@
|
||||
export class Message { |
||||
address: string; |
||||
amount: string; |
||||
payload: string; |
||||
|
||||
constructor(address: string, amount: string, payload: string) { |
||||
this.address = address; |
||||
this.amount = amount; |
||||
this.payload = payload; |
||||
} |
||||
} |
||||
|
||||
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', |
||||
} |
||||
} |
||||
} |
||||
|
||||
// export function convert_address(address_raw: string): string {
|
||||
// const address = new TonWeb.utils.Address(address_raw);
|
||||
// return address.toString(true, true, true);
|
||||
// }
|
||||
export function parse_zone(domain: string) { |
||||
// extract the zone from the domain (e.g. example.ton from 123.example.ton)
|
||||
return domain.split('.').slice(1).join('.'); |
||||
} |
||||
|
||||
// look up tables
|
||||
const to_hex_array = []; |
||||
// map from string to a number
|
||||
const to_byte_map: any = {}; |
||||
for (let ord = 0; ord <= 0xff; ord++) { |
||||
let s = ord.toString(16); |
||||
if (s.length < 2) { |
||||
s = "0" + s; |
||||
} |
||||
to_hex_array.push(s); |
||||
to_byte_map[s] = ord; |
||||
} |
||||
|
||||
export function hexToBytes(s: string) { |
||||
s = s.toLowerCase(); |
||||
const length2 = s.length; |
||||
if (length2 % 2 !== 0) { |
||||
throw "hex string must have length a multiple of 2"; |
||||
} |
||||
const length = length2 / 2; |
||||
const result = new Uint8Array(length); |
||||
for (let i = 0; i < length; i++) { |
||||
const i2 = i * 2; |
||||
const b = s.substring(i2, i2 + 2); |
||||
if (!to_byte_map.hasOwnProperty(b)) throw new Error('invalid hex character ' + b); |
||||
result[i] = to_byte_map[b]; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
|
||||
function crc16(data: any) { |
||||
const poly = 0x1021; |
||||
let reg = 0; |
||||
const message = new Uint8Array(data.length + 2); |
||||
message.set(data); |
||||
for (let byte of message) { |
||||
let mask = 0x80; |
||||
while (mask > 0) { |
||||
reg <<= 1; |
||||
if (byte & mask) { |
||||
reg += 1; |
||||
} |
||||
mask >>= 1 |
||||
if (reg > 0xffff) { |
||||
reg &= 0xffff; |
||||
reg ^= poly; |
||||
} |
||||
} |
||||
} |
||||
return new Uint8Array([Math.floor(reg / 256), reg % 256]); |
||||
} |
||||
|
||||
export function convertAddress(address: string) { |
||||
const addrSrc = hexToBytes(address.split(':')[1]); |
||||
const addr = new Int8Array(34); |
||||
addr[0] = 0x11; // tag (bounceable)
|
||||
addr[1] = 0; // workchain
|
||||
addr.set(addrSrc, 2); |
||||
|
||||
const addressWithChecksum = new Uint8Array(36); |
||||
addressWithChecksum.set(addr); |
||||
addressWithChecksum.set(crc16(addr), 34); |
||||
// @ts-ignore
|
||||
let addressBase64 = btoa(String.fromCharCode.apply(null, new Uint8Array(addressWithChecksum))); |
||||
addressBase64 = addressBase64.replace(/\+/g, '-').replace(/\//g, '_'); |
||||
return addressBase64; |
||||
} |
@ -1,134 +0,0 @@
|
||||
<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> |
@ -1,112 +0,0 @@
|
||||
<template> |
||||
<DarkLayout> |
||||
<template v-slot:header> |
||||
<router-link |
||||
:to="{ name: 'Get', params: { domain_init: domain, zone: zone } }" |
||||
> |
||||
<button class="b darkish back"> |
||||
<img src="@/assets/icons/ton_left.svg" alt="TON" /> |
||||
Back |
||||
</button> |
||||
</router-link> |
||||
</template> |
||||
<!-- todo: handle auction --> |
||||
<div class="text">To buy</div> |
||||
<DomainBar |
||||
:zone="'.' + zone" |
||||
:value="domain" |
||||
:has_button="false" |
||||
:editable="false" |
||||
/> |
||||
<div class="text">Confirm the transaction in your wallet</div> |
||||
<div class="center"> |
||||
<Socket /> |
||||
</div> |
||||
</DarkLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
import DarkLayout from "../components/DarkLayout.vue"; |
||||
import DomainBar from "../components/DomainBar.vue"; |
||||
import { get_domain_result, get_ton_link } from "../result"; |
||||
import RotateSquare2 from "../components/RotateSquare2.vue"; |
||||
import { qr_options } from "../utils"; |
||||
import Socket from "../components/Socket.vue"; |
||||
|
||||
export default { |
||||
name: "Checkout", |
||||
components: { Socket, RotateSquare2, DomainBar, DarkLayout }, |
||||
props: { |
||||
domain: { |
||||
type: String, |
||||
}, |
||||
zone: { |
||||
type: String, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
result: null, |
||||
ton_link: null, |
||||
qr_code: null, |
||||
}; |
||||
}, |
||||
methods: { |
||||
async get_result() { |
||||
this.result = await get_domain_result(this.domain + "." + this.zone); |
||||
this.ton_link = await get_ton_link(this.result); |
||||
}, |
||||
async sign_transaction() { |
||||
await this.get_result(); |
||||
let d = new Date(); |
||||
let validness = parseInt((d.getTime() / 1000).toFixed(0)) + 360000; |
||||
const transaction = { |
||||
validUntil: validness, |
||||
messages: [Object.assign({}, this.result.buy_msg)], |
||||
}; |
||||
console.log("Signing the transaction", transaction); |
||||
const result = await this.$store.state.connector.sendTransaction( |
||||
transaction |
||||
); |
||||
console.log(result); |
||||
if (result.boc === "") { |
||||
this.$router.push({ |
||||
name: "Get", |
||||
params: { domain_init: this.domain, zone: this.zone }, |
||||
}); |
||||
} |
||||
|
||||
let res = await get_domain_result(this.domain + "." + this.zone); |
||||
|
||||
while (res.owner !== this.$store.getters.address) { |
||||
res = await get_domain_result(this.domain + "." + this.zone); |
||||
} |
||||
|
||||
this.$router.push({ |
||||
name: "Explore", |
||||
params: { domain: this.domain + "." + this.zone }, |
||||
}); |
||||
}, |
||||
}, |
||||
mounted() { |
||||
this.sign_transaction(); |
||||
}, |
||||
computed: { |
||||
loaded() { |
||||
return this.result !== null && this.ton_link !== null; |
||||
}, |
||||
options() { |
||||
if (!this.loaded) { |
||||
return null; |
||||
} |
||||
return qr_options(this.ton_link.getLink()); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style> |
||||
.qr > div > svg { |
||||
border-radius: 2rem; |
||||
} |
||||
</style> |
@ -1,496 +0,0 @@
|
||||
<template> |
||||
<DarkLayout> |
||||
<template v-slot:header> |
||||
<router-link |
||||
:to="{ name: 'Get', params: { domain_init: core_domain, zone: zone } }" |
||||
> |
||||
<button class="b darkish back"> |
||||
<img src="@/assets/icons/ton_left.svg" alt="TON" /> |
||||
Back |
||||
</button> |
||||
</router-link> |
||||
</template> |
||||
<div style="min-width: 50%"> |
||||
<DomainBar |
||||
:value="core_domain" |
||||
:zone="'.' + zone" |
||||
:has_button="false" |
||||
:editable="false" |
||||
/> |
||||
<div v-if="loading" class="center"> |
||||
<RotateSquare2 style="width: 5rem; height: 5rem; margin-top: 3rem" /> |
||||
</div> |
||||
<div v-else> |
||||
<div class="owned" v-if="result.owner && !isMine"> |
||||
<p class="domain">{{ domain }}</p> |
||||
<div> |
||||
is owned by |
||||
<div class="owner-address">{{ ownerAddr }}</div> |
||||
</div> |
||||
<a :href="result.nft_info.explorer_link">View nft</a> |
||||
</div> |
||||
<div v-else-if="isMine" class="owned"> |
||||
<p class="domain">{{ domain }}</p> |
||||
<div> |
||||
is owned by |
||||
<div class="owner-address">you</div> |
||||
</div> |
||||
<div class="buttons"> |
||||
<a :href="result.nft_info.explorer_link">View nft</a> |
||||
<router-link class="button" :to="`/mint-collection/${domain}`"> |
||||
Mint a subdomain collection |
||||
</router-link> |
||||
</div> |
||||
<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 class="wallet-panel"> |
||||
<p class="label">Wallet:</p> |
||||
<contenteditable |
||||
class="record-inp wallet-record-field" |
||||
tag="div" |
||||
:no-hl="true" |
||||
:no-html="true" |
||||
spellcheck="false" |
||||
v-model="wallet_rec" |
||||
></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 |
||||
:class="{ |
||||
'record-submit': true, |
||||
get_b: true, |
||||
inactive: !walletChanged, |
||||
signing: signingWallet, |
||||
}" |
||||
@click="saveWallet()" |
||||
> |
||||
<template v-if="!signingWallet">Save</template> |
||||
<template v-else> |
||||
<Socket |
||||
secondary-color="#a7aab3" |
||||
color="#282e46" |
||||
size="50px" |
||||
style="min-width: 3rem" |
||||
/> |
||||
Confirm in the wallet... |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<SiteSettings |
||||
ref="site_settings" |
||||
@save="saveSite()" |
||||
@save-constructor="saveSiteConstr()" |
||||
@select-template="selectTemplate" |
||||
@change="site_rec = $event" |
||||
:template-id="constructor_params.template_id" |
||||
:site-changed="siteChanged" |
||||
:signing-site="signingSite" |
||||
@change-constructor="changeConstructor" |
||||
/> |
||||
<!-- @change-constructor="changeConstructor" --> |
||||
</div> |
||||
<div v-else> |
||||
This domain is not owned. |
||||
<router-link |
||||
:to="{ |
||||
name: 'Get', |
||||
params: { domain_init: core_domain, zone: zone }, |
||||
}" |
||||
> |
||||
<button class="b darkish back">Get it</button> |
||||
</router-link> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</DarkLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
import DarkLayout from "../components/DarkLayout.vue"; |
||||
import DomainBar from "../components/DomainBar.vue"; |
||||
import RotateSquare2 from "../components/RotateSquare2.vue"; |
||||
import { |
||||
get_constr_params, |
||||
get_domain_result, |
||||
get_records, |
||||
SiteConstructorParams, |
||||
} from "../result"; |
||||
import { convertAddress } from "../utils"; |
||||
import contenteditable from "vue-contenteditable"; |
||||
import { call_api, call_api_post, config } from "../api"; |
||||
import Socket from "../components/Socket.vue"; |
||||
import SiteSettings from "../components/SiteSettings.vue"; |
||||
|
||||
export default { |
||||
name: "Explore", |
||||
components: { |
||||
SiteSettings, |
||||
Socket, |
||||
DomainBar, |
||||
DarkLayout, |
||||
RotateSquare2, |
||||
contenteditable, |
||||
}, |
||||
props: { |
||||
domain: { |
||||
type: String, |
||||
}, |
||||
}, |
||||
data() { |
||||
const saved_constructor_params = new SiteConstructorParams( |
||||
this.domain, |
||||
this.domain |
||||
); |
||||
|
||||
return { |
||||
result: null, |
||||
records: { |
||||
wallet: null, |
||||
storage: null, |
||||
uri: null, |
||||
next_resolver: null, |
||||
site: null, |
||||
}, |
||||
wallet_rec: null, |
||||
site_rec: null, |
||||
signingSite: false, |
||||
signingWallet: false, |
||||
constructor_params: saved_constructor_params, |
||||
saved_constructor_params, |
||||
interval: null, |
||||
}; |
||||
}, |
||||
mounted() { |
||||
this.interval = setInterval( |
||||
() => |
||||
get_domain_result(this.domain, this.$store.getters.address).then( |
||||
(r) => { |
||||
this.result = Object.assign({}, r); |
||||
if (this.isMine) { |
||||
get_constr_params(this.domain).then((r) => { |
||||
this.constructor_params = r; |
||||
this.saved_constructor_params = r; |
||||
this.updSettingsComponent(); |
||||
}); |
||||
get_records(r.nft_info.address).then((r) => { |
||||
this.records = r; |
||||
// this.timer = setTimeout(this.updRecords, 10000); |
||||
this.updSettingsComponent(); |
||||
}); |
||||
} |
||||
} |
||||
), |
||||
20000 |
||||
); |
||||
}, |
||||
computed: { |
||||
core_domain() { |
||||
return this.domain.split(".")[0]; |
||||
}, |
||||
zone() { |
||||
return this.domain.split(".").slice(1).join("."); |
||||
}, |
||||
loading() { |
||||
return this.result === null; |
||||
}, |
||||
ownerAddr() { |
||||
let owner = this.result.owner; |
||||
if (!owner instanceof String) { |
||||
return ""; |
||||
} |
||||
let address = convertAddress(owner); |
||||
return address.slice(0, 5) + "..." + address.slice(-4); |
||||
}, |
||||
isMine() { |
||||
console.log(this.result); |
||||
return ( |
||||
this.result.owner === this.$store.getters.address || |
||||
this.result.nft_info.owner.address === this.$store.getters.address |
||||
); |
||||
}, |
||||
walletChanged() { |
||||
return this.records && this.wallet_rec !== this.records.wallet; |
||||
}, |
||||
siteChanged() { |
||||
let constr_change = false; |
||||
if (this.site_rec === config.agorata_adnl) { |
||||
constr_change = |
||||
this.constructor_params !== this.saved_constructor_params; |
||||
} |
||||
return ( |
||||
(this.records && |
||||
(this.site_rec !== this.records.site || constr_change)) || |
||||
(!this.records && constr_change) || |
||||
(this.records !== null && this.site_rec === null) |
||||
); |
||||
}, |
||||
settingsCompLoaded() { |
||||
return this.$refs.site_settings !== undefined; |
||||
}, |
||||
}, |
||||
methods: { |
||||
async saveWallet() { |
||||
if (!this.walletChanged) { |
||||
return; |
||||
} |
||||
// get the message from api at /set-record/site/{site} and sign it with tonconnect (from storage) |
||||
let message = await call_api("set-record/wallet/" + this.wallet_rec); |
||||
// send the message to tonconnect |
||||
let d = new Date(); |
||||
let validness = parseInt((d.getTime() / 1000).toFixed(0)) + 360000; |
||||
let transaction = { |
||||
validUntil: validness, |
||||
messages: [ |
||||
{ |
||||
amount: (0.05 * 1000000000).toString(), |
||||
address: this.result.nft_info.address, |
||||
payload: message, |
||||
}, |
||||
], |
||||
}; |
||||
this.signingWallet = true; |
||||
console.log("Sending transaction", transaction); |
||||
const result = await this.$store.state.connector.sendTransaction( |
||||
transaction |
||||
); |
||||
this.signingWallet = false; |
||||
if (!result.boc) { |
||||
// todo |
||||
} |
||||
this.records.wallet = this.wallet_rec; |
||||
}, |
||||
async saveSite() { |
||||
if (!this.siteChanged) { |
||||
return; |
||||
} |
||||
// get the message from api at /set-record/site/{site} and sign it with tonconnect (from storage) |
||||
let message = await call_api("set-record/site/" + this.site_rec); |
||||
// send the message to tonconnect |
||||
let d = new Date(); |
||||
let validness = parseInt((d.getTime() / 1000).toFixed(0)) + 360000; |
||||
let transaction = { |
||||
validUntil: validness, |
||||
messages: [ |
||||
{ |
||||
amount: (0.05 * 1000000000).toString(), |
||||
address: this.result.nft_info.address, |
||||
payload: message, |
||||
}, |
||||
], |
||||
}; |
||||
this.signingSite = true; |
||||
const result = await this.$store.state.connector.sendTransaction( |
||||
transaction |
||||
); |
||||
this.signingSite = false; |
||||
if (!result.boc) { |
||||
// todo |
||||
} |
||||
this.records.site = this.site_rec; |
||||
}, |
||||
async saveSiteConstr() { |
||||
if (!this.site_rec) { |
||||
this.site_rec = config.agorata_adnl.toLowerCase(); |
||||
await this.saveSite(); |
||||
} else if (this.site_rec !== this.records.site) { |
||||
await this.saveSite(); |
||||
} |
||||
this.constructor_params.domain = this.domain; |
||||
console.log("this.constructor_params", this.constructor_params); |
||||
await call_api_post("set-site-data", { |
||||
...this.constructor_params, |
||||
blog: |
||||
this.constructor_params.template_id === "Template #3" |
||||
? [ |
||||
"Ipsum dolor sit amet", |
||||
"Consectetur adipiscing elit", |
||||
"Sed do eiusmod tempor", |
||||
] |
||||
: undefined, |
||||
}); |
||||
this.saved_constructor_params = this.constructor_params; |
||||
this.updSettingsComponent(); |
||||
}, |
||||
updRecords() { |
||||
get_records(this.result.nft_info.address).then((r) => { |
||||
if (this.records.wallet !== r.wallet || this.records.site !== r.site) { |
||||
this.records = r; |
||||
} |
||||
}); |
||||
}, |
||||
updSettingsComponent() { |
||||
if (this.$refs.site_settings) { |
||||
this.$refs.site_settings.set_site_rec(this.site_rec); |
||||
this.$refs.site_settings.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; |
||||
}, |
||||
changeConstructor(evt) { |
||||
this.constructor_params = evt; |
||||
}, |
||||
}, |
||||
watch: { |
||||
records: function (val) { |
||||
if (val) { |
||||
this.wallet_rec = val.wallet; |
||||
if (!this.site_rec || val.site) { |
||||
this.site_rec = val.site; |
||||
} |
||||
} |
||||
}, |
||||
site_rec() { |
||||
if (this.$refs.site_settings) { |
||||
this.$refs.site_settings.set_site_rec(this.site_rec); |
||||
} |
||||
}, |
||||
saved_constructor_params() { |
||||
this.$refs.site_settings.saved_constructor_params = |
||||
this.saved_constructor_params; |
||||
}, |
||||
settingsCompLoaded() { |
||||
if (!this.loading && this.settingsCompLoaded) { |
||||
this.updSettingsComponent(); |
||||
} |
||||
}, |
||||
loading() { |
||||
if (!this.loading && this.settingsCompLoaded) { |
||||
this.updSettingsComponent(); |
||||
} |
||||
}, |
||||
}, |
||||
unmounted() { |
||||
clearInterval(this.interval); |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.domain { |
||||
font-family: "Inconsolata", monospace; |
||||
font-weight: bold; |
||||
font-size: 2rem; |
||||
} |
||||
|
||||
@media only screen and (max-width: 800px) { |
||||
.domain { |
||||
font-size: 1.7rem; |
||||
} |
||||
} |
||||
|
||||
.owned { |
||||
border-radius: 1rem; |
||||
background-color: #363e5e; |
||||
color: white; |
||||
font-size: 1.5rem; |
||||
text-align: center; |
||||
padding: 2rem; |
||||
} |
||||
|
||||
.owner-address { |
||||
font-family: "Inconsolata", monospace; |
||||
color: #c86161; |
||||
font-size: 2rem; |
||||
} |
||||
|
||||
.record-inp { |
||||
padding: 0.3rem; |
||||
border-radius: 0.3rem; |
||||
background-color: #4e5a88; |
||||
color: white; |
||||
} |
||||
|
||||
.rec-field:not(:last-child) { |
||||
margin-bottom: 4rem; |
||||
} |
||||
|
||||
.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; |
||||
} |
||||
|
||||
.wallet-record-field { |
||||
font-family: "Inconsolata", monospace; |
||||
} |
||||
|
||||
.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; |
||||
} |
||||
|
||||
.buttons { |
||||
display: flex; |
||||
align-items: 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> |
@ -1,34 +0,0 @@
|
||||
<template> |
||||
<DarkLayout> |
||||
<div class="center"> |
||||
<DomainBar :value="query" @search="search()" @input_d="query = $event" /> |
||||
</div> |
||||
<ZoneTable :domain="query" :zones="zones" /> |
||||
</DarkLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
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", |
||||
zones: [], |
||||
}; |
||||
}, |
||||
async mounted() { |
||||
this.zones = await get_zones(); |
||||
}, |
||||
methods: { |
||||
search() { |
||||
this.$router.push({ name: "FindQ", params: { query: this.query } }); |
||||
}, |
||||
}, |
||||
components: { ZoneTable, DarkLayout, DomainBar }, |
||||
}; |
||||
</script> |
@ -1,118 +0,0 @@
|
||||
<template> |
||||
<DarkLayout> |
||||
<div class="center"> |
||||
<DomainBar zone="" :value="query_" @search="search()" @input_d="query_ = $event"/> |
||||
<div class="results"> |
||||
<RotateSquare2 v-if="results === null" style="width: 5rem; height: 5rem"/> |
||||
<div v-for="result in results" :key="result" class="mobile-scale"> |
||||
<div class="search-result"> |
||||
<span class="domain">{{ result.domain }}</span> |
||||
<table> |
||||
<thead> |
||||
<tr> |
||||
<th v-if="result.canAuction()"> |
||||
<Popper content="Buy on auction" placement="left" :hover="true" :arrow="true"> |
||||
<img src="@/assets/icons/buy.svg" class="buy_img" alt="Buy on auction"/> |
||||
</Popper> |
||||
</th> |
||||
<th v-if="result.canBuy()"> |
||||
<Popper content="Buy instantly" placement="right" :hover="true" :arrow="true"> |
||||
<img src="@/assets/icons/instant_buy.svg" class="buy_img" alt="Instant Buy"/> |
||||
</Popper> |
||||
</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr> |
||||
<td v-if="result.canAuction()"> |
||||
<TonButton>{{ result.auction_price }}</TonButton> |
||||
</td> |
||||
<td v-if="result.canBuy()"> |
||||
<TonButton>{{ result.buy_price }}</TonButton> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
<router-link :to="{name: 'Get', params: result.getRouteParams()}"> |
||||
<div class="get_b"> |
||||
<template v-if="result.canBuy() || result.canAuction()">Get</template> |
||||
<template v-else>See</template> |
||||
</div> |
||||
</router-link> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</DarkLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
import DomainBar from "../components/DomainBar.vue"; |
||||
import DarkLayout from "../components/DarkLayout.vue"; |
||||
import RotateSquare2 from "../components/RotateSquare2.vue"; |
||||
import TonButton from "../components/TonButton.vue"; |
||||
import Popper from "vue3-popper"; |
||||
import {get_search_results} from "../result"; |
||||
|
||||
export default { |
||||
name: "FindQ", |
||||
props: { |
||||
query: { |
||||
type: String, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
query_: this.query, |
||||
results: null, |
||||
} |
||||
}, |
||||
mounted() { |
||||
this.getResults(); |
||||
}, |
||||
components: {RotateSquare2, DarkLayout, DomainBar, TonButton, Popper}, |
||||
methods: { |
||||
search() { |
||||
this.$router.push({name: 'FindQ', params: {query: this.query_}}); |
||||
}, |
||||
async getResults() { |
||||
this.results = await get_search_results(this.query_); |
||||
} |
||||
}, |
||||
} |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.results { |
||||
margin-top: 3rem; |
||||
} |
||||
|
||||
.search-result { |
||||
margin: 1rem 0; |
||||
padding: 1rem; |
||||
border-radius: 1rem; |
||||
background-color: #4e5a88; |
||||
color: white; |
||||
font-size: 1.5rem; |
||||
display: flex; |
||||
height: 6rem; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
} |
||||
|
||||
.search-result > .domain { |
||||
font-family: 'Inconsolata', monospace; |
||||
} |
||||
|
||||
table { |
||||
width: 10rem; |
||||
} |
||||
|
||||
.buy_img { |
||||
margin-bottom: -15px; |
||||
} |
||||
|
||||
tr > th { |
||||
padding-bottom: -10px; |
||||
} |
||||
</style> |
@ -1,64 +0,0 @@
|
||||
<template> |
||||
<DarkLayout ref="dl"> |
||||
<template v-slot:header> |
||||
<router-link :to="{ name: 'Find' }"> |
||||
<button class="b darkish back"> |
||||
<img src="@/assets/icons/ton_left.svg" alt="TON" /> |
||||
All zones |
||||
</button> |
||||
</router-link> |
||||
</template> |
||||
<DomainBar |
||||
:zone="'.' + zone" |
||||
:value="domain" |
||||
@search="search()" |
||||
@input_d="handle_input($event)" |
||||
/> |
||||
<DomainResult |
||||
:domain="domain + '.' + zone" |
||||
v-if="domain !== ''" |
||||
@login="$refs.dl.login()" |
||||
/> |
||||
</DarkLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
import DarkLayout from "../components/DarkLayout.vue"; |
||||
import DomainBar from "../components/DomainBar.vue"; |
||||
import DomainResult from "../components/DomainResult.vue"; |
||||
export default { |
||||
name: "Get", |
||||
props: { |
||||
domain_init: { |
||||
type: String, |
||||
}, |
||||
zone: { |
||||
type: String, |
||||
}, |
||||
}, |
||||
components: { DomainResult, DomainBar, DarkLayout }, |
||||
data() { |
||||
return { |
||||
domain: this.domain_init, |
||||
}; |
||||
}, |
||||
methods: { |
||||
search() { |
||||
this.$router.push({ |
||||
name: "Get", |
||||
params: { domain_init: this.domain, zone: this.zone }, |
||||
}); |
||||
}, |
||||
handle_input(val) { |
||||
this.domain = val; |
||||
}, |
||||
}, |
||||
watch: { |
||||
domain_init: function (val) { |
||||
this.domain = val; |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped></style> |
@ -1,88 +0,0 @@
|
||||
<template> |
||||
<WhiteLayout next=""> |
||||
<template v-slot:header> |
||||
<img |
||||
src="@/assets/headers/i-know.svg" |
||||
style="max-height: 100%; max-width: 90%" |
||||
alt="TON DNS" |
||||
/> |
||||
</template> |
||||
<template v-slot:content> |
||||
<div class="cont2"> |
||||
<div |
||||
class="columns-3" |
||||
style=" |
||||
display: flex; |
||||
columns: 3; |
||||
justify-content: center; |
||||
align-items: center; |
||||
" |
||||
> |
||||
<div></div> |
||||
<div id="great"> |
||||
<p> |
||||
Very good!<br />We can help you host a website there or sell |
||||
subdomains.<br /> |
||||
For now, you will have to |
||||
<a href="https://t.me/ennucore">contact us on Telegram</a>. <br /> |
||||
We are especially interested in partnering with different projects |
||||
and communities which are interested in distributing domains to |
||||
their users.<br /> |
||||
Also, you can always |
||||
<router-link :to="{ name: 'Find' }" |
||||
>buy another domain</router-link |
||||
>. |
||||
</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<template v-slot:buttons> |
||||
<a href="https://t.me/ennucore"> |
||||
<button class="b blue wide"> |
||||
<span>Contact us</span> |
||||
</button> |
||||
</a> |
||||
<router-link :to="{ name: 'Find' }"> |
||||
<button class="b blue wide"> |
||||
<span>Buy a domain</span> |
||||
</button> |
||||
</router-link> |
||||
</template> |
||||
</WhiteLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
import WhiteLayout from "../components/WhiteLayout.vue"; |
||||
|
||||
export default { |
||||
name: "IHave", |
||||
components: { WhiteLayout }, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
#great { |
||||
font-size: 3rem; |
||||
} |
||||
|
||||
/* narrow buttons on mobile */ |
||||
@media only screen and (max-width: 800px) { |
||||
.b.blue.wide { |
||||
max-width: 35vw; |
||||
min-width: revert; |
||||
} |
||||
|
||||
#great { |
||||
font-size: 1.5rem; |
||||
} |
||||
} |
||||
.cont2 { |
||||
/* vertical centering */ |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
height: 100%; |
||||
} |
||||
</style> |
@ -1,78 +0,0 @@
|
||||
<template> |
||||
<WhiteLayout next=""> |
||||
<template v-slot:header> |
||||
<img |
||||
src="@/assets/headers/i-know.svg" |
||||
style="max-height: 100%; max-width: 90%" |
||||
alt="TON DNS" |
||||
/> |
||||
</template> |
||||
<template v-slot:content> |
||||
<div class="cont2"> |
||||
<div |
||||
class="columns-3" |
||||
style=" |
||||
display: flex; |
||||
columns: 3; |
||||
justify-content: center; |
||||
align-items: center; |
||||
" |
||||
> |
||||
<div></div> |
||||
<div id="great"> |
||||
<p>That's great!<br />Maybe, you even have a domain already?</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<template v-slot:buttons> |
||||
<router-link :to="{ name: 'IHave' }"> |
||||
<button class="b blue wide"> |
||||
<img src="@/assets/icons/ton_top.svg" alt="Next" /> |
||||
<span>Yes</span> |
||||
</button> |
||||
</router-link> |
||||
<router-link :to="{ name: 'tondns' }"> |
||||
<button class="b blue wide"> |
||||
<img src="@/assets/icons/ton_bottom.svg" alt="Next" /> |
||||
<span>No</span> |
||||
</button> |
||||
</router-link> |
||||
</template> |
||||
</WhiteLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
import WhiteLayout from "../components/WhiteLayout.vue"; |
||||
|
||||
export default { |
||||
name: "IKnow", |
||||
components: { WhiteLayout }, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
#great { |
||||
font-size: 4rem; |
||||
} |
||||
|
||||
/* narrow buttons on mobile */ |
||||
@media only screen and (max-width: 800px) { |
||||
.b.blue.wide { |
||||
max-width: 35vw; |
||||
min-width: revert; |
||||
} |
||||
|
||||
#great { |
||||
font-size: 2rem; |
||||
} |
||||
} |
||||
.cont2 { |
||||
/* vertical centering */ |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
height: 100%; |
||||
} |
||||
</style> |
@ -1,57 +0,0 @@
|
||||
<script setup lang="ts"></script> |
||||
|
||||
<template> |
||||
<main> |
||||
<!-- @/assets/logo_single.png centered horizontally and slightly above center vertically --> |
||||
<img class="logo" src="@/assets/logo_landing.png" alt="Agorata" /> |
||||
<p style="font-size: 1.8rem; padding: 2rem"> |
||||
Helping you <b style="font-weight: bold">be</b> the new internet |
||||
</p> |
||||
|
||||
<div id="do-you-know"> |
||||
<p>Do you know what TON Web is?</p> |
||||
<br /> |
||||
<router-link to="/i-know"> |
||||
<button class="b white wide">Yes</button> |
||||
</router-link> |
||||
<br /> |
||||
<router-link to="/tonweb"> |
||||
<button class="b white wide">No</button> |
||||
</router-link> |
||||
</div> |
||||
</main> |
||||
</template> |
||||
|
||||
<style scoped lang="scss"> |
||||
// Main has a radial gradient background with #1f2b5c at the center and #3e3e4b at the edges. |
||||
main { |
||||
background: radial-gradient(ellipse at center, #1f2b5c 0%, #3e3e4b 100%); |
||||
height: 100vh; |
||||
width: 100vw; |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
color: white; |
||||
font-family: "Raleway", serif; |
||||
font-size: 1.5rem; |
||||
text-align: center; |
||||
} |
||||
|
||||
.logo { |
||||
display: block; |
||||
margin: 0 auto 2rem; |
||||
width: 25%; |
||||
min-width: 13rem; |
||||
} |
||||
|
||||
#do-you-know { |
||||
margin-top: 5rem; |
||||
// Center elements horizontally, with each element at its line |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
padding: 2rem; |
||||
} |
||||
</style> |
@ -1,139 +0,0 @@
|
||||
<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> |
@ -1,18 +0,0 @@
|
||||
<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,53 +0,0 @@
|
||||
<template> |
||||
<WhiteLayout> |
||||
<template v-slot:header> |
||||
<img src="@/assets/tondns.svg" style="height: 100%" alt="TON DNS" /> |
||||
</template> |
||||
<template v-slot:content> |
||||
<div style="justify-content: center; align-items: center"> |
||||
<div> |
||||
<p> |
||||
TON domains — .ton and .t.me — are your names in the TON world.<br /> |
||||
They can be used to host a website or to transfer money.<br /> |
||||
You can host a TON website for your personal brand. |
||||
</p> |
||||
</div> |
||||
<div class="columns-3 images"> |
||||
<img class="img" src="@/assets/images/usage.png" alt="Usage" /> |
||||
<img class="img" src="@/assets/images/my_name_is.png" alt="lame" /> |
||||
<img class="img" src="@/assets/images/anton_ton.png" alt="cool" /> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
<template v-slot:buttons> |
||||
<router-link :to="{ name: 'Find' }"> |
||||
<button class="b blue wide"> |
||||
<span>Omg, can I get one?</span> |
||||
<img src="@/assets/icons/ton_right.svg" alt="Next" /> |
||||
</button> |
||||
</router-link> |
||||
</template> |
||||
</WhiteLayout> |
||||
</template> |
||||
|
||||
<script> |
||||
import WhiteLayout from "../components/WhiteLayout.vue"; |
||||
|
||||
export default { |
||||
name: "TonDns", |
||||
components: { WhiteLayout }, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped> |
||||
.img { |
||||
width: 25%; |
||||
margin: 0 1rem; |
||||
} |
||||
/* Fix width and not height on mobile */ |
||||
@media only screen and (max-width: 800px) { |
||||
.img { |
||||
width: 75vw; |
||||
} |
||||
} |
||||
</style> |