From 77059084846b6ccc0048bcf03c440ee22fe95cc4 Mon Sep 17 00:00:00 2001 From: Verticool Date: Sat, 1 Feb 2025 19:17:40 +0600 Subject: [PATCH] tg buttons /viewContent, tags, validation address form and more /uploadContent --- .env | 1 + .gitignore | 1 + package-lock.json | 65 +++++++++++ package.json | 1 + src/app/index.tsx | 1 + src/app/styles/globals.css | 105 +++++++++++------- src/pages/root/steps/presubmit-step/index.tsx | 20 +++- src/pages/root/steps/price-step/index.tsx | 37 +++--- src/pages/root/steps/royalty-step/index.tsx | 52 +++++++-- src/pages/view-content/index.tsx | 43 ++++++- src/shared/libs/buffer/index.ts | 2 + src/shared/services/content/index.ts | 1 + src/shared/stores/root/index.ts | 2 +- src/shared/ui/hashtag-input/index.tsx | 42 +++++-- vite.config.ts | 17 ++- yarn.lock | 35 ++++-- 16 files changed, 336 insertions(+), 89 deletions(-) create mode 100644 .env create mode 100644 src/shared/libs/buffer/index.ts diff --git a/.env b/.env new file mode 100644 index 0000000..c1948b4 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VITE_API_BASE_URL=https://my-public-node-1.projscale.dev/api/v1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index a71e418..948dfab 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules .DS_Store dist +.env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2324b9b..689d7c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@tonconnect/ui-react": "^2.0.2", "@vkruglikov/react-telegram-web-app": "^2.1.9", "axios": "^1.6.7", + "buffer": "^6.0.3", "clsx": "^2.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -2054,6 +2055,26 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/big-integer": { "version": "1.6.52", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", @@ -2139,6 +2160,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -3621,6 +3666,26 @@ "react-is": "^16.7.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", diff --git a/package.json b/package.json index 6dd29bb..f4b588b 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@tonconnect/ui-react": "^2.0.2", "@vkruglikov/react-telegram-web-app": "^2.1.9", "axios": "^1.6.7", + "buffer": "^6.0.3", "clsx": "^2.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/app/index.tsx b/src/app/index.tsx index 4833d7a..6c66e95 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,4 +1,5 @@ import "~/app/styles/globals.css"; +import '~/shared/libs/buffer'; import { useEffect } from "react"; import { useExpand, useWebApp } from "@vkruglikov/react-telegram-web-app"; diff --git a/src/app/styles/globals.css b/src/app/styles/globals.css index bbf4d90..ea8f082 100644 --- a/src/app/styles/globals.css +++ b/src/app/styles/globals.css @@ -26,18 +26,26 @@ /*Input Range*/ /* Custom styles for the range input */ input[type="range"] { - -webkit-appearance: none; /* Remove default appearance */ - width: 100%; /* Full width */ - height: 2px; /* Track height */ - background: linear-gradient( - to right, - white 0%, /* Start color of passed track */ - white var(--value-percentage, 0%), /* End color of passed track */ - gray var(--value-percentage, 0%), /* Start color of remaining track */ - gray 100% /* End color of remaining track */ - ); - border-radius: 9999px; /* Rounded-full track */ - outline: none; /* Remove outline */ + -webkit-appearance: none; + /* Remove default appearance */ + width: 100%; + /* Full width */ + height: 2px; + /* Track height */ + background: linear-gradient(to right, + white 0%, + /* Start color of passed track */ + white var(--value-percentage, 0%), + /* End color of passed track */ + gray var(--value-percentage, 0%), + /* Start color of remaining track */ + gray 100% + /* End color of remaining track */ + ); + border-radius: 9999px; + /* Rounded-full track */ + outline: none; + /* Remove outline */ transition: background 0.3s; } @@ -45,45 +53,64 @@ input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; - width: 9px; /* Thumb width */ - height: 9px; /* Thumb height */ - background: #fff; /* Thumb color (blue-500) */ - border-radius: 9999px; /* Rounded-full thumb */ - cursor: pointer; /* Pointer cursor on hover */ + width: 9px; + /* Thumb width */ + height: 9px; + /* Thumb height */ + background: #fff; + /* Thumb color (blue-500) */ + border-radius: 9999px; + /* Rounded-full thumb */ + cursor: pointer; + /* Pointer cursor on hover */ transition: background 0.3s; } /* For Firefox */ input[type="range"]::-moz-range-thumb { appearance: none; - width: 9px; /* Thumb width */ - height: 9px; /* Thumb height */ - background: #fff; /* Thumb color (blue-500) */ - border-radius: 9999px; /* Rounded-full thumb */ - cursor: pointer; /* Pointer cursor on hover */ + width: 9px; + /* Thumb width */ + height: 9px; + /* Thumb height */ + background: #fff; + /* Thumb color (blue-500) */ + border-radius: 9999px; + /* Rounded-full thumb */ + cursor: pointer; + /* Pointer cursor on hover */ transition: background 0.3s; } input[type="range"]::-moz-range-track { - -webkit-appearance: none; /* Remove default appearance */ - width: 100%; /* Full width */ - height: 2px; /* Track height */ - background: linear-gradient( - to right, - white 0%, /* Start color of passed track */ - white var(--value-percentage, 0%), /* End color of passed track */ - gray var(--value-percentage, 0%), /* Start color of remaining track */ - gray 100% /* End color of remaining track */ - ); - border-radius: 9999px; /* Rounded-full track */ - outline: none; /* Remove outline */ + -webkit-appearance: none; + /* Remove default appearance */ + width: 100%; + /* Full width */ + height: 2px; + /* Track height */ + background: linear-gradient(to right, + white 0%, + /* Start color of passed track */ + white var(--value-percentage, 0%), + /* End color of passed track */ + gray var(--value-percentage, 0%), + /* Start color of remaining track */ + gray 100% + /* End color of remaining track */ + ); + border-radius: 9999px; + /* Rounded-full track */ + outline: none; + /* Remove outline */ transition: background 0.3s; } /* English comment: override react-tag-input classes */ .ReactTags__tagInputField { - @apply bg-transparent text-gray placeholder:text-gray outline-none w-full h-8; - @apply border border-white py-[20px] !important; + @apply bg-[#2B2B2B] outline-none w-full h-8 text-sm !important; + @apply border border-white px-[10px] py-[18px] !important; + @apply whitespace-pre !important; /* English comment: 'bg-transparent' to blend with your dark background 'text-white' to have white text @@ -94,7 +121,7 @@ /* English comment: style for the tag itself when it's rendered */ .ReactTags__selected .ReactTags__tag { - @apply bg-[#363636] text-white text-sm inline-flex items-center px-2 py-1 rounded mr-1; + @apply bg-[#363636] text-white text-sm inline-flex items-center px-2 py-1 mb-2 rounded mr-1 !important; /* English comment: 'bg-[#363636]' to have a dark gray background 'text-white' keeps the text white @@ -105,11 +132,11 @@ } .ReactTags__selected .ReactTags__remove { - @apply ml-1 text-gray hover:text-white cursor-pointer; + @apply ml-1 text-gray hover:text-white cursor-pointer !important; /* English comment: 'ml-1' a small margin to separate the 'x' or close symbol 'text-gray-400' by default, and change to white on hover 'cursor-pointer' so it looks clickable */ } -} +} \ No newline at end of file diff --git a/src/pages/root/steps/presubmit-step/index.tsx b/src/pages/root/steps/presubmit-step/index.tsx index db44819..c9d986c 100644 --- a/src/pages/root/steps/presubmit-step/index.tsx +++ b/src/pages/root/steps/presubmit-step/index.tsx @@ -64,7 +64,7 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => { content: fileUploadResult.content_id_v1, image: coverUploadResult.content_id_v1, price: String(rootStore.price * 10 ** 9), - + hashtags: rootStore.hashtags, royaltyParams: rootStore.royalty.map((member) => ({ ...member, value: member.value * 100, @@ -151,6 +151,24 @@ export const PresubmitStep = ({ prevStep }: PresubmitStepProps) => { )} + {rootStore.author && ( + +
+ {rootStore.hashtags.map((tag, index) => ( +
+ {tag} +
+ ))} +
+
+ )} +
{ const formSchema = useMemo(() => { const parsePrice = (value: unknown) => { - if (typeof value === "string") { - // Replace commas with dots and parse the value - const parsedValue = parseFloat(value.replace(",", ".")); + if (typeof value === "string" || typeof value === "number") { + const stringValue = value.toString().replace(",", "."); + const parsedValue = parseFloat(stringValue); return isNaN(parsedValue) ? undefined : parsedValue; } return undefined; @@ -37,10 +37,10 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => { return z.object({ price: z.preprocess( parsePrice, - z.number().min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`) + z.number({required_error: 'Цена не соответствует требованиям'}).min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`) ), resaleLicensePrice: z - .preprocess(parsePrice, z.number().min(MIN_RESALE_PRICE, `Цена копии должна быть минимум ${MIN_RESALE_PRICE} TON.`)) + .preprocess(parsePrice, z.number({required_error: 'Цена не соответствует требованиям'}).min(MIN_RESALE_PRICE, `Цена копии должна быть минимум ${MIN_RESALE_PRICE} TON.`)) .optional(), }); } @@ -48,7 +48,7 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => { return z.object({ price: z.preprocess( parsePrice, - z.number().min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`) + z.number({required_error: 'Цена не соответствует требованиям'}).min(MIN_PRICE, `Цена должна быть минимум ${MIN_PRICE} TON.`) ), }); }, [rootStore.allowResale]); @@ -59,9 +59,9 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => { resolver: zodResolver(formSchema), mode: "onChange", defaultValues: { - price: rootStore.price, + price: rootStore.price || MIN_PRICE, //@ts-expect-error Fix typings - resaleLicensePrice: rootStore?.licenseResalePrice, + resaleLicensePrice: rootStore?.licenseResalePrice || MIN_RESALE_PRICE, }, }); @@ -98,13 +98,22 @@ export const PriceStep = ({ nextStep, prevStep }: PriceStepProps) => {

Минимальная стоимость {MIN_PRICE} TON.

-

Рекомендуемая стоимость {RECOMMENDED_PRICE} TON.

+ {/*

Рекомендуемая стоимость {RECOMMENDED_PRICE} TON.

*/}
+ inputMode="decimal" + pattern="[0-9]*[.,]?[0-9]*" + {...form.register("price", { + onChange: (e) => { + const value = e.target.value; + if (!/^\d*[.,]?\d*$/.test(value)) { + e.target.value = value.replace(/[^\d.,]/g, ''); + } + } + })} + />