diff --git a/package.json b/package.json index 028206c7..78fb06f6 100644 --- a/package.json +++ b/package.json @@ -145,6 +145,7 @@ "remark": "^15.0.1", "remark-gfm": "^4.0.0", "remark-html": "^16.0.1", + "rss-parser": "^3.13.0", "sharp": "^0.33.4", "sonner": "^1.4.41", "tailwind-merge": "^2.2.0", diff --git a/src/common/components/atoms/reorderable-tab.tsx b/src/common/components/atoms/reorderable-tab.tsx index c432d457..714c484d 100644 --- a/src/common/components/atoms/reorderable-tab.tsx +++ b/src/common/components/atoms/reorderable-tab.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { motion, Reorder } from "framer-motion"; import { CloseIcon } from "./icons/CloseIcon"; import EditableText from "./editable-text"; -import { FaX } from "react-icons/fa6"; interface Props { tabName: string; diff --git a/src/common/components/molecules/DaoSelector.tsx b/src/common/components/molecules/DaoSelector.tsx index f45e3067..b7d33c02 100644 --- a/src/common/components/molecules/DaoSelector.tsx +++ b/src/common/components/molecules/DaoSelector.tsx @@ -1,13 +1,10 @@ import React from "react"; import { Select, - SelectGroup, SelectValue, SelectTrigger, SelectContent, - SelectLabel, SelectItem, - SelectSeparator, } from "@/common/components/atoms/select"; import { DAO_OPTIONS } from "@/constants/basedDaos"; diff --git a/src/common/components/molecules/ImageScaleSlider.tsx b/src/common/components/molecules/ImageScaleSlider.tsx new file mode 100644 index 00000000..d84357be --- /dev/null +++ b/src/common/components/molecules/ImageScaleSlider.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import { Slider } from "@mui/material"; + +export type ImageScaleSliderProps = { + min: number; + max: number; + step?: number; + defaultValue?: number; + onChange: (value: number) => void; +}; + +const ImageScaleSlider: React.FC = ({ + min = 0.2, + max = 3, + step = 0.1, + defaultValue = 1, + onChange, +}) => { + return ( +
+ onChange(value as number)} + /> +
+ ); +}; + +export default ImageScaleSlider; diff --git a/src/common/components/molecules/LinksInput.tsx b/src/common/components/molecules/LinksInput.tsx index c2a69a27..b0cdfb69 100644 --- a/src/common/components/molecules/LinksInput.tsx +++ b/src/common/components/molecules/LinksInput.tsx @@ -94,12 +94,12 @@ const LinksInput = forwardRef( const newLink = { text: "New Link", url: "https://", - avatar: "/images/chainEmoji.png", + avatar: "https://www.nounspace.com/images/chainEmoji.png", description: "Description", }; onChange?.([...value, newLink]); - setVisibleFields([...visibleFields, true]); // Automatically expand new link + setVisibleFields([...visibleFields, true]); }; const removeLink = (index: number) => { @@ -133,8 +133,9 @@ const LinksInput = forwardRef( value={link.url} onChange={(e: any) => { handleLinkChange(index, { ...link, url: e.target.value }); - showAdditionalFields(index); // Show fields when URL is updated + showAdditionalFields(index); }} + onFocus={() => showAdditionalFields(index)} />

( - + {overlay && open && ( diff --git a/src/common/components/organisms/Navigation.tsx b/src/common/components/organisms/Navigation.tsx index 33ce0475..8705043c 100644 --- a/src/common/components/organisms/Navigation.tsx +++ b/src/common/components/organisms/Navigation.tsx @@ -163,10 +163,10 @@ const Navigation: React.FC = ({ isEditable, enterEditMode }) => { - + setShowCastModal(false)} />

@@ -213,7 +213,7 @@ const Navigation: React.FC = ({ isEditable, enterEditMode }) => { trackAnalyticsEvent(AnalyticsEvent.CLICK_SPACE_FAIR_LAUNCH) } diff --git a/src/common/components/organisms/NogsChecker.tsx b/src/common/components/organisms/NogsChecker.tsx index 1fdca0ce..eb11508b 100644 --- a/src/common/components/organisms/NogsChecker.tsx +++ b/src/common/components/organisms/NogsChecker.tsx @@ -21,7 +21,7 @@ export default function NogsChecker() { return ( <>

- Tabs are only for early supporters holding a nounspace OG NFT (nOGs){" "} + For now Tabs are only for early supporters holding a nounspace OG NFT (nOGs){" "}
Mint a pair{" "} {inEditMode ? ( -

+
- {isNil(profile) && ( )} +
diff --git a/src/common/components/templates/SpaceLoading.tsx b/src/common/components/templates/SpaceLoading.tsx index 992fbc97..dd49d653 100644 --- a/src/common/components/templates/SpaceLoading.tsx +++ b/src/common/components/templates/SpaceLoading.tsx @@ -1,15 +1,17 @@ -import React, { ReactNode, useMemo } from "react"; +import React, { ReactNode, useState, useEffect } from "react"; import { isUndefined } from "lodash"; import useWindowSize from "@/common/lib/hooks/useWindowSize"; export default function SpaceLoading({ profile }: { profile?: ReactNode }) { + const [rowHeight, setRowHeight] = useState(70); + const { height } = useWindowSize(); const maxRows = 12; const cols = 12; const margin = [16, 16]; const containerPadding = [16, 16]; - const { height } = useWindowSize(); - const rowHeight = useMemo( - () => + + useEffect(() => { + setRowHeight( height ? Math.round( // The 64 magic number here is the height of the tabs bar above the grid @@ -17,8 +19,9 @@ export default function SpaceLoading({ profile }: { profile?: ReactNode }) { maxRows, ) : 70, - [height], - ); + ), + [height]; + }); return ( <> diff --git a/src/common/fidgets/FidgetWrapper.tsx b/src/common/fidgets/FidgetWrapper.tsx index 1da7842f..971749c1 100644 --- a/src/common/fidgets/FidgetWrapper.tsx +++ b/src/common/fidgets/FidgetWrapper.tsx @@ -70,7 +70,6 @@ export function FidgetWrapper({ homebaseConfig: state.homebase.homebaseConfig, })); - console.log(homebaseConfig); function onClickEdit() { setSelectedFidgetID(bundle.id); setCurrentFidgetSettings( diff --git a/src/common/fidgets/index.d.ts b/src/common/fidgets/index.d.ts index dcb70b3f..c9a5dff9 100644 --- a/src/common/fidgets/index.d.ts +++ b/src/common/fidgets/index.d.ts @@ -4,7 +4,8 @@ import HTMLInput from "@/common/ui/molecules/HTMLInput"; import ColorSelector from "@/common/components/molecules/ColorSelector"; import FontSelector from "@/common/components/molecules/FontSelector"; import type { ThemeSettings, FontFamily, Color } from "@/common/lib/theme"; - +import SwitchButton from "../components/molecules/ViewSelector"; +import ImageScaleSlider from "@/common/components/molecules/ImageScaleSlider"; export type FidgetSettings = Record; export type FidgetSettingsStyle = { background?: Color; @@ -15,6 +16,9 @@ export type FidgetSettingsStyle = { fidgetBorderWidth?: string; fidgetBorderColor?: Color; fidgetShadow?: string; + itemBorderWidth?: string; + itemBorderColor?: Color; + itemBackground?: Color; }; export type FidgetData = Record; @@ -38,7 +42,9 @@ export type FidgetFieldConfig = { | typeof ColorSelector | typeof FontSelector | typeof CSSInput - | typeof HTMLInput; + | typeof HTMLInput + | typeof ImageScaleSlider + | typeof SwitchButton; // change the name of ViewSelector.tsx file in next link fidget PR readonly default?: any; readonly required: boolean; readonly group?: FidgetGroup; diff --git a/src/common/lib/theme/ThemeCard.tsx b/src/common/lib/theme/ThemeCard.tsx index 5db97ba0..379a1665 100644 --- a/src/common/lib/theme/ThemeCard.tsx +++ b/src/common/lib/theme/ThemeCard.tsx @@ -25,7 +25,7 @@ export const ThemeCard = ({ return (
{/* Templates Dropdown */} - - {/* Theme Card Example */} - + +
+
+ {/* Theme Card Example */} + +
+
+ +
+
{THEMES.map((theme, i) => ( diff --git a/src/common/lib/utils/generateUserMetadataHtml.tsx b/src/common/lib/utils/generateUserMetadataHtml.tsx index 3e742a01..9a6aafb2 100644 --- a/src/common/lib/utils/generateUserMetadataHtml.tsx +++ b/src/common/lib/utils/generateUserMetadataHtml.tsx @@ -31,7 +31,6 @@ export const generateUserMetadataHtml = (userMetadata?: UserMetadata) => { - {bio && ( <> @@ -41,9 +40,9 @@ export const generateUserMetadataHtml = (userMetadata?: UserMetadata) => { )} {pfpUrl && ( <> - - + + )} diff --git a/src/common/lib/utils/markdownRenderers.tsx b/src/common/lib/utils/markdownRenderers.tsx index a55ed7a3..0d735805 100644 --- a/src/common/lib/utils/markdownRenderers.tsx +++ b/src/common/lib/utils/markdownRenderers.tsx @@ -14,7 +14,7 @@ type RendererProps = MarkdownProps & { href?: any; }; -export const MarkdownRenderers = { +export const MarkdownRenderers = (linkColor?: string) => ({ img: ({ alt, src, title, ...props }: RendererProps) => ( ), - a: ({ href, children, ...props }: RendererProps) => ( - - {children} - - ), + a: ({ href, children, ...props }: RendererProps) => { + const isPrettyLink = href !== children; + + const style: React.CSSProperties = isPrettyLink + ? { color: linkColor, wordBreak: "keep-all", overflowWrap: "normal" } + : { + color: linkColor, + wordBreak: "break-all", + overflowWrap: "break-word", + }; + + return ( + + {children} + + ); + }, + h1: ({ children, ...props }: RendererProps) => (

), -}; +}); diff --git a/src/constants/intialHomebase.ts b/src/constants/intialHomebase.ts index de68ab01..95e9af12 100644 --- a/src/constants/intialHomebase.ts +++ b/src/constants/intialHomebase.ts @@ -1,6 +1,5 @@ import { SpaceConfig } from "@/common/components/templates/Space"; import DEFAULT_THEME from "@/common/lib/theme/defaultTheme"; -import { FeedType } from "@neynar/nodejs-sdk"; const layoutID = ""; const INITIAL_HOMEBASE_CONFIG: SpaceConfig = { diff --git a/src/fidgets/farcaster/Feed.tsx b/src/fidgets/farcaster/Feed.tsx index 66b6d37d..3658d051 100644 --- a/src/fidgets/farcaster/Feed.tsx +++ b/src/fidgets/farcaster/Feed.tsx @@ -93,7 +93,7 @@ const feedProperties: FidgetProperties = { displayName: "Select App", inputSelector: PlatformSelector, required: false, - default: { name: "farcaster", icon: "/images/farcaster.jpeg" }, + default: { name: "Farcaster", icon: "/images/farcaster.jpeg" }, }, { fieldName: "feedType", @@ -109,7 +109,7 @@ const feedProperties: FidgetProperties = { displayName: "Username", inputSelector: TextInput, required: false, - disabledIf: (settings) => settings.selectPlatform.name === "Farcaster", + disabledIf: (settings) => settings.selectPlatform.name === "farcaster", default: "thenounspace", }, { @@ -206,7 +206,7 @@ export const FEED_TYPES = [ const Feed: React.FC> = ({ settings }) => { const { - selectPlatform = { name: "farcaster", icon: "/images/farcaster.jpeg" }, + selectPlatform = { name: "Farcaster", icon: "/images/farcaster.jpeg" }, Xhandle, style, } = settings; diff --git a/src/fidgets/farcaster/Frame.tsx b/src/fidgets/farcaster/Frame.tsx index e33c35f1..3680c971 100644 --- a/src/fidgets/farcaster/Frame.tsx +++ b/src/fidgets/farcaster/Frame.tsx @@ -9,7 +9,6 @@ import { import FrameEmbed from "./components/Embeds/FrameEmbed"; import { isValidUrl } from "@/common/lib/utils/url"; import useSafeUrl from "@/common/lib/hooks/useSafeUrl"; -import { defaultStyleFields } from "@/fidgets/helpers"; import ColorSelector from "@/common/components/molecules/ColorSelector"; import BorderSelector from "@/common/components/molecules/BorderSelector"; import ShadowSelector from "@/common/components/molecules/ShadowSelector"; diff --git a/src/fidgets/farcaster/components/CastRow.tsx b/src/fidgets/farcaster/components/CastRow.tsx index 46496de5..92cae368 100644 --- a/src/fidgets/farcaster/components/CastRow.tsx +++ b/src/fidgets/farcaster/components/CastRow.tsx @@ -4,7 +4,6 @@ import { mergeClasses as classNames } from "@/common/lib/utils/mergeClasses"; import { ArrowPathRoundedSquareIcon, ArrowTopRightOnSquareIcon, - ChatBubbleLeftIcon, HeartIcon, ChatBubbleLeftRightIcon, } from "@heroicons/react/24/outline"; @@ -381,11 +380,12 @@ const CastReactions = ({ cast }: { cast: CastWithInteractions }) => { const reactions = getReactions(); - const linksCount = cast?.embeds ? cast.embeds.length : 0; - const isOnchainLink = - linksCount > 0 && "url" in cast.embeds[0] - ? cast.embeds[0].url.startsWith("chain:") - : false; + // const linksCount = cast?.embeds ? cast.embeds.length : 0; + + // const isOnchainLink = + // linksCount > 0 && "url" in cast.embeds[0] + // ? cast.embeds[0].url.startsWith("chain:") + // : false; return ( <> diff --git a/src/fidgets/farcaster/components/CreateCast.tsx b/src/fidgets/farcaster/components/CreateCast.tsx index d74d0997..6d52d3e8 100644 --- a/src/fidgets/farcaster/components/CreateCast.tsx +++ b/src/fidgets/farcaster/components/CreateCast.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useEditor, EditorContent } from "@mod-protocol/react-editor"; import { EmbedsEditor } from "@mod-protocol/react-ui-shadcn/dist/lib/embeds"; import { @@ -10,7 +10,7 @@ import { } from "@mod-protocol/core"; import { getFarcasterMentions, - getFarcasterChannels, + // getFarcasterChannels, formatPlaintextToHubCastMessage, getMentionFidsByUsernames, } from "@mod-protocol/farcaster"; @@ -34,21 +34,20 @@ import { PhotoIcon } from "@heroicons/react/20/solid"; import { FarcasterEmbed, isFarcasterUrlEmbed } from "@mod-protocol/farcaster"; import { CastType, Signer } from "@farcaster/core"; import { useFarcasterSigner } from ".."; -import { submitCast } from "../utils"; +import { + fetchChannelsByName, + fetchChannelsForUser, + submitCast, +} from "../utils"; import { bytesToHex } from "@noble/ciphers/utils"; const API_URL = process.env.NEXT_PUBLIC_MOD_PROTOCOL_API_URL!; const getMentions = getFarcasterMentions(API_URL); + const debouncedGetMentions = debounce(getMentions, 200, { leading: true, trailing: false, }); -const getChannels = getFarcasterChannels(API_URL); -const debouncedGetChannels = debounce(getChannels, 200, { - leading: true, - trailing: false, -}); - const getUrlMetadata = fetchUrlMetadata(API_URL); const getMentionFids = getMentionFidsByUsernames(API_URL); @@ -82,6 +81,7 @@ export type DraftType = { type CreateCastProps = { initialDraft?: Partial; + afterSubmit?: () => void; }; export type ModProtocolCastAddBody = Exclude< @@ -117,7 +117,10 @@ async function publishPost(draft: DraftType, fid: number, signer: Signer) { } } -const CreateCast: React.FC = ({ initialDraft }) => { +const CreateCast: React.FC = ({ + initialDraft, + afterSubmit = () => {}, +}) => { const [currentMod, setCurrentMod] = useState(null); const [initialEmbeds, setInitialEmbeds] = useState(); const [draft, setDraft] = useState({ @@ -131,10 +134,26 @@ const CreateCast: React.FC = ({ initialDraft }) => { const { signer, isLoadingSigner, fid } = useFarcasterSigner("create-cast"); + const debouncedGetChannels = useCallback( + debounce( + async (query: string) => { + if (query && query !== "") { + return await fetchChannelsByName(query); + } else { + return await fetchChannelsForUser(fid); + } + }, + 200, + { leading: true, trailing: false }, + ), + [fid], + ); + const onSubmitPost = async (): Promise => { if ((!draft?.text && !draft?.embeds?.length) || isUndefined(signer)) return false; await publishPost(draft, fid, signer); + afterSubmit(); return true; }; diff --git a/src/fidgets/farcaster/utils.ts b/src/fidgets/farcaster/utils.ts index 118005c5..7bae5bc9 100644 --- a/src/fidgets/farcaster/utils.ts +++ b/src/fidgets/farcaster/utils.ts @@ -28,6 +28,7 @@ import { mnemonicToAccount } from "viem/accounts"; import { optimismChaninClient } from "@/constants/optimismChainClient"; import axiosBackend from "@/common/data/api/backend"; import { ModProtocolCastAddBody } from "./components/CreateCast"; +import { type Channel } from "@mod-protocol/farcaster"; export const WARPCAST_RECOVERY_PROXY: `0x${string}` = "0x00000000FcB080a4D6c39a9354dA9EB9bC104cd7"; @@ -404,3 +405,31 @@ export const getSignatureForUsernameProof = async ( }); return signature; }; + +export async function fetchChannelsForUser( + fid: number, + limit: number = 20, +): Promise { + try { + const channelsResponse = await axiosBackend.get( + `/api/farcaster/neynar/active-channels/?limit=${limit}&fid=${fid}`, + ); + return channelsResponse.data.channels as Channel[]; + } catch (e) { + return [] as Channel[]; + } +} + +export async function fetchChannelsByName( + query: string, + limit: number = 20, +): Promise { + try { + const channelsResponse = await axiosBackend.get( + `/api/farcaster/neynar/search-channels?limit=${limit}&q=${query}`, + ); + return channelsResponse.data.channels as Channel[]; + } catch (e) { + return [] as Channel[]; + } +} diff --git a/src/fidgets/helpers.tsx b/src/fidgets/helpers.tsx index ea2dcb1d..c5dafa5d 100644 --- a/src/fidgets/helpers.tsx +++ b/src/fidgets/helpers.tsx @@ -34,3 +34,16 @@ export const defaultStyleFields: FidgetFieldConfig[] = [ group: "style", }, ]; + +export const transformUrl = (url: string) => { + if (url && url.match(/youtube\.com\/watch\?v=/)) { + return url.replace("watch?v=", "embed/"); + } + if (url && url.match(/vimeo\.com\/\d+/)) { + return url.replace("vimeo.com", "player.vimeo.com/video"); + } + if (url && url.match(/odysee\.com\/@/)) { + return url.replace("odysee.com", "odysee.com/$/embed"); + } + return url; +}; diff --git a/src/fidgets/index.ts b/src/fidgets/index.ts index bb5c83c0..7da82e50 100644 --- a/src/fidgets/index.ts +++ b/src/fidgets/index.ts @@ -12,6 +12,9 @@ import Feed from "./farcaster/Feed"; import CreateCast from "./farcaster/CreateCast"; import Links from "./ui/Links"; import snapShot from "./snapshot/SnapShot"; +// import Swap from "./swap/Swap"; +import rss from "./ui/rss"; +import VideoFidget from "./ui/Video"; export const CompleteFidgets = { // @@ -33,6 +36,9 @@ export const CompleteFidgets = { links: Links, // zora: zoraEmbed, -> 500 server error -Frame ancestors block SnapShot: snapShot, + // Swap: Swap, + Rss: rss, + Video: VideoFidget, }; export const LayoutFidgets = { diff --git a/src/fidgets/layout/Grid.tsx b/src/fidgets/layout/Grid.tsx index b564c0af..8d3e3553 100644 --- a/src/fidgets/layout/Grid.tsx +++ b/src/fidgets/layout/Grid.tsx @@ -24,7 +24,6 @@ import { analytics, AnalyticsEvent, } from "@/common/providers/AnalyticsProvider"; -import TabBar from "@/common/components/organisms/TabBar"; import AddFidgetIcon from "@/common/components/atoms/icons/AddFidget"; export const resizeDirections = ["s", "w", "e", "n", "sw", "nw", "se", "ne"]; diff --git a/src/fidgets/snapshot/components/ProposalItem.tsx b/src/fidgets/snapshot/components/ProposalItem.tsx index b708f921..f19eca03 100644 --- a/src/fidgets/snapshot/components/ProposalItem.tsx +++ b/src/fidgets/snapshot/components/ProposalItem.tsx @@ -196,7 +196,7 @@ const ProposalItem: React.FC = ({ proposal, space }) => { {proposal.body} diff --git a/src/fidgets/ui/IFrame.tsx b/src/fidgets/ui/IFrame.tsx index 734721c2..e641e8a5 100644 --- a/src/fidgets/ui/IFrame.tsx +++ b/src/fidgets/ui/IFrame.tsx @@ -9,9 +9,8 @@ import { import { isValidUrl } from "@/common/lib/utils/url"; import useSafeUrl from "@/common/lib/hooks/useSafeUrl"; import { defaultStyleFields } from "@/fidgets/helpers"; -import WidthSlider from "@/common/components/molecules/ScaleSliderSelector"; import IFrameWidthSlider from "@/common/components/molecules/IframeScaleSlider"; - +import { transformUrl } from "@/fidgets/helpers"; export type IFrameFidgetSettings = { url: string; size: number; @@ -69,7 +68,7 @@ const IFrame: React.FC> = ({ }) => { const isValid = isValidUrl(url); const sanitizedUrl = useSafeUrl(url, DISALLOW_URL_PATTERNS); - + const transformedUrl = transformUrl(sanitizedUrl || ""); if (!url) { return ; } @@ -78,7 +77,7 @@ const IFrame: React.FC> = ({ return ; } - if (!sanitizedUrl) { + if (!transformedUrl) { return ( > = ({ return (