Browse Source

add balance to dashdboard

main
matthew 2 years ago
parent
commit
ef05117a60
  1. 2
      searching-front/app/core/components/Footer/Footer.tsx
  2. 5
      searching-front/app/core/components/Link/styles.module.css
  3. 3
      searching-front/app/core/helpers/common.ts
  4. 12
      searching-front/app/core/icons/TonLogoWithoutBg.tsx
  5. 33
      searching-front/app/core/pages/State/State.tsx
  6. 55
      searching-front/app/core/pages/State/styles.module.css
  7. 14
      searching-front/package-lock.json
  8. 2
      searching-front/package.json
  9. 70
      searching-front/services/domain-watcher.ts
  10. 2
      searching-front/services/helpers.ts
  11. 10
      searching-front/services/main.ts

2
searching-front/app/core/components/Footer/Footer.tsx

@ -34,8 +34,8 @@ const Footer = () => {
return ( return (
<div className={s.root}> <div className={s.root}>
<div className={s.contactsWrapper}> <div className={s.contactsWrapper}>
<FooterLink href="https://searching_ton.t.me/" keyLink="footer.linkContacts" />
<FooterLink href="https://searchington.t.me/" keyLink="footer.linkAnnouncments" /> <FooterLink href="https://searchington.t.me/" keyLink="footer.linkAnnouncments" />
<FooterLink href="https://searching_ton_feedback_bot.t.me/" keyLink="footer.linkFeedback" />
</div> </div>
</div> </div>
) )

5
searching-front/app/core/components/Link/styles.module.css

@ -1,6 +1,6 @@
.root { .root {
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-flex;
/* size */ /* size */
font-size: 18px; font-size: 18px;
@ -8,6 +8,9 @@
} }
.root a { .root a {
text-decoration: none; text-decoration: none;
display: inline-flex;
justify-content: center;
align-items: center;
} }
.root a.wide::before { .root a.wide::before {

3
searching-front/app/core/helpers/common.ts

@ -10,3 +10,6 @@ export const isNode = () => {
} }
export const getDomainFromUrl = (url: string) => url.match(/http:\/\/(.*).ton/)?.[1] export const getDomainFromUrl = (url: string) => url.match(/http:\/\/(.*).ton/)?.[1]
export const getObserverUrlByAddress = (address: string) => `https://tonscan.org/address/${address}`

12
searching-front/app/core/icons/TonLogoWithoutBg.tsx

@ -0,0 +1,12 @@
const TonLogoWithoutBg = () => (
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.79243 2.50158C3.80829 2.50053 3.82417 2.5 3.84006 2.5H7.89606C7.90067 2.5 7.90527 2.50004 7.90986 2.50013C7.91445 2.50004 7.91905 2.5 7.92366 2.5H11.9797C11.9955 2.5 12.0114 2.50053 12.0273 2.50158C12.3422 2.52246 12.6472 2.62068 12.9151 2.78756C13.183 2.95443 13.4056 3.18481 13.5633 3.45827C13.7209 3.73174 13.8086 4.03985 13.8187 4.35533C13.8288 4.6708 13.761 4.9839 13.6212 5.2669C13.6144 5.2806 13.6072 5.29409 13.5996 5.30733L8.54758 14.0793C8.41442 14.3105 8.16771 14.4449 7.90986 14.4399C7.652 14.4449 7.4053 14.3105 7.27214 14.0793L2.22014 5.30733C2.21251 5.29409 2.2053 5.2806 2.19853 5.2669C2.05873 4.98391 1.99088 4.67081 2.00098 4.35533C2.01109 4.03985 2.09884 3.73174 2.25646 3.45827C2.41408 3.18481 2.63671 2.95444 2.90463 2.78756C3.17255 2.62068 3.47748 2.52246 3.79243 2.50158ZM8.64366 11.0272L12.3377 4.61302C12.3675 4.54665 12.3818 4.47429 12.3795 4.40143C12.3769 4.32263 12.355 4.24567 12.3157 4.17737C12.2763 4.10907 12.2207 4.05153 12.1538 4.00985C12.0921 3.97143 12.0226 3.94758 11.9504 3.94H8.64366V11.0272ZM7.17606 3.94H3.86928C3.79717 3.94758 3.72763 3.97143 3.66595 4.00985C3.59903 4.05153 3.54342 4.10907 3.50405 4.17737C3.46469 4.24567 3.44277 4.32263 3.44025 4.40143C3.43791 4.47429 3.45223 4.54665 3.48201 4.61303L7.17606 11.0272V3.94Z"
fill="currentColor"
/>
</svg>
)
export default TonLogoWithoutBg

33
searching-front/app/core/pages/State/State.tsx

@ -9,13 +9,14 @@ import "chart.js/auto"
import getActualSitesState from "app/stateSites/queries/getActualSitesState" import getActualSitesState from "app/stateSites/queries/getActualSitesState"
import getHistoryOfSitesState from "app/stateSites/queries/getHistoryOfSitesState" import getHistoryOfSitesState from "app/stateSites/queries/getHistoryOfSitesState"
import { InfluxPeriod } from "services/modules/influxdb/types" import { InfluxPeriod } from "services/modules/influxdb/types"
import { cn, getDomainFromUrl } from "app/core/helpers/common" import { cn, getDomainFromUrl, getObserverUrlByAddress } from "app/core/helpers/common"
import s from "./styles.module.css" import s from "./styles.module.css"
import { NftDomain } from "@prisma/client" import { NftDomain } from "@prisma/client"
import Button from "app/auth/components/Button" import Button from "app/auth/components/Button"
import Link from "app/core/components/Link" import Link from "app/core/components/Link"
import { count } from "app/core/helpers/metrika" import { count } from "app/core/helpers/metrika"
import TonLogoWithoutBg from "app/core/icons/TonLogoWithoutBg"
interface HistoryOfStateItem { interface HistoryOfStateItem {
value: number value: number
@ -89,7 +90,7 @@ const State = ({
historyOfState: historyOfStatePreloaded, historyOfState: historyOfStatePreloaded,
lastWeekNewSites, lastWeekNewSites,
}: StatePageProps) => { }: StatePageProps) => {
const nowDate = format(new Date(),'d MMMM').toLowerCase(); const nowDate = format(new Date(), "d MMMM").toLowerCase()
const [historyPeriod, setHistoryPeriod] = useState(InfluxPeriod.D) const [historyPeriod, setHistoryPeriod] = useState(InfluxPeriod.D)
const { t } = useTranslation() const { t } = useTranslation()
const [historyOfState] = useQuery(getHistoryOfSitesState, historyPeriod, { const [historyOfState] = useQuery(getHistoryOfSitesState, historyPeriod, {
@ -115,19 +116,43 @@ const State = ({
</div> </div>
<div className={s.newestListWrapper}> <div className={s.newestListWrapper}>
{lastWeekNewSites.map((i) => ( {lastWeekNewSites.map((i) => (
<div className={s.newestListItem}>
<Link <Link
className={s.newestListItemLeft}
target="_blank" target="_blank"
theme="clear" theme="clear"
href={i.address + "?from=Searching.ton"} href={i.address + "?from=Searching.ton"}
onClick={() => count("from_state_page_to_site")}
wide wide
className={s.newestListItem} title="Open tonsite"
onClick={()=>count('from_state_page_to_site')}
> >
{getDomainFromUrl(i.address)} {getDomainFromUrl(i.address)}
<Button className={s.siteButton} theme="primary"> <Button className={s.siteButton} theme="primary">
.ton .ton
</Button> </Button>
</Link> </Link>
{i.walletAddress && i.tonBalance && (
<Link
className={s.newestListItemRight}
target="_blank"
theme="clear"
href={getObserverUrlByAddress(i.walletAddress)}
onClick={() => count("from_state_page_to_tonscan")}
title="Open wallet in tonscan"
wide
>
~
{new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" })
.format(+i.tonBalance)
.replace("$", "")
.replace(",", " ")}
<div className={s.tonScanIcon}>
<TonLogoWithoutBg />
</div>
</Link>
)}
</div>
))} ))}
</div> </div>

55
searching-front/app/core/pages/State/styles.module.css

@ -64,16 +64,15 @@
flex-direction: column; flex-direction: column;
} }
.newestListItem { .newestListItem {
padding: 24px 34px;
border-radius: var(--border_radius_base); border-radius: var(--border_radius_base);
border: 1px solid var(--border_color_main); border: 1px solid var(--border_color_main);
text-transform: capitalize; text-transform: capitalize;
font-weight: 700; font-weight: 700;
font-size: 16px; font-size: 16px;
line-height: 20px; line-height: 20px;
cursor: pointer;
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
display: flex;
} }
.siteButton.siteButton { .siteButton.siteButton {
@ -82,16 +81,12 @@
font-size: 20px; font-size: 20px;
line-height: 25px; line-height: 25px;
border-radius: 16px; border-radius: 16px;
transition: opacity 0.2s ease-in-out;
padding: 0; padding: 0;
background-color: var(--background_secondary); background-color: var(--background_secondary);
margin-left: 8px; margin-left: 8px;
} }
.newestListItem:hover .siteButton.siteButton {
background-color: var(--button_primary);
}
.button { .button {
position: absolute; position: absolute;
@ -146,3 +141,49 @@
background: var(--button_primary); background: var(--button_primary);
color: white; color: white;
} }
.newestListItemLeft {
position: relative;
padding: 24px 0 24px 34px;
flex: 1 1 70%;
}
.newestListItemRight {
padding: 24px 34px 24px 0px;
flex: 1 1 30%;
position: relative;
display: flex;
align-items: center;
border-left: 1px solid var(--border_color_main);
justify-content: flex-end;
white-space: nowrap;
font-size: 12px !important;
background: inherit;
}
.newestListItemLeft:hover .siteButton.siteButton {
background-color: var(--button_primary);
}
.newestListItemRight:hover .tonScanIcon{
color: white;
background: var(--button_primary);
transition: all .3s ease-in-out;
}
.tonScanIcon{
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: var(--button_primary);
margin-left: 4px;
}
.siteButton, .tonScanIcon {
transition: background-color .2s ease-in-out;
}

14
searching-front/package-lock.json generated

@ -45,7 +45,7 @@
"sanitize-html": "2.7.1", "sanitize-html": "2.7.1",
"textversionjs": "1.1.3", "textversionjs": "1.1.3",
"tonapi-sdk-js": "0.18.0", "tonapi-sdk-js": "0.18.0",
"tonweb": "0.0.55", "tonweb": "0.0.57",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"zod": "3.17.3" "zod": "3.17.3"
}, },
@ -15585,9 +15585,9 @@
} }
}, },
"node_modules/tonweb": { "node_modules/tonweb": {
"version": "0.0.55", "version": "0.0.57",
"resolved": "https://registry.npmjs.org/tonweb/-/tonweb-0.0.55.tgz", "resolved": "https://registry.npmjs.org/tonweb/-/tonweb-0.0.57.tgz",
"integrity": "sha512-C6YUTmHEzAHayUZMh7TWkoPr5lFAuetAO/7CmIumRQm85aeFcTY86TeCsJz0rmf2vmKPlJE2hqjByN5cYFKQfA==", "integrity": "sha512-/kqRn3/H1nw2bJ68uOtvwkOj6/9EgYaYpMYAXs93I2pLdJZxVKNwTuZ7weGKVmrn9tOt/nCNooq3OAZDSAVUSg==",
"dependencies": { "dependencies": {
"@ledgerhq/hw-transport-web-ble": "5.48.0", "@ledgerhq/hw-transport-web-ble": "5.48.0",
"@ledgerhq/hw-transport-webhid": "5.48.0", "@ledgerhq/hw-transport-webhid": "5.48.0",
@ -27855,9 +27855,9 @@
} }
}, },
"tonweb": { "tonweb": {
"version": "0.0.55", "version": "0.0.57",
"resolved": "https://registry.npmjs.org/tonweb/-/tonweb-0.0.55.tgz", "resolved": "https://registry.npmjs.org/tonweb/-/tonweb-0.0.57.tgz",
"integrity": "sha512-C6YUTmHEzAHayUZMh7TWkoPr5lFAuetAO/7CmIumRQm85aeFcTY86TeCsJz0rmf2vmKPlJE2hqjByN5cYFKQfA==", "integrity": "sha512-/kqRn3/H1nw2bJ68uOtvwkOj6/9EgYaYpMYAXs93I2pLdJZxVKNwTuZ7weGKVmrn9tOt/nCNooq3OAZDSAVUSg==",
"requires": { "requires": {
"@ledgerhq/hw-transport-web-ble": "5.48.0", "@ledgerhq/hw-transport-web-ble": "5.48.0",
"@ledgerhq/hw-transport-webhid": "5.48.0", "@ledgerhq/hw-transport-webhid": "5.48.0",

2
searching-front/package.json

@ -64,7 +64,7 @@
"sanitize-html": "2.7.1", "sanitize-html": "2.7.1",
"textversionjs": "1.1.3", "textversionjs": "1.1.3",
"tonapi-sdk-js": "0.18.0", "tonapi-sdk-js": "0.18.0",
"tonweb": "0.0.55", "tonweb": "0.0.57",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"zod": "3.17.3" "zod": "3.17.3"
}, },

70
searching-front/services/domain-watcher.ts

@ -1,4 +1,4 @@
import tonweb from "tonweb" import TonWeb from "tonweb"
import { import {
JettonApi, JettonApi,
DNSApi, DNSApi,
@ -13,19 +13,30 @@ import db from "../db/index"
import axios from "axios" import axios from "axios"
import { getTonProxy } from "./helpers" import { getTonProxy } from "./helpers"
const dnsApi = new DNSApi()
interface SearchNFTItemsParams { interface SearchNFTItemsParams {
limit: number limit: number
offset: number offset: number
} }
const getFullUrl = (dmn: string) => `http://${dmn}` const getFullUrl = (dmn: string) => `http://${dmn}`
const upsertDmn = async (dmn: string, available: boolean) => {
const domainFromDB = await db.nftDomain.findFirst({where:{address: getFullUrl(dmn)}})
const shouldUpdateFirstAvailableDate = !domainFromDB?.firstAvailableDate && available;
const upsertObj = { available, address: getFullUrl(dmn) }; interface UpsertDmnParams {
if(shouldUpdateFirstAvailableDate){ available: boolean
upsertObj.firstAvailableDate = new Date(); walletAddress?: string
tonBalance?: number
}
const upsertDmn = async (
dmn: string,
{ available, tonBalance, walletAddress }: UpsertDmnParams
) => {
const domainFromDB = await db.nftDomain.findFirst({ where: { address: getFullUrl(dmn) } })
const shouldUpdateFirstAvailableDate = !domainFromDB?.firstAvailableDate && available
const upsertObj = { available, address: getFullUrl(dmn), domainName: dmn, walletAddress, tonBalance }
if (shouldUpdateFirstAvailableDate) {
upsertObj.firstAvailableDate = new Date()
} }
return await db.nftDomain.upsert({ return await db.nftDomain.upsert({
where: { where: {
@ -36,7 +47,6 @@ const upsertDmn = async (dmn: string, available: boolean) => {
}) })
} }
const wait = (time: number) => new Promise((resolve) => setTimeout(() => resolve(true), time)) const wait = (time: number) => new Promise((resolve) => setTimeout(() => resolve(true), time))
const searchNFTItems = async ({ limit, offset }: SearchNFTItemsParams) => { const searchNFTItems = async ({ limit, offset }: SearchNFTItemsParams) => {
@ -53,6 +63,7 @@ const searchNFTItems = async ({ limit, offset }: SearchNFTItemsParams) => {
) )
return data.nft_items return data.nft_items
} catch (e) { } catch (e) {
console.log("error fetch items", e)
return searchNFTItems({ limit, offset }) return searchNFTItems({ limit, offset })
} }
} }
@ -71,12 +82,40 @@ const fetchTonSite = async (url: string) => {
} }
return url return url
} catch (e) { } catch (e) {
// console.log('restart fetch domains',e)
throw url throw url
} }
} }
const tonweb = new TonWeb(
new TonWeb.HttpProvider("https://toncenter.com/api/v2/jsonRPC", {
apiKey: "2594e0a460095b81258b639950a16d9718fa3cbb1ba8a2eb87fbb4586a529b8f",
})
)
const fetchDomainInfo = async (url: string) => {
try {
await wait(1)
const address = (await tonweb.dns.getWalletAddress(url))?.toString(true, true, true)
let balance
if (address) {
balance = tonweb.utils.fromNano(await tonweb.getBalance(address))
return {
balance,
address,
}
}
return null
} catch (e) {
return null
}
}
const main = async () => const main = async () =>
new Promise(async (resolve) => { new Promise(async (resolve) => {
// const result = await tonweb.dns.getWalletAddress('just-for-test.ton')
// Receive typed array of owner nfts // Receive typed array of owner nfts
let count = 0 let count = 0
while (true) { while (true) {
@ -86,26 +125,25 @@ const main = async () =>
offset: count * portion, offset: count * portion,
}) })
if (nftItems.length) { if (nftItems.length) {
console.time('fetchDomain')
const promises1: Promise<unknown>[] = [] const promises1: Promise<unknown>[] = []
for (let i = 0; i < nftItems.length; i++) { for (let i = 0; i < nftItems.length; i++) {
const nftDomainItem = nftItems[i] const nftDomainItem = nftItems[i]
if (nftDomainItem.dns) { if (nftDomainItem.dns) {
promises1.push(
promises1.push(fetchTonSite(nftDomainItem.dns) fetchTonSite(nftDomainItem.dns)
.then(async (dmn) => { .then(async (dmn) => {
const domainInfo = await fetchDomainInfo(dmn)
console.log("success dmn", dmn) console.log("success dmn", dmn)
upsertDmn(dmn, true) upsertDmn(dmn, {available: true, walletAddress:domainInfo?.address,tonBalance:domainInfo?.balance})
}) })
.catch((dmn) => { .catch((dmn) => {
upsertDmn(dmn,false) upsertDmn(dmn, {available: false})
})) })
)
} }
} }
count++ count++
await Promise.all(promises1) await Promise.all(promises1)
console.timeEnd('fetchDomain')
continue continue
} }
break break

2
searching-front/services/helpers.ts

@ -1,4 +1,4 @@
export const getTonProxy = () => ({ export const getTonProxy = () => ({
host: "in2.ton.org", host: "in3.ton.org",
port: 8080, port: 8080,
}) })

10
searching-front/services/main.ts

@ -10,11 +10,11 @@ const run = async()=>{
console.time('watcher') console.time('watcher')
await domainWatcher(); await domainWatcher();
console.timeEnd('watcher') console.timeEnd('watcher')
influx() // influx()
console.log('Start parser'); // console.log('Start parser');
console.time('watcher'); // console.time('watcher');
await parser(); // await parser();
console.timeEnd('watcher'); // console.timeEnd('watcher');
} }

Loading…
Cancel
Save