Skip to content

Commit

Permalink
file selection
Browse files Browse the repository at this point in the history
  • Loading branch information
ankushKun committed Aug 22, 2024
1 parent bd4f8bf commit 1d85b3d
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 34 deletions.
2 changes: 1 addition & 1 deletion next_app/src/components/drawer/components/marketplace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function TemplateMarketplace() {
</div>
<br />
<div>
To publish your own template, simply use the <span className="text-primary inline-flex items-center">Project <ChevronRight className="inline p-0" size={15} /> Publish</span> Template option from the menu
To publish your own template, simply use the <span className="text-primary inline-flex items-center">Project <ChevronRight className="inline p-0" size={15} /> Publish Template</span> option from the menu
</div>
</div>
</div>
Expand Down
60 changes: 50 additions & 10 deletions next_app/src/components/menubar/components/publish-template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useState } from "react";
import { extractHandlerNames } from "@/lib/utils";
import { Label } from "@/components/ui/label";
import { FileCode, LoaderIcon, SquareFunction } from "lucide-react";
import { Checkbox } from "@/components/ui/checkbox";

export type TemplateAsset = {
name: string;
Expand All @@ -38,6 +39,8 @@ export default function PublishTemplateBtn() {

const project = globalState.activeProject && manager.getProject(globalState.activeProject)
const files = project && project.files
console.log(files)
const [selectedFiles, setSelectedFiles] = useState<string[]>(Object.keys(files || {}))

async function publishTemplateHandler() {
if (!globalState.activeProject) return toast.error("No active project")
Expand All @@ -58,12 +61,13 @@ export default function PublishTemplateBtn() {

//strip process ids from project.files
const templateFiles = Object.values(project.files).map((file) => {
return {
name: file.name,
content: file.content,
language: file.language,
type: file.type,
}
if (selectedFiles.includes(file.name))
return {
name: file.name,
content: file.content,
language: file.language,
type: file.type,
}
})

if (templateFiles.length === 0) return toast.error("Project must have at least one file to publish as a template")
Expand Down Expand Up @@ -172,12 +176,48 @@ export default function PublishTemplateBtn() {
This will publish your project as an atomic asset on the bazar marketplace
</DialogDescription>
</DialogHeader>
<Label htmlFor="name" className="px-2">Template Name</Label>
<Label htmlFor="name" className="px-2 text-lg -mb-3">Name</Label>
<Input id="name" placeholder="Enter a title for your template" onChange={(e) => setTname(e.target.value)} />
<Label htmlFor="description" className="px-2">Template Description</Label>
<Label htmlFor="description" className="px-2 text-lg -mb-3">Short Description</Label>
<Textarea id="description" placeholder="Enter a description for your template" onChange={(e) => setTdescription(e.target.value)} />
<Label htmlFor="quantity" className="px-2">Number of copies</Label>
<Input id="quantity" placeholder="Enter the number of copies you want to mint" type="number" min={1} defaultValue={1} onChange={(e) => setTquantity(parseInt(e.target.value))} />
<div className="grid grid-cols-2">
<div>
<Label htmlFor="quantity" className="px-2 text-lg">Select Files</Label>
<Label htmlFor="select-all" className="inline-flex gap-1 items-center ml-2 text-muted-foreground">[ All<Checkbox
id="select-all"
className="scale-75 m-0 p-0"
defaultChecked
checked={selectedFiles.length === Object.keys(files || {}).length}
onCheckedChange={(e) => {
if (e) {
setSelectedFiles(Object.keys(files))
} else {
setSelectedFiles([])
}
}} />]</Label>
{
files && Object.keys(files).map((fname) => {
const file = files[fname]
return <div key={fname} className="flex items-center gap-2 my-1 ml-3">
<Checkbox id={fname} defaultChecked checked={selectedFiles.includes(fname)} onCheckedChange={(e) => {
if (e) {
setSelectedFiles([...selectedFiles, fname])
} else {
setSelectedFiles(selectedFiles.filter((f) => f !== fname))
}
}} />
<Label htmlFor={fname} data-selected={selectedFiles.includes(file.name)} className="text-muted data-[selected=true]:text-muted-foreground">{file.name}</Label>
</div>
})
}

</div>
<div>

<Label htmlFor="quantity" className="px-2 text-lg">Number of copies</Label>
<Input id="quantity" placeholder="Enter the number of copies you want to mint" type="number" min={1} defaultValue={1} onChange={(e) => setTquantity(parseInt(e.target.value))} />
</div>
</div>

<div className="flex gap-4 px-2">
<div className="flex gap-1 items-center">{`${files && Object.keys(files).length} Files`}<FileCode size={20} /></div>
Expand Down
20 changes: 12 additions & 8 deletions next_app/src/components/menubar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import DuplicateProject from "./components/duplicate-project"
import DuplicateFile from "./components/duplicate-file"
import PublishTemplateBtn from "./components/publish-template"
import { toast } from "sonner"
import { GitHubLogoIcon, TwitterLogoIcon } from "@radix-ui/react-icons"
import { DiscordLogoIcon, GitHubLogoIcon, TwitterLogoIcon } from "@radix-ui/react-icons"
import { FaDiscord, FaSquareXTwitter, FaXTwitter } from "react-icons/fa6";
import Link from "next/link"
import Sponsor from "./components/sponsor"
import Upload from "./components/upload"
Expand Down Expand Up @@ -45,7 +46,7 @@ export default function Menubar() {
}

return <div className="border-b h-[30px] text-xs flex items-center overflow-clip">
<Image src="/icon.svg" alt="Logo" width={40} height={40} className="py-1 min-w-12 h-[30px] cursor-pointer hover:bg-accent" onClick={logoClicked} />
<Link href="https://betteridea.dev" target="_blank"><Image src="/icon.svg" alt="Logo" width={40} height={40} className="py-1 min-w-12 h-[30px] cursor-pointer hover:bg-accent" /></Link>
<MenubarComponent className="border-none m-0 p-0 min-w-[calc(100vw-50px)]">
<MenubarMenu>
<MenubarTrigger className="rounded-none m-0">Project</MenubarTrigger>
Expand All @@ -57,7 +58,7 @@ export default function Menubar() {
<MenubarItem onClick={() => document.getElementById("new-project")?.click()}>New Project</MenubarItem>
<MenubarItem onClick={() => document.getElementById("all-projects")?.click()}>All Projects</MenubarItem>
<MenubarSeparator />
<MenubarItem onClick={() => document.getElementById("publish-template")?.click()} >Publish Template (coming soon)</MenubarItem>
<MenubarItem disabled={!project} onClick={() => document.getElementById("publish-template")?.click()} >Publish Template (beta)</MenubarItem>
<MenubarSeparator />
<MenubarItem disabled={!project} onClick={() => document.getElementById("rename-project")?.click()}>Rename</MenubarItem>
<MenubarItem disabled={!project} onClick={() => document.getElementById("duplicate-project")?.click()}>Duplicate</MenubarItem>
Expand Down Expand Up @@ -96,8 +97,10 @@ export default function Menubar() {
</MenubarMenu>
<div className="grow"></div>
<MenubarMenu>
<MenubarTrigger className="rounded-none">More from us</MenubarTrigger>
<MenubarContent sideOffset={1} alignOffset={0} className="rounded-b-md rounded-t-none bg-background">
<Link href="https://x.com/betteridea_dev" target="_blank"><Button variant="ghost" className="rounded-none p-1.5"><FaXTwitter /></Button></Link>
<Link href="https://github.com/betteridea-dev" target="_blank"><Button variant="ghost" className="rounded-none p-1.5"><GitHubLogoIcon /></Button></Link>
<Link href="https://discord.gg/nm6VKUQBrA" target="_blank"><Button variant="ghost" className="rounded-none p-1.5"><FaDiscord /></Button></Link>
{/* <MenubarContent sideOffset={1} alignOffset={0} className="rounded-b-md rounded-t-none bg-background">
<MenubarLabel className="text-muted-foreground">Our products in the ecosystem</MenubarLabel>
<MenubarSeparator />
<Link href="https://learn.betteridea.dev" target="_blank"><MenubarItem>LearnAO</MenubarItem></Link>
Expand All @@ -109,17 +112,18 @@ export default function Menubar() {
<MenubarSeparator />
<Link href="https://discord.gg/nm6VKUQBrA" target="_blank"><MenubarItem>Chat with us on Discord</MenubarItem></Link>
<Link href="https://x.com/twitter.com/betteridea_dev" target="_blank"><MenubarItem>Follow us on X (Twitter)</MenubarItem></Link>
</MenubarContent>
</MenubarContent> */}
</MenubarMenu>
<MenubarMenu>
{/* <MenubarMenu>
<MenubarTrigger className="rounded-none" onClick={() => document.getElementById("sponsor-us")?.click()}>Sponsor Us</MenubarTrigger>
<MenubarContent sideOffset={1} alignOffset={0} className="rounded-b-md rounded-t-none bg-background max-w-sm">
<MenubarLabel className="text-muted-foreground">
♥️ Sponsor BetterIDEa
so we can continue working on enhancing the developer experience on ao
</MenubarLabel>
</MenubarContent>
</MenubarMenu>
</MenubarMenu> */}

</MenubarComponent>

<div className="grow" />
Expand Down
73 changes: 59 additions & 14 deletions next_app/src/components/views/components/marketplace.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Input } from "@/components/ui/input";
import { TView } from "."
import { Button } from "@/components/ui/button";
import { ArrowLeft, CircleUserRound, Files, Search, SquareFunction } from "lucide-react";
import { useGlobalState } from "@/hooks";
import { ArrowLeft, CircleUserRound, Files, Loader, LoaderIcon, Search, SquareFunction } from "lucide-react";
import { useGlobalState, useProjectManager } from "@/hooks";
import WarpedAR from "@/components/ui/icons/war";
import Link from "next/link";
import BazarIcon from "@/components/ui/icons/bazar";
Expand All @@ -24,8 +24,12 @@ import { Tag } from "@/lib/ao-vars";
import { extractHandlerNames } from "@/lib/utils";
import { AOProfileType, getProfileById } from "@/lib/bazar";
import Image from "next/image";
import { useLocalStorage, useSessionStorage } from "usehooks-ts";

function Template({ pid, search }: { pid: string, search: string }) {
const globalState = useGlobalState()
const manager = useProjectManager()
const [open, setOpen] = useState(false)
const [hovered, setHovered] = useState(false)
const [txData, setTxData] = useState<TemplateAsset & { creator: string }>(null)
const [assetTags, setAssetTags] = useState<{ [name: string]: string }>({
Expand All @@ -46,6 +50,7 @@ function Template({ pid, search }: { pid: string, search: string }) {
banner: null,
version: null,
})
const [loading, setLoading] = useState(false)

useEffect(() => {
if (!pid) return
Expand All @@ -66,7 +71,7 @@ function Template({ pid, search }: { pid: string, search: string }) {
}
}
`

setLoading(true)
const client = new GraphQLClient("https://arweave.net/graphql")
client.request(gqlQuery).then((data) => {
const dataTags: Tag[] = (data as any).transactions.edges[0].node.tags
Expand All @@ -93,6 +98,8 @@ function Template({ pid, search }: { pid: string, search: string }) {
}).catch((e) => {
console.error(e)
toast.error("Error fetching transaction data")
}).finally(() => {
setLoading(false)
})
}, [pid])

Expand All @@ -102,6 +109,7 @@ function Template({ pid, search }: { pid: string, search: string }) {
const fileNames = Object.keys(txData.files);
const sourceSummed = fileNames.map(fileName => {
const file = txData.files[fileName];
if (!file) return "";
if (file.type == "NORMAL")
return file.content.cells[0].code;
else {
Expand Down Expand Up @@ -165,15 +173,52 @@ function Template({ pid, search }: { pid: string, search: string }) {
return null
}

function importIntoProj() {
if (!globalState.activeProject) return toast.error("No active project")
const proj = manager.getProject(globalState.activeProject)
if (!proj) return toast.error("Project not found")
if (!txData) return toast.error("No transaction data, try again")

txData.files.forEach((file) => {
// if (proj.files[file.name])
// file.name = file.name + " (1)"
let count = 0;
while (proj.files[file.name]) {
count++;
file.name = file.name.replace(/\s\(\d+\)/, "") + ` (${count})`;
}
if (file.type == "NORMAL") {
manager.newFile(proj, {
name: file.name,
initialContent: file.content.cells[0].code,
type: file.type
})
} else {
const newfile = manager.newFile(proj, {
name: file.name,
initialContent: "",
type: file.type
})
newfile.content = file.content;
proj.files[newfile.name] = newfile;
manager.projects[proj.name] = proj;
manager.saveProjects(manager.projects);
}
})

toast.success("Imported into active project")
globalState.setActiveSidebarItem("FILES")
setOpen(false)
}

return <Dialog>
<DialogTrigger asChild>
return <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild disabled={loading}>
<div data-hovered={hovered}
className="p-5 border rounded transition-all duration-200 cursor-pointer data-[hovered=true]:bg-muted/15"
className="p-5 border relative rounded transition-all duration-200 cursor-pointer data-[hovered=true]:bg-muted/15"
onMouseMove={handleMouseEnter} onMouseLeave={handleMouseLeave}
onClick={handleClick}
>

{loading && <LoaderIcon className="animate-spin absolute bg-background rounded-full p-1 left-1 top-1" />}
<div className="grid grid-cols-5 items-start justify-between mb-5">
<div className="flex flex-col col-span-3">
<div className="text-lg font-semibold">{assetTags['Title']}</div>
Expand Down Expand Up @@ -208,20 +253,21 @@ function Template({ pid, search }: { pid: string, search: string }) {
<Link href={`https://ao-bazar.arweave.net/#/asset/${pid}`} target="_blank" className="text-primary hover:underline underline-offset-4 flex items-center gap-1.5"
onClick={(e) => { e.stopPropagation() }}
><BazarIcon /> View on bazar</Link>
<Button type="submit">Import Into current project</Button>
<Button disabled={!globalState.activeProject} type="submit" onClick={importIntoProj}>Import Into current project</Button>
</DialogFooter>
</DialogContent>
</Dialog>
}

function Marketplace() {
const globalState = useGlobalState();
const [assetIds, setAssetIds] = useState<string[]>([])
const [assetIds, setAssetIds] = useSessionStorage<string[]>("marketplace", [], { initializeWithValue: true })
const [search, setSearch] = useState("")
const [loading, setLoading] = useState(false)

useEffect(() => {
function fetchAssets() {
if (!search) return
if (!search && assetIds.length > 0) return
if (search.length == 43) return

const gqlQuery = gql`query {
Expand All @@ -242,6 +288,7 @@ function Marketplace() {
}
}
}`
setLoading(true)
const client = new GraphQLClient("https://arweave.net/graphql")
client.request(gqlQuery).then((data) => {
const node = (data as any).transactions.edges.map((edge) => edge.node)
Expand All @@ -252,16 +299,13 @@ function Marketplace() {
console.error(e)
toast.error("Error fetching transaction data")
})
setLoading(false)
}
const t = setTimeout(fetchAssets, 500)

return () => clearTimeout(t)
}, [search])

useEffect(() => {
console.log(search)
}, [search])

return <div className="p-10 overflow-scroll max-h-[calc(100vh-55px)]">
<Button variant="link" className="mb-5 text-sm text-muted p-0" onClick={() => {
globalState.setActiveView(null)
Expand All @@ -285,6 +329,7 @@ function Marketplace() {

<div className="grid lg:grid-cols-2 gap-3">
{/* {Array.from({ length: 10 }).map((_, i) => <Template key={i} pid={"YWkoL2_Myf2r05dYUzGJNIMSd3leAYwTpvSaU6E8zQQ"} />)} */}
{loading && <div className="text-center col-span-2 flex items-center justify-center"><LoaderIcon className="animate-spin" /></div>}
{
assetIds.map((pid) => <Template key={pid} pid={pid} search={search} />)
}
Expand Down
2 changes: 1 addition & 1 deletion next_app/src/hooks/useProjectManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export class Project {
///// FILE /////

export class PFile {
readonly name: string;
name: string;
readonly language: TLanguages;
readonly type: "NORMAL" | "NOTEBOOK";
process: string;
Expand Down
2 changes: 2 additions & 0 deletions next_app/src/pages/renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export default function TxRenderer({ id_ }: { id_: string }) {
const fileNames = Object.keys(txData.files);
const sourceSummed = fileNames.map(fileName => {
const file = txData.files[fileName];
if (!file) return ""
if (file.type == "NORMAL")
return file.content.cells[0].code;
else {
Expand Down Expand Up @@ -110,6 +111,7 @@ export default function TxRenderer({ id_ }: { id_: string }) {
<div className="">
{
txData?.files && txData.files.map((file, _) => {
if (!file) return
return <div data-active={activeFile == _} className="p-0.5 px-1 m-0.5 overflow-scroll data-[active=true]:bg-muted/20 hover:!bg-muted/40 cursor-pointer rounded" key={_} onClick={e => setActiveFile(_)}>{file.name}</div>
})
}
Expand Down

0 comments on commit 1d85b3d

Please sign in to comment.