From 805a6fa52fcd9e24a9477315cfa541a80bc37ec2 Mon Sep 17 00:00:00 2001 From: Joseph Chalabi Date: Thu, 5 Dec 2024 12:28:21 -0700 Subject: [PATCH] fix: uri yup extension --- components/factory/components/DenomImage.tsx | 8 +++-- .../factory/modals/updateDenomMetadata.tsx | 6 +++- next.config.js | 5 ++++ styles/globals.css | 21 +++++++++++++ utils/yupExtensions.ts | 30 +++++++++++++++++++ 5 files changed, 66 insertions(+), 4 deletions(-) diff --git a/components/factory/components/DenomImage.tsx b/components/factory/components/DenomImage.tsx index f9a72e6c..82f1e473 100644 --- a/components/factory/components/DenomImage.tsx +++ b/components/factory/components/DenomImage.tsx @@ -2,7 +2,7 @@ import ProfileAvatar from '@/utils/identicon'; import Image from 'next/image'; import { useState, useEffect } from 'react'; -const supportedDomains = [ +export const supportedDomains = [ 'imgur.com', 'i.imgur.com', 'cloudfront.net', @@ -24,9 +24,10 @@ const supportedDomains = [ 'upload.wikimedia.org', 'unsplash.com', 'istockphoto.com', + 't4.ftcdn.net', ]; -const supportedPatterns = [ +export const supportedPatterns = [ /^https:\/\/.*\.s3\.amazonaws\.com/, /^https:\/\/.*\.storage\.googleapis\.com/, /^https:\/\/.*\.cloudinary\.com/, @@ -49,9 +50,10 @@ const supportedPatterns = [ /^https:\/\/.*\.twimg\.com/, /^https:\/\/.*\.pinimg\.com/, /^https:\/\/.*\.giphy\.com/, + /^https:\/\/.*\.t4\.ftcdn\.net/, ]; -const isUrlSupported = (url: string) => { +export const isUrlSupported = (url: string) => { try { const { hostname } = new URL(url); return ( diff --git a/components/factory/modals/updateDenomMetadata.tsx b/components/factory/modals/updateDenomMetadata.tsx index f3846a80..0356414c 100644 --- a/components/factory/modals/updateDenomMetadata.tsx +++ b/components/factory/modals/updateDenomMetadata.tsx @@ -15,7 +15,11 @@ const TokenDetailsSchema = Yup.object().shape({ description: Yup.string() .min(10, 'Description must be at least 10 characters long') .noProfanity(), - uri: Yup.string().url('Must be a valid URL'), + uri: Yup.string() + .url('Must be a valid URL') + .matches(/^https:\/\//i, 'URL must use HTTPS protocol') + .matches(/\.(jpg|jpeg|png|gif)$/i, 'URL must point to an image file') + .supportedImageUrl(), }); export function UpdateDenomMetadataModal({ diff --git a/next.config.js b/next.config.js index 25c61024..d5266d1e 100644 --- a/next.config.js +++ b/next.config.js @@ -22,6 +22,7 @@ const nextConfig = { 'media.istockphoto.com', 'upload.wikimedia.org', 'istockphoto.com', + 't4.ftcdn.net', ], remotePatterns: [ { @@ -76,6 +77,10 @@ const nextConfig = { protocol: 'https', hostname: '*.upload.wikimedia.org', }, + { + protocol: 'https', + hostname: '*.t4.ftcdn.net', + }, ], }, }; diff --git a/styles/globals.css b/styles/globals.css index 44b173fe..49ccfc44 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -32,3 +32,24 @@ input:-webkit-autofill:active { [data-toast-container='true'] { z-index: 100000; } + +.tooltip { + position: relative; + cursor: help; +} + +.tooltip[data-tooltip]:hover::after { + content: attr(data-tooltip); + position: absolute; + bottom: 100%; + left: 0; + background: #333; + color: white; + padding: 8px; + border-radius: 4px; + font-size: 14px; + white-space: pre-wrap; + width: max-content; + max-width: 300px; + z-index: 1000; +} diff --git a/utils/yupExtensions.ts b/utils/yupExtensions.ts index 89ede4ae..e2de151e 100644 --- a/utils/yupExtensions.ts +++ b/utils/yupExtensions.ts @@ -1,6 +1,7 @@ import * as Yup from 'yup'; import { containsProfanity } from '@/utils/profanityFilter'; import { bech32 } from '@scure/base'; // Updated import +import { supportedDomains, supportedPatterns } from '@/components/factory/components/DenomImage'; declare module 'yup' { interface StringSchema { @@ -8,6 +9,7 @@ declare module 'yup' { manifestAddress(message?: string): this; simulateDenomCreation(simulateFn: () => Promise, message?: string): this; simulateDenomMetadata(simulateFn: () => Promise, message?: string): this; + supportedImageUrl(message?: string): this; } } @@ -115,4 +117,32 @@ Yup.addMethod(Yup.string, 'manifestAddress', function (message }); }); +Yup.addMethod(Yup.string, 'supportedImageUrl', function (message) { + return this.test('supported-image-url', message, function (value) { + const { path, createError } = this; + if (!value) return true; + + try { + const { hostname } = new URL(value); + const isSupported = + supportedDomains.includes(hostname) || + supportedPatterns.some(pattern => pattern.test(value)); + + return ( + isSupported || + createError({ + path, + message: + message || `URL domain is not supported. Please use one of the supported domains`, + }) + ); + } catch { + return createError({ + path, + message: message || 'Invalid URL format', + }); + } + }); +}); + export default Yup;