Browse Source

feat: 更新依赖版本,添加项目操作下拉菜单和图标组件

feature/group
huangmin 1 month ago
parent
commit
624129f3c9
  1. 8
      package-lock.json
  2. 412
      src/app/page.tsx
  3. 48
      src/components/ProjectActionsDropdown.tsx
  4. 169
      src/components/icons/index.tsx
  5. 7
      src/store/execution/nanoBananaExecutor.ts

8
package-lock.json

@ -254,14 +254,14 @@
}
},
"node_modules/@ant-design/icons": {
"version": "6.2.3",
"resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-6.2.3.tgz",
"integrity": "sha512-Pl3aoAtxQeKryYnt6VvDJtOxMOtA8wrRSACe/pTjOAIG3fdHrWm6Ivb4ku9tsFjYroSXBKirvuxG4QkwBXD9gg==",
"version": "6.2.5",
"resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-6.2.5.tgz",
"integrity": "sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw==",
"license": "MIT",
"dependencies": {
"@ant-design/colors": "^8.0.1",
"@ant-design/icons-svg": "^4.4.2",
"@rc-component/util": "^1.10.1",
"@rc-component/util": "^1.11.0",
"clsx": "^2.1.1"
},
"engines": {

412
src/app/page.tsx

@ -1,6 +1,414 @@
import Link from "next/link";
import type { ReactNode } from "react";
import { ProjectActionsDropdown } from "@/components/ProjectActionsDropdown";
import {
HelpIcon,
GlobeIcon,
CloseIcon,
SearchIcon,
ArrowIcon,
WeChatIcon,
SparklingIcon,
StartCreation
} from "@/components/icons";
const assets = {
heroSeedance:
"https://www.figma.com/api/mcp/asset/a7864f0d-0f6b-4509-bca3-26d1016d5d5e",
heroVlog:
"https://www.figma.com/api/mcp/asset/c675f2af-159a-49df-823a-b7d45136a96d",
heroPopi:
"https://www.figma.com/api/mcp/asset/c6cc76f8-ef2e-4236-b991-5e4e126b12f5",
projectAlice:
"https://www.figma.com/api/mcp/asset/9404cb6b-32ce-44ff-81f2-977a3b606915",
projectCoffee:
"https://www.figma.com/api/mcp/asset/5406ee32-dfc1-404f-a892-af494f3d6b27",
projectSakura:
"https://www.figma.com/api/mcp/asset/0033848b-0ca2-4e9e-ab59-b6526a185c5b",
projectUntitled:
"https://www.figma.com/api/mcp/asset/d28a0f40-98d0-4f4f-b3b7-5a507133dad2",
showPopi:
"https://www.figma.com/api/mcp/asset/f6307116-de24-4fb0-b608-f9c62412742e",
showAlice:
"https://www.figma.com/api/mcp/asset/51465958-a0ee-4a87-ba4f-d10a75eb36b0",
showDing:
"https://www.figma.com/api/mcp/asset/dec2b26f-4833-4532-a681-b398a0cb4b41",
showAido:
"https://www.figma.com/api/mcp/asset/09698f2b-ed7c-4356-8a5f-abbaa6b9fba9",
avatarUser:
"https://www.figma.com/api/mcp/asset/14978ca5-92b2-4ade-9687-eef333baa9d0",
};
const text = {
closeBanner: "关闭公告",
messages: "消息",
help: "帮助",
language: "语言",
search: "搜索",
moreActions: "更多操作",
};
const heroCards = [
{ title: "Seedance 2.0", subtitle: "火热上线", image: assets.heroSeedance },
{
title: "做一条VLOG分享生活",
subtitle: "精选案例账号",
image: assets.heroVlog,
},
{
title: "Popi.TV 测试上线",
subtitle: "帮助人类更好的表达",
image: assets.heroPopi,
},
];
const projects = [
{ title: "爱丽丝初稿", date: "2026/06/01", image: assets.projectAlice },
{ title: "喝咖啡Vlog", date: "2026/05/30", image: assets.projectCoffee },
{ title: "赏樱花测试", date: "2026/05/29", image: assets.projectSakura },
{ title: "未命名", date: "2026/05/28", image: assets.projectUntitled },
];
const tabs = [
"全部",
"精选画布",
"生活Vlog",
"短剧漫剧",
"专业影视",
"商业广告",
];
const shows = [
{
title: "PopiTV",
subtitle: "和爱丽丝一起郊游-Vlog",
image: assets.showPopi,
avatar: "👾",
vip: true,
},
{
title: "爱丽丝大神",
subtitle: "和爱丽丝一起郊游-Vlog",
image: assets.showAlice,
avatar: "🌸",
vip: true,
},
{
title: "叮叮频道",
subtitle: "和爱丽丝一起郊游-Vlog",
image: assets.showDing,
avatar: "🌊",
},
{
title: "Aido",
subtitle: "和爱丽丝一起郊游-Vlog",
image: assets.showAido,
avatar: "🌿",
},
];
function DiamondLogo({ label }: { label: string }) {
return (
<Link
href="/"
className="flex shrink-0 items-center gap-1.5 text-[14px] font-medium text-[#333]"
aria-label={label}
>
<img src="/banana_icon.png" alt={label} className="h-5 w-5" />
<span className="whitespace-nowrap text-[14px] font-medium text-[#333]">
{label}
</span>
</Link>
);
}
function HeaderIconButton({
children,
label,
}: {
children: ReactNode;
label: string;
}) {
return (
<button
type="button"
aria-label={label}
title={label}
className="grid h-[33px] w-[33px] place-items-center rounded-full bg-[#f9f9f9] text-[#606266] transition hover:bg-[#f0f0f0] hover:text-[#5c3bc7]"
>
{children}
</button>
);
}
function Header() {
return (
<header className="sticky top-0 z-30 bg-[#f9f9f9]">
<section className="relative flex h-[33px] items-center justify-center bg-[linear-gradient(90deg,#8a7fae_0%,#5c3bc7_50%,#8a7fae_100%)] px-10 text-[14px] font-medium leading-none text-white">
<div className="flex max-w-[920px] items-center justify-center gap-x-8 overflow-hidden whitespace-nowrap">
<span className="rounded-tl-[5px] rounded-br-[5px] bg-white/30 px-1.5 py-0.5 text-[12px]">
NEW
</span>
<span>
<strong>2.0</strong>
</span>
<span className="hidden md:inline">
<strong>Seedance 2.0</strong> 线
</span>
<span className="hidden rounded-tl-[5px] rounded-br-[5px] bg-white/30 px-1.5 py-0.5 text-[12px] lg:inline">
HOT
</span>
<span className="hidden lg:inline">
<strong>63</strong>
</span>
<span className="hidden xl:inline">
<strong>17600</strong>
</span>
</div>
<button
type="button"
aria-label={text.closeBanner}
title={text.closeBanner}
className="absolute right-[38px] top-1/2 grid h-5 w-5 -translate-y-1/2 place-items-center rounded-full bg-white text-[#999] transition hover:text-[#5c3bc7]"
>
<CloseIcon />
</button>
</section>
<div className="flex h-[57px] items-center justify-between px-[30px]">
<DiamondLogo label="Popi.TV" />
<nav className="flex items-center gap-3" aria-label="主导航">
<div className="hidden items-center gap-3 md:flex">
<Link
href="/"
className="flex h-[33px] items-center justify-center gap-1 rounded-full bg-[#f9f9f9] px-3 text-[14px] font-medium !text-[#333] hover:bg-[#f0f0f0]"
>
<img src="/banana_icon.png" alt="Popi.art" className="h-4 w-4" />
Popi.art
</Link>
<Link
href="/canvas"
className="flex h-[33px] items-center justify-center gap-1 rounded-full bg-[#f9f9f9] px-3 text-[14px] font-medium !text-[#333] hover:bg-[#f0f0f0]"
>
<img src="/banana_icon.png" alt="Skill" className="h-4 w-4" />
Skill
</Link>
</div>
<div className="hidden items-center gap-2 sm:flex">
<HeaderIconButton label={text.messages}>
<WeChatIcon />
</HeaderIconButton>
<HeaderIconButton label={text.help}>
<HelpIcon />
</HeaderIconButton>
<HeaderIconButton label={text.language}>
<GlobeIcon />
</HeaderIconButton>
</div>
<div className="hidden h-[33px] items-center gap-1 rounded-full bg-[#f9f9f9] px-3 text-[14px] text-[#666] lg:flex">
<SparklingIcon />
<span className="text-[#606266]">80</span>
<span></span>
</div>
<button
type="button"
aria-label="用户菜单"
title="用户菜单"
className="h-[33px] w-[33px] overflow-hidden rounded-full"
>
<img
src={assets.avatarUser}
alt=""
className="h-full w-full object-cover"
/>
</button>
</nav>
</div>
</header>
);
}
function HeroCard({
title,
subtitle,
image,
}: {
title: string;
subtitle: string;
image: string;
}) {
return (
<article className="group relative h-[277px] overflow-hidden rounded-[24px] bg-[#ddd]">
<img
src={image}
alt=""
className="h-full w-full object-cover transition duration-500 group-hover:scale-105"
/>
<div className="absolute inset-0 bg-gradient-to-b from-transparent from-[56%] to-black/70" />
<div className="absolute bottom-[30px] left-7 text-white">
<h2 className="text-[30px] font-bold leading-none tracking-[0]">
{title}
</h2>
<p className="mt-4 text-[16px] font-medium leading-none">{subtitle}</p>
</div>
</article>
);
}
function ProjectCard({
title,
date,
image,
}: {
title: string;
date: string;
image: string;
}) {
return (
<article className="min-w-0">
<div className="h-[139px] overflow-hidden rounded-[24px] bg-[#ddd]">
<img src={image} alt="" className="h-full w-full object-cover" />
</div>
<div className="mt-3 flex items-start justify-between gap-3">
<div className="min-w-0">
<h3 className="truncate text-[16px] font-medium leading-tight text-[#333]">
{title}
</h3>
<p className="mt-1 text-[13px] leading-tight text-[#666]">{date}</p>
</div>
<ProjectActionsDropdown title={title} label={text.moreActions} />
</div>
</article>
);
}
function ShowCard({
title,
subtitle,
image,
avatar,
vip,
}: {
title: string;
subtitle: string;
image: string;
avatar: string;
vip?: boolean;
}) {
return (
<article className="min-w-0">
<div className="h-[189px] overflow-hidden rounded-[24px] bg-[#ddd]">
<img src={image} alt="" className="h-full w-full object-cover" />
</div>
<div className="mt-3">
<div className="flex items-center gap-[5px]">
<span className="grid h-5 w-5 place-items-center rounded-full bg-[#d6ceef] text-[11px]">
{avatar}
</span>
<h3 className="truncate text-[16px] font-medium leading-tight text-[#333]">
{title}
</h3>
{vip ? (
<span className="rounded-full bg-[linear-gradient(90deg,#d6ceef_45%,#f0f0f0_100%)] px-2.5 py-0.5 text-[12px] font-medium leading-none text-[#5c3bc7]">
VIP
</span>
) : null}
</div>
<p className="mt-2 truncate text-[13px] leading-tight text-[#666]">
{subtitle}
</p>
</div>
</article>
);
}
export default function Home() {
return (
<div>11111111111111</div>
<main className="min-h-screen bg-[#f9f9f9] text-[#333]">
<Header />
<div className="mx-auto w-full max-w-[1456px] px-5 pb-16">
<section className="grid gap-5 lg:grid-cols-3">
{heroCards.map((card) => (
<HeroCard key={card.title} {...card} />
))}
</section>
<section className="mt-[70px]">
<div className="mb-5 flex items-center justify-between">
<h2 className="text-[20px] font-bold leading-none text-[#333]">
</h2>
<Link
href="/canvas"
className="flex items-center gap-1 text-[16px] leading-none !text-[#666] hover:text-[#5c3bc7]"
>
<ArrowIcon />
</Link>
</div>
<div className="relative grid gap-5 md:grid-cols-2 xl:grid-cols-5">
<Link
href="/canvas"
className="flex h-[139px] flex-col items-center justify-center rounded-[24px] border border-[#d6ceef] bg-[linear-gradient(123deg,#f0f0f0_20%,#d6ceef_100%)] transition hover:border-[#5c3bc7]"
>
<StartCreation />
<span className="mt-2 text-[18px] font-medium leading-none text-[#999]">
</span>
</Link>
{projects.map((project) => (
<ProjectCard key={project.title} {...project} />
))}
<div className="pointer-events-none absolute bottom-0 right-0 hidden h-[139px] w-[105px] bg-gradient-to-r from-[#f9f9f9]/0 to-[#f9f9f9] xl:block" />
<button
type="button"
aria-label="下一个项目"
title="下一个项目"
className="absolute right-[-25px] top-[52px] hidden h-[39px] w-[39px] items-center justify-center rounded-full bg-white text-[#666] shadow-[0_2px_5px_rgba(0,0,0,0.1)] xl:flex"
>
<ArrowIcon />
</button>
</div>
</section>
<section className="mt-[64px]">
<div className="mb-5 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div>
<h2 className="text-[20px] font-bold leading-none text-[#333]">
TV show
</h2>
<div className="mt-5 flex flex-wrap items-center gap-1 text-[16px]">
{tabs.map((tab, index) => (
<button
key={tab}
type="button"
className={
index === 0
? "rounded-[9px] bg-[#f0f0f0] px-[11px] py-[5px] font-medium text-[#333]"
: "rounded-[9px] bg-[#f9f9f9] px-[15px] py-[5px] text-[#666] transition hover:bg-[#f0f0f0] hover:text-[#333]"
}
>
{tab}
</button>
))}
</div>
</div>
<div className="flex h-[35px] w-full items-center justify-between rounded-full bg-white px-[15px] text-[#999] shadow-[0_2px_5px_rgba(0,0,0,0.1)] lg:w-[295px]">
<input
aria-label={text.search}
placeholder="请输入搜索的内容"
className="min-w-0 flex-1 bg-transparent text-[14px] outline-none placeholder:text-[#999]"
/>
<SearchIcon />
</div>
</div>
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-4">
{shows.map((show) => (
<ShowCard key={show.title} {...show} />
))}
</div>
</section>
</div>
</main>
);
}
}

48
src/components/ProjectActionsDropdown.tsx

@ -0,0 +1,48 @@
"use client";
import { Dropdown, type MenuProps } from "antd";
import { useState } from "react";
const projectActionItems: MenuProps["items"] = [
{ key: "open", label: "打开" },
{ key: "rename", label: "重命名" },
{ key: "change-cover", label: "修改封面" },
{ key: "duplicate", label: "创建副本" },
{ key: "delete", label: "删除项目" },
];
export function ProjectActionsDropdown({
title,
label,
}: {
title: string;
label: string;
}) {
const [open, setOpen] = useState(false);
return (
<Dropdown
open={open}
onOpenChange={setOpen}
trigger={["click"]}
placement="bottomRight"
menu={{ items: projectActionItems }}
popupRender={(menu) => (
<div className="w-[144px] rounded-[9px] bg-white p-[5px] shadow-[0_2px_10px_rgba(0,0,0,0.1)] [&_.ant-dropdown-menu]:!border-0 [&_.ant-dropdown-menu]:!bg-transparent [&_.ant-dropdown-menu]:!p-0 [&_.ant-dropdown-menu]:!shadow-none [&_.ant-dropdown-menu-item]:!h-[35px] [&_.ant-dropdown-menu-item]:!rounded-[5px] [&_.ant-dropdown-menu-item]:!px-3 [&_.ant-dropdown-menu-item]:!py-2 [&_.ant-dropdown-menu-item]:!text-[13px] [&_.ant-dropdown-menu-item]:!font-normal [&_.ant-dropdown-menu-item]:!text-[#666] [&_.ant-dropdown-menu-item-active]:!bg-[#f0f0f0] [&_.ant-dropdown-menu-item-selected]:!bg-[#f0f0f0]">
{menu}
</div>
)}
>
<button
type="button"
aria-label={`${title} ${label}`}
title={label}
className={`flex h-[28px] min-w-[28px] items-center justify-center rounded-[8px] px-2 leading-none !text-[#666] transition hover:bg-[#f0f0f0] ${
open ? "bg-[#f0f0f0]" : "bg-transparent"
}`}
>
<span className="-translate-y-[3px] leading-none">...</span>
</button>
</Dropdown>
);
}

169
src/components/icons/index.tsx

@ -0,0 +1,169 @@
export function MessageIcon() {
return (
<svg
viewBox="0 0 24 24"
className="h-[18px] w-[18px]"
fill="none"
aria-hidden="true"
>
<path
d="M5.5 6.5h13v8.25h-6.8L8.3 18v-3.25H5.5z"
stroke="currentColor"
strokeWidth="1.8"
/>
<path
d="M8.5 9.5h7M8.5 12h4.5"
stroke="currentColor"
strokeWidth="1.8"
strokeLinecap="round"
/>
</svg>
);
}
export function HelpIcon() {
return (
<svg
viewBox="0 0 24 24"
className="h-[18px] w-[18px]"
fill="none"
aria-hidden="true"
>
<circle cx="12" cy="12" r="8.2" stroke="currentColor" strokeWidth="1.8" />
<path
d="M9.8 9.4a2.3 2.3 0 0 1 4.4.8c0 1.55-1.65 1.9-2.05 3.05M12 16.25h.01"
stroke="currentColor"
strokeWidth="1.8"
strokeLinecap="round"
/>
</svg>
);
}
export function GlobeIcon() {
return (
<svg
viewBox="0 0 24 24"
className="h-[18px] w-[18px]"
fill="none"
aria-hidden="true"
>
<circle cx="12" cy="12" r="8.2" stroke="currentColor" strokeWidth="1.8" />
<path
d="M4.5 12h15M12 3.8c2 2.2 3 4.9 3 8.2s-1 6-3 8.2c-2-2.2-3-4.9-3-8.2s1-6 3-8.2Z"
stroke="currentColor"
strokeWidth="1.8"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export function CloseIcon() {
return (
<svg
viewBox="0 0 20 20"
className="h-3.5 w-3.5"
fill="none"
aria-hidden="true"
>
<path
d="m5.5 5.5 9 9M14.5 5.5l-9 9"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
);
}
export function SearchIcon() {
return (
<svg viewBox="0 0 24 24" className="h-5 w-5" fill="none" aria-hidden="true">
<circle
cx="10.8"
cy="10.8"
r="5.8"
stroke="currentColor"
strokeWidth="1.8"
/>
<path
d="m15.3 15.3 4 4"
stroke="currentColor"
strokeWidth="1.8"
strokeLinecap="round"
/>
</svg>
);
}
export function ArrowIcon() {
return (
<svg viewBox="0 0 24 24" className="h-5 w-5" fill="none" aria-hidden="true">
<path
d="m9 5 7 7-7 7"
stroke="currentColor"
strokeWidth="1.8"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export const WeChatIcon = () => (
<svg
viewBox="64 64 896 896"
focusable="false"
data-icon="wechat"
width="1em"
height="1em"
fill="currentColor"
aria-hidden="true"
>
<path d="M690.1 377.4c5.9 0 11.8.2 17.6.5-24.4-128.7-158.3-227.1-319.9-227.1C209 150.8 64 271.4 64 420.2c0 81.1 43.6 154.2 111.9 203.6a21.5 21.5 0 019.1 17.6c0 2.4-.5 4.6-1.1 6.9-5.5 20.3-14.2 52.8-14.6 54.3-.7 2.6-1.7 5.2-1.7 7.9 0 5.9 4.8 10.8 10.8 10.8 2.3 0 4.2-.9 6.2-2l70.9-40.9c5.3-3.1 11-5 17.2-5 3.2 0 6.4.5 9.5 1.4 33.1 9.5 68.8 14.8 105.7 14.8 6 0 11.9-.1 17.8-.4-7.1-21-10.9-43.1-10.9-66 0-135.8 132.2-245.8 295.3-245.8zm-194.3-86.5c23.8 0 43.2 19.3 43.2 43.1s-19.3 43.1-43.2 43.1c-23.8 0-43.2-19.3-43.2-43.1s19.4-43.1 43.2-43.1zm-215.9 86.2c-23.8 0-43.2-19.3-43.2-43.1s19.3-43.1 43.2-43.1 43.2 19.3 43.2 43.1-19.4 43.1-43.2 43.1zm586.8 415.6c56.9-41.2 93.2-102 93.2-169.7 0-124-120.8-224.5-269.9-224.5-149 0-269.9 100.5-269.9 224.5S540.9 847.5 690 847.5c30.8 0 60.6-4.4 88.1-12.3 2.6-.8 5.2-1.2 7.9-1.2 5.2 0 9.9 1.6 14.3 4.1l59.1 34c1.7 1 3.3 1.7 5.2 1.7a9 9 0 006.4-2.6 9 9 0 002.6-6.4c0-2.2-.9-4.4-1.4-6.6-.3-1.2-7.6-28.3-12.2-45.3-.5-1.9-.9-3.8-.9-5.7.1-5.9 3.1-11.2 7.6-14.5zM600.2 587.2c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9c0 19.8-16.2 35.9-36 35.9zm179.9 0c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9a36.08 36.08 0 01-36 35.9z"></path>
</svg>
);
export const SparklingIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
>
<path
d="M7.875 3.69141C8.46396 3.69141 8.94141 3.21396 8.94141 2.625H9.43359C9.43359 3.21396 9.91104 3.69141 10.5 3.69141V4.18359C9.91104 4.18359 9.43359 4.66104 9.43359 5.25H8.94141C8.94141 4.66104 8.46396 4.18359 7.875 4.18359V3.69141ZM2.1875 6.5625C3.63725 6.5625 4.8125 5.38725 4.8125 3.9375H5.6875C5.6875 5.38725 6.86276 6.5625 8.3125 6.5625V7.4375C6.86276 7.4375 5.6875 8.61276 5.6875 10.0625H4.8125C4.8125 8.61276 3.63725 7.4375 2.1875 7.4375V6.5625ZM9.29688 7.875C9.29688 8.66027 8.66027 9.29688 7.875 9.29688V9.95312C8.66027 9.95312 9.29688 10.5897 9.29688 11.375H9.95312C9.95312 10.5897 10.5897 9.95312 11.375 9.95312V9.29688C10.5897 9.29688 9.95312 8.66027 9.95312 7.875H9.29688Z"
fill="url(#paint0_linear_15_1855)"
/>
<defs>
<linearGradient
id="paint0_linear_15_1855"
x1="3.09416"
y1="3.54605"
x2="12.1472"
y2="6.1316"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#6B45E9" />
<stop offset="0.5" stopColor="#6841EA" />
<stop offset="1" stopColor="#8A68FE" />
</linearGradient>
</defs>
</svg>
);
export const StartCreation = () => (
<svg
viewBox="64 64 896 896"
focusable="false"
data-icon="plus"
width="20px"
height="20px"
fill="gray"
aria-hidden="true"
>
<path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path>
<path d="M192 474h672q8 0 8 8v60q0 8-8 8H160q-8 0-8-8v-60q0-8 8-8z"></path>
</svg>
);

7
src/store/execution/nanoBananaExecutor.ts

@ -47,6 +47,11 @@ export interface NanoBananaOptions {
useStoredFallback?: boolean;
}
type GeneratedResultImage = {
url: string;
previewImg?: string;
};
const GEMINI_RESOLUTIONS: Resolution[] = ["512", "1K", "2K", "4K"];
function normalizeGeminiResolution(value: string): Resolution {
@ -289,7 +294,7 @@ export async function executeNanoBanana(
signal,
});
}
const resultImages = result.success && Array.isArray(result.images) && result.images.length > 0
const resultImages: GeneratedResultImage[] = result.success && Array.isArray(result.images) && result.images.length > 0
? result.images
: result.success && result.image
? [result.image]

Loading…
Cancel
Save