diff --git a/package.json b/package.json index 2d0b1b39..b780574b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Nounspace", - "version": "0.0.10", + "version": "0.0.11", "license": "", "private": true, "scripts": { @@ -117,7 +117,7 @@ "moment": "^2.30.1", "mutative": "^1.0.3", "neverthrow": "^6.2.2", - "next": "14.1", + "next": "14.2", "next-themes": "^0.3.0", "pino-pretty": "^11.2.0", "prop-types": "^15.8.1", diff --git a/src/common/components/atoms/alert.tsx b/src/common/components/atoms/alert.tsx index 637044d0..98efb6dd 100644 --- a/src/common/components/atoms/alert.tsx +++ b/src/common/components/atoms/alert.tsx @@ -7,8 +7,7 @@ const alertVariants = cva( { variants: { variant: { - default: - "border bg-background text-foreground/80 [&>svg]:text-gray-400", + default: "border bg-background opacity-80 [&>svg]:text-gray-400", destructive: "text-red-700 bg-red-100", }, }, diff --git a/src/common/components/organisms/SearchAutocompleteInput.tsx b/src/common/components/organisms/SearchAutocompleteInput.tsx index 7676dcd1..9b32c86c 100644 --- a/src/common/components/organisms/SearchAutocompleteInput.tsx +++ b/src/common/components/organisms/SearchAutocompleteInput.tsx @@ -72,7 +72,7 @@ const SearchAutocompleteInput: React.FC = ({ >
-
{`Search for "${query}"`}
+
{`Search for "${query}"`}
)} @@ -89,12 +89,8 @@ const SearchAutocompleteInput: React.FC = ({
-

- {user.display_name} -

-

- @{user.username} -

+

{user.display_name}

+

@{user.username}

))} diff --git a/src/common/lib/utils/markdownRenderers.tsx b/src/common/lib/utils/markdownRenderers.tsx index 0d735805..b8bfc1f8 100644 --- a/src/common/lib/utils/markdownRenderers.tsx +++ b/src/common/lib/utils/markdownRenderers.tsx @@ -57,7 +57,13 @@ export const MarkdownRenderers = (linkColor?: string) => ({ }; return ( - + {children} ); diff --git a/src/constants/intialHomebase.ts b/src/constants/intialHomebase.ts index 95e9af12..606833b5 100644 --- a/src/constants/intialHomebase.ts +++ b/src/constants/intialHomebase.ts @@ -1,17 +1,94 @@ import { SpaceConfig } from "@/common/components/templates/Space"; import DEFAULT_THEME from "@/common/lib/theme/defaultTheme"; +const tutorialText = ` + ## To start customizing, click the paintbrush in the bottom left next to 'Cast' ⬋🖌️⬋ + +### Add Fidgets +From customization mode, click the big blue **+** button. Then, drag a Fidget to an open spot on the grid. Finally, click Save so you can scroll down here for more instructions. + +![DragFidget-ezgif.com-loop-count](https://hackmd.io/_uploads/S1MTNDTsC.gif) + +scroll down for more ⬇️ ⬇️ ⬇️ + +### Customize Fidgets +Click any Fidget on the grid to open its settings. In addition to configuring each Fidget's settings, you can customize its look in the 'Style' tab. Any Fidget style parameters set to "Theme" inherit their look from the theme of their Tab. + +![EditFidget-ezgif.com-loop-count](https://hackmd.io/_uploads/r1pTND6s0.gif) + +### Arrange Fidgets +- **Move:** Click and drag fidgets from the middle to move them +![move fidget](https://nounspace.mypinata.cloud/ipfs/QmYWvdpdiyKwjVAqjhcFTBkiTUnc8rF4p2EGg3C4sTRsr6) +- **Resize:** Click and drag fidgets' edges to resize them +![2024-08-28 21.05.59](https://hackmd.io/_uploads/ryxADUpjC.gif) +- **Stash in Fidget Tray:** Click a fidget then click the ⇱ icon above it to save it in your Fidget Tray for later. +![image](https://hackmd.io/_uploads/Syz8wUajC.png) +- **Delete:** Click a fidget then click the X icon above it to delete it forever. +![image](https://hackmd.io/_uploads/SyhwvLpoR.png) + +### Customize Theme +- **Templates:** One quick and easy option is to select a template. Then if you'd like, you can further customize to make it your own. +- **Style:** Set a background color or gradient for each Tab, or set the default color, border, and shadows for all Fidgets on the selected Tab. +- **Fonts:** Set the default header and body fonts for Fidgets on your space. +- **Code:** Add HTML/CSS to fully customize your space's background. + +![Edit Theme](https://hackmd.io/_uploads/Sk7sWw6sC.gif) + +### Customize Music +Last but not least, search for or paste the link to any song or playlist on YouTube to play it for yourself on your homebase or on your space for your friends. + +### Homebase vs. Space +**Your Homebase** is a private space that only you can see. + +**Your Space** is your public profile that everyone can see. + +You can use the same Fidgets and tricks to customize them both. Use your **Homebase** to access the content, communities, and functionality that you love, and use your **Space** to share the content and functionality you love with your friends. + +`; +const onboardingFidgetID = "text:onboarding"; +const onboardingFidgetConfig = { + config: { + editable: true, + settings: { + title: "Welcome to Nounspace! 🚀 👾", + text: tutorialText, + urlColor: "blue", + fontFamily: "Londrina Solid", + fontColor: "#073b4c", + headingsFontFamily: "Londrina Solid", + headingsFontColor: "#2563ea", + backgroundColor: "#06d6a0", + borderColor: "#ffd166", + }, + data: {}, + }, + fidgetType: "text", + id: onboardingFidgetID, +}; const layoutID = ""; const INITIAL_HOMEBASE_CONFIG: SpaceConfig = { layoutID, layoutDetails: { layoutConfig: { - layout: [], + layout: [ + // Existing layouts can go here, e.g., feed, profile, etc. + { + w: 6, + h: 7, + x: 8, + y: 3, + i: onboardingFidgetID, + moved: false, + static: false, + }, + ], }, layoutFidget: "grid", }, theme: DEFAULT_THEME, - fidgetInstanceDatums: {}, + fidgetInstanceDatums: { + [onboardingFidgetID]: onboardingFidgetConfig, + }, isEditable: true, fidgetTrayContents: [], }; diff --git a/src/fidgets/farcaster/Feed.tsx b/src/fidgets/farcaster/Feed.tsx index 3658d051..19bd8226 100644 --- a/src/fidgets/farcaster/Feed.tsx +++ b/src/fidgets/farcaster/Feed.tsx @@ -25,6 +25,7 @@ import { import ColorSelector from "@/common/components/molecules/ColorSelector"; import BorderSelector from "@/common/components/molecules/BorderSelector"; import ShadowSelector from "@/common/components/molecules/ShadowSelector"; +import FontSelector from "@/common/components/molecules/FontSelector"; export enum FilterType { Channel = "channel_id", @@ -153,6 +154,27 @@ const feedProperties: FidgetProperties = { settings.selectPlatform.name !== "The other app", default: "light", }, + { + fieldName: "fontFamily", + default: "var(--user-theme-font)", + required: false, + inputSelector: FontSelector, + group: "style", + }, + { + fieldName: "fontColor", + default: "var(--user-theme-font-color)", + required: false, + inputSelector: ColorSelector, + group: "style", + }, + { + fieldName: "urlColor", + required: false, + inputSelector: ColorSelector, + default: "blue", + group: "style", + }, { fieldName: "background", default: "var(--user-theme-fidget-background)", @@ -300,7 +322,14 @@ const Feed: React.FC> = ({ settings }) => { )} {!isError && ( -
+
{isFetchingNextPage ? (
@@ -323,7 +352,13 @@ const Feed: React.FC> = ({ settings }) => { // Note: feed is mounted in its own scroll container to maintain its scroll position when // returning from a thread. return ( - <> +
{isThreadView && (
{renderThread()} @@ -337,7 +372,7 @@ const Feed: React.FC> = ({ settings }) => { > {renderFeedContent()}
- +
); }; diff --git a/src/fidgets/farcaster/components/CastRow.tsx b/src/fidgets/farcaster/components/CastRow.tsx index 92cae368..ff348b69 100644 --- a/src/fidgets/farcaster/components/CastRow.tsx +++ b/src/fidgets/farcaster/components/CastRow.tsx @@ -20,7 +20,7 @@ import { } from "@neynar/nodejs-sdk/build/neynar-api/v2"; import { useFarcasterSigner } from "@/fidgets/farcaster/index"; import { CastReactionType } from "@/fidgets/farcaster/types"; -import { ReactionType } from "@farcaster/core"; +import { bytesToHexString, ReactionType } from "@farcaster/core"; import { hexToBytes } from "@noble/ciphers/utils"; import CreateCast, { DraftType } from "./CreateCast"; import Modal from "@/common/components/molecules/Modal"; @@ -201,7 +201,7 @@ const CastAttributionPrimary = ({ cast }) => { if (!cast?.author?.display_name) return null; return ( -
+
{ }, [cast?.timestamp]); return ( -
+
@{cast.author.username} {relativeDateString && ( <> @@ -342,7 +342,7 @@ const CastReactions = ({ cast }: { cast: CastWithInteractions }) => { return (
{ event.stopPropagation(); onClickReaction(key, isActive); @@ -359,8 +359,26 @@ const CastReactions = ({ cast }: { cast: CastWithInteractions }) => { username: cast.author.username, castId: cast.hash, }); + + // Clean the hash by removing the "0x" prefix if present + const cleanedHash = cast.hash.startsWith("0x") + ? cast.hash.slice(2) + : cast.hash; + + // Convert the hex string to Uint8Array + const parentCastHash = hexToBytes(cleanedHash); + + // Check for invalid length and prevent submission if necessary + if (parentCastHash.length !== 20) { + console.error( + "Hash must be 20 bytes, but received length:", + parentCastHash.length, + ); + return; + } + setReplyCastDraft({ - parentCastId: castId, + parentCastId: { fid: cast.author.fid, hash: parentCastHash }, }); setReplyCastType("reply"); setShowModal(true); @@ -445,7 +463,7 @@ const CastReactions = ({ cast }: { cast: CastWithInteractions }) => { {cast.channel && cast.channel.name && (
/{cast.channel.name}
@@ -643,7 +661,7 @@ export const CastRow = ({ isReply={isReply} /> {replyingToUsername && ( -

+

Replying to{" "} diff --git a/src/fidgets/farcaster/components/CastThreadView.tsx b/src/fidgets/farcaster/components/CastThreadView.tsx index 547f3666..e154fa55 100644 --- a/src/fidgets/farcaster/components/CastThreadView.tsx +++ b/src/fidgets/farcaster/components/CastThreadView.tsx @@ -91,7 +91,7 @@ const StickyHeader = ({ onBack }: { onBack?: () => void }) => { <>

= ({
)} + {submitStatus === "error" && ( +
+ An error occurred while submitting the cast. +
+ )} +
{!isReply && ( -
+
{isPublishing || isLoadingSigner ? ( - channel.name + channel?.name ) : ( { + setChannel(selectedChannel); + }} value={channel} + initialChannels={initialChannels} /> )}
@@ -305,16 +372,16 @@ const CreateCast: React.FC = ({
-
-
- +
+ +
{hasEmbeds && ( diff --git a/src/fidgets/farcaster/components/Embeds/NounsBuildEmbed.tsx b/src/fidgets/farcaster/components/Embeds/NounsBuildEmbed.tsx index c141d754..a61b529e 100644 --- a/src/fidgets/farcaster/components/Embeds/NounsBuildEmbed.tsx +++ b/src/fidgets/farcaster/components/Embeds/NounsBuildEmbed.tsx @@ -223,7 +223,7 @@ const NounsBuildEmbed = ({ url }: { url: string }) => {

{stat.unit ? ( - {stat.unit} + {stat.unit} ) : null}
))} diff --git a/src/fidgets/farcaster/components/Embeds/OnchainEmbed.tsx b/src/fidgets/farcaster/components/Embeds/OnchainEmbed.tsx index 82cc02a9..36ad13dc 100644 --- a/src/fidgets/farcaster/components/Embeds/OnchainEmbed.tsx +++ b/src/fidgets/farcaster/components/Embeds/OnchainEmbed.tsx @@ -2,7 +2,7 @@ import React from "react"; const OnchainEmbed = ({ url }: { url: string }) => { return ( -
+
{url}
); diff --git a/src/fidgets/farcaster/components/Embeds/ParagraphXyzEmbed.tsx b/src/fidgets/farcaster/components/Embeds/ParagraphXyzEmbed.tsx index a2727ef7..13aa179c 100644 --- a/src/fidgets/farcaster/components/Embeds/ParagraphXyzEmbed.tsx +++ b/src/fidgets/farcaster/components/Embeds/ParagraphXyzEmbed.tsx @@ -121,7 +121,7 @@ const ParagraphXyzEmbed: React.FC = ({ url }) => { } else if (data.publication) { return renderPublication(); } else { - return

{url}

; + return

{url}

; } }; return
{renderData()}
; diff --git a/src/fidgets/farcaster/components/channelPicker.tsx b/src/fidgets/farcaster/components/channelPicker.tsx new file mode 100644 index 00000000..8c11d537 --- /dev/null +++ b/src/fidgets/farcaster/components/channelPicker.tsx @@ -0,0 +1,111 @@ +"use client"; + +import * as React from "react"; +import { CaretDownIcon } from "@radix-ui/react-icons"; +import { Button } from "@/common/components/atoms/button"; +import { + Command, + CommandInput, + CommandItem, + CommandEmpty, + CommandGroup, + CommandList, +} from "@/common/components/atoms/command"; // Adjust the import paths if needed +import { + Popover, + PopoverTrigger, + PopoverContent, +} from "@/common/components/atoms/popover"; // Adjust the import paths if needed +import { Channel } from "@mod-protocol/farcaster"; // Assuming this is your type + +type Props = { + getChannels: (query: string) => Promise; + onSelect: (value: Channel) => void; + value: Channel; + initialChannels?: Channel[]; +}; + +export function ChannelPicker(props: Props) { + const { getChannels, onSelect, value } = props; + const [open, setOpen] = React.useState(false); + const [channelResults, setChannelResults] = React.useState( + props.initialChannels ?? [], + ); + const [query, setQuery] = React.useState(""); + + React.useEffect(() => { + async function fetchChannels() { + const channels = await getChannels(query); + setChannelResults(channels); + } + + if (query !== "") { + fetchChannels(); + } else { + setChannelResults(props.initialChannels ?? []); + } + }, [getChannels, query, props.initialChannels]); + + const handleSelect = React.useCallback( + (channel: Channel) => { + setOpen(false); + onSelect(channel); + }, + [onSelect], + ); + + return ( + + + + + + + + + {channelResults.length === 0 ? ( + No channels found. + ) : ( + channelResults.map((channel) => ( + handleSelect(channel)} + > + {channel.name} + {channel.name} + + )) + )} + + + + + ); +} diff --git a/src/fidgets/farcaster/utils.ts b/src/fidgets/farcaster/utils.ts index 7bae5bc9..817ef7f2 100644 --- a/src/fidgets/farcaster/utils.ts +++ b/src/fidgets/farcaster/utils.ts @@ -137,15 +137,37 @@ export const submitCast = async ( fid: number, signer: Signer, ) => { - const castAddMessageResp = await makeCastAdd( - unsignedCastBody, - { fid, network: FarcasterNetwork.MAINNET }, - signer, - ); - if (castAddMessageResp.isOk()) { - return await submitMessageToBackend(castAddMessageResp.value); + try { + const castAddMessageResp = await makeCastAdd( + unsignedCastBody, + { fid, network: FarcasterNetwork.MAINNET }, // Ensure the network and fid are correct + signer, + ); + + // Check if cast creation was successful + if (!castAddMessageResp.isOk()) { + console.error("makeCastAdd failed with error:", castAddMessageResp.error); // Log the error returned + return false; + } + + // Submit the created message to the backend + const backendResponse = await submitMessageToBackend( + castAddMessageResp.value, + ); + + if (!backendResponse) { + console.error( + "submitMessageToBackend failed, response:", + backendResponse, + ); + return false; + } + + return backendResponse; + } catch (error) { + console.error("Error in submitCast:", error); + return false; } - return false; }; export const getDeadline = (): bigint => { diff --git a/src/fidgets/index.ts b/src/fidgets/index.ts index 7da82e50..6cb8665a 100644 --- a/src/fidgets/index.ts +++ b/src/fidgets/index.ts @@ -9,7 +9,7 @@ import Grid from "./layout/Grid"; import NounishGovernance from "./community/nouns-dao/NounishGovernance"; import Cast from "./farcaster/Cast"; import Feed from "./farcaster/Feed"; -import CreateCast from "./farcaster/CreateCast"; +// import CreateCast from "./farcaster/CreateCast"; import Links from "./ui/Links"; import snapShot from "./snapshot/SnapShot"; // import Swap from "./swap/Swap"; @@ -26,7 +26,7 @@ export const CompleteFidgets = { frame: Frame, feed: Feed, cast: Cast, - createCast: CreateCast, + // createCast: CreateCast, // Basic UI elements gallery: Gallery, text: TextFidget, diff --git a/src/fidgets/layout/Grid.tsx b/src/fidgets/layout/Grid.tsx index 8d3e3553..08dbe15c 100644 --- a/src/fidgets/layout/Grid.tsx +++ b/src/fidgets/layout/Grid.tsx @@ -337,9 +337,9 @@ const Grid: LayoutFidget = ({ {inEditMode && (