Skip to content

Commit 2a02b75

Browse files
authored
Merge pull request #632 from PotLock/fix/issues-batch-7-may2026
Fix/issues batch 7 may2026
2 parents a8ad4f9 + 6269cf5 commit 2a02b75

8 files changed

Lines changed: 96 additions & 45 deletions

File tree

src/entities/_shared/account/components/profile-images.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import type { LazyLoadImageProps } from "react-lazy-load-image-component";
22

33
import { ByAccountId, type LiveUpdateParams } from "@/common/types";
4-
import { Avatar, AvatarImage, Skeleton } from "@/common/ui/layout/components";
4+
import { Avatar, AvatarFallback, AvatarImage, Skeleton } from "@/common/ui/layout/components";
55
import { LazyImage } from "@/common/ui/layout/components/LazyImage";
66
import { cn } from "@/common/ui/layout/utils";
77

8-
import { ACCOUNT_PROFILE_COVER_IMAGE_PLACEHOLDER_SRC } from "../constants";
8+
import {
9+
ACCOUNT_PROFILE_COVER_IMAGE_PLACEHOLDER_SRC,
10+
ACCOUNT_PROFILE_IMAGE_PLACEHOLDER_SRC,
11+
} from "../constants";
912
import { useAccountSocialProfile } from "../hooks/social-profile";
1013

14+
const getInitial = (accountId: string) => accountId?.trim()?.[0]?.toUpperCase() ?? "?";
15+
1116
export type AccountProfilePictureProps = ByAccountId &
1217
LiveUpdateParams & {
1318
className?: string;
@@ -26,10 +31,13 @@ export const AccountProfilePicture: React.FC<AccountProfilePictureProps> = ({
2631
<Avatar className={cn("h-3 w-3", className)}>
2732
<AvatarImage
2833
alt={`Profile picture of ${accountId}`}
29-
src={avatar.url}
34+
src={avatar.url || ACCOUNT_PROFILE_IMAGE_PLACEHOLDER_SRC}
3035
width={40}
3136
height={40}
3237
/>
38+
<AvatarFallback className="bg-[#f0e7df] text-sm font-semibold text-[#7B7B7B]">
39+
{getInitial(accountId)}
40+
</AvatarFallback>
3341
</Avatar>
3442
);
3543
};

src/entities/campaign/components/CampaignProgressBar.tsx

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -210,25 +210,13 @@ export const CampaignProgressBar: React.FC<CampaignProgressBarProps> = ({
210210
minAmount={`${minAmountFloat} ${token?.metadata.symbol ?? ""}`}
211211
minValuePercentage={((): number | undefined => {
212212
if (!minAmountFloat || !targetAmountFloat) return undefined;
213+
if (isTargetMet) return undefined;
213214

214-
// Compute geometric min arrow position
215215
const rawMinPercent = (minAmountFloat / targetAmountFloat) * 100;
216216
const LEFT_PAD_PCT = 3;
217217
const RIGHT_PAD_PCT = 3;
218218

219-
const clampedMinPercent = Math.max(
220-
LEFT_PAD_PCT,
221-
Math.min(100 - RIGHT_PAD_PCT, rawMinPercent),
222-
);
223-
224-
const PASSED_DELTA = 0.5;
225-
226-
const minArrowPercent =
227-
progressExact >= clampedMinPercent
228-
? Math.min(clampedMinPercent, Math.max(LEFT_PAD_PCT, progressExact - PASSED_DELTA))
229-
: clampedMinPercent;
230-
231-
return minArrowPercent;
219+
return Math.max(LEFT_PAD_PCT, Math.min(100 - RIGHT_PAD_PCT, rawMinPercent));
232220
})()}
233221
value={progressPercentage}
234222
bgColor={color}

src/entities/campaign/components/editor.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { pinataHooks } from "@/common/services/pinata";
1313
import { CampaignId } from "@/common/types";
1414
import { TextAreaField, TextField } from "@/common/ui/form/components";
1515
import { RichTextEditor } from "@/common/ui/form/components/richtext";
16-
import { Button, Form, FormField, Switch } from "@/common/ui/layout/components";
16+
import { Button, Form, FormField, Spinner, Switch } from "@/common/ui/layout/components";
1717
import { cn } from "@/common/ui/layout/utils";
1818
import { useWalletUserSession } from "@/common/wallet";
1919
import {
@@ -918,8 +918,19 @@ export const CampaignEditor = ({ existingData, campaignId, close }: CampaignEdit
918918
)}
919919
</div>
920920
)}
921-
<Button variant="brand-filled" disabled={isDisabled} type="submit">
922-
{isUpdate ? "Update" : "Create"} Campaign
921+
<Button
922+
variant="brand-filled"
923+
disabled={isDisabled || form.formState.isSubmitting}
924+
type="submit"
925+
>
926+
{form.formState.isSubmitting ? (
927+
<span className="flex items-center gap-2">
928+
<Spinner className="h-4 w-4" />
929+
{isUpdate ? "Updating..." : "Creating..."}
930+
</span>
931+
) : (
932+
<>{isUpdate ? "Update" : "Create"} Campaign</>
933+
)}
923934
</Button>
924935
</div>
925936

src/entities/campaign/hooks/forms.ts

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -170,20 +170,18 @@ export const useCampaignForm = ({ campaignId, ftId, onUpdateSuccess }: CampaignF
170170
return new Date(time).getTime();
171171
};
172172

173-
const formatFullDateTime = (timestampMs: number) => {
174-
const date = new Date(timestampMs);
175-
const day = date.getDate();
173+
const formatRelativeFromNow = (timestampMs: number) => {
174+
const diffMs = timestampMs - Date.now();
175+
if (diffMs <= 0) return "now";
176176

177-
let suffix = "th";
178-
if (day % 10 === 1 && day !== 11) suffix = "st";
179-
else if (day % 10 === 2 && day !== 12) suffix = "nd";
180-
else if (day % 10 === 3 && day !== 13) suffix = "rd";
177+
const minutes = Math.round(diffMs / 60_000);
178+
if (minutes < 60) return `${minutes} minute${minutes === 1 ? "" : "s"}`;
181179

182-
const month = date.toLocaleString("en-US", { month: "long" });
183-
const year = date.getFullYear();
184-
const time = date.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" });
180+
const hours = Math.round(minutes / 60);
181+
if (hours < 24) return `${hours} hour${hours === 1 ? "" : "s"}`;
185182

186-
return `${day}${suffix} ${month} ${year}, ${time}`;
183+
const days = Math.round(hours / 24);
184+
return `${days} day${days === 1 ? "" : "s"}`;
187185
};
188186

189187
const handleDeleteCampaign = async () => {
@@ -367,7 +365,7 @@ export const useCampaignForm = ({ campaignId, ftId, onUpdateSuccess }: CampaignF
367365
};
368366

369367
if (campaignId) {
370-
campaignsContractClient
368+
return campaignsContractClient
371369
.update_campaign({
372370
args: { ...args, campaign_id: campaignId },
373371
})
@@ -378,12 +376,12 @@ export const useCampaignForm = ({ campaignId, ftId, onUpdateSuccess }: CampaignF
378376
self.reset(values, { keepErrors: false });
379377

380378
toast({
381-
title: `You’ve successfully updated this campaign`,
379+
title: "Campaign updated",
382380
description: (() => {
383381
const startMs = values.start_ms ? timeToMilliseconds(values.start_ms) : undefined;
384382

385383
if (startMs && startMs > Date.now()) {
386-
return `Campaign starts on ${formatFullDateTime(startMs)}.`;
384+
return `Campaign starts in ${formatRelativeFromNow(startMs)}.`;
387385
}
388386

389387
return "Campaign is live.";
@@ -406,7 +404,7 @@ export const useCampaignForm = ({ campaignId, ftId, onUpdateSuccess }: CampaignF
406404
});
407405
});
408406
} else {
409-
campaignsContractClient
407+
return campaignsContractClient
410408
.create_campaign({ args })
411409
.then(async (newCampaign) => {
412410
const startMs = values.start_ms ? timeToMilliseconds(values.start_ms) : undefined;
@@ -422,10 +420,10 @@ export const useCampaignForm = ({ campaignId, ftId, onUpdateSuccess }: CampaignF
422420
}
423421

424422
toast({
425-
title: `You’ve successfully created a campaign for ${values.name}.`,
423+
title: "Campaign created",
426424
description: (() => {
427425
if (startMs && startMs > Date.now()) {
428-
return `Campaign starts on ${formatFullDateTime(startMs)}.`;
426+
return `Campaign starts in ${formatRelativeFromNow(startMs)}.`;
429427
}
430428

431429
return "Campaign is live.";
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { HelpCircle } from "lucide-react";
2+
3+
const DOCS_URL = "https://docs.potlock.io";
4+
5+
export const FloatingDocsButton = () => (
6+
<a
7+
href={DOCS_URL}
8+
target="_blank"
9+
rel="noopener noreferrer"
10+
aria-label="Open documentation"
11+
title="Docs"
12+
className="fixed bottom-5 right-5 z-50 flex h-12 w-12 items-center justify-center rounded-full bg-[#dd3345] text-white shadow-lg transition-transform hover:scale-105 hover:bg-[#c52d3d] focus:outline-none focus:ring-2 focus:ring-[#dd3345] focus:ring-offset-2"
13+
>
14+
<HelpCircle className="h-6 w-6" />
15+
</a>
16+
);

src/pages/_app.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { cn } from "@/common/ui/layout/utils";
2424
import { WalletUserSessionProvider } from "@/common/wallet";
2525
import { AppBar } from "@/layout/components/app-bar";
2626
import { CampaignRouteLoading } from "@/layout/components/campaign-route-loading";
27+
import { FloatingDocsButton } from "@/layout/components/floating-docs-button";
2728
import { store } from "@/store";
2829

2930
const lora = Lora({
@@ -98,6 +99,7 @@ export default function RootLayout({ Component, pageProps }: AppPropsWithLayout)
9899
>
99100
<AppBar />
100101
<CampaignRouteLoading>{getLayout(<Component {...pageProps} />)}</CampaignRouteLoading>
102+
<FloatingDocsButton />
101103
</div>
102104
</TooltipProvider>
103105
</NiceModalProvider>

src/pages/campaigns.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import {
1515
import { cn } from "@/common/ui/layout/utils";
1616
import { useWalletUserSession } from "@/common/wallet";
1717
import { CampaignCarouselItem, CampaignsList } from "@/entities/campaign";
18+
import { rootPathnames } from "@/navigation";
19+
20+
const FEATURED_CAMPAIGN_ON_CHAIN_IDS = [131, 106, 101, 91];
1821

1922
export const FeaturedCampaigns = ({
2023
data,
@@ -26,6 +29,10 @@ export const FeaturedCampaigns = ({
2629
const [api, setApi] = useState<CarouselApi>();
2730
const [current, setCurrent] = useState(0);
2831

32+
const featuredCampaigns = (data ?? []).filter((c) =>
33+
FEATURED_CAMPAIGN_ON_CHAIN_IDS.includes(c?.on_chain_id),
34+
);
35+
2936
useEffect(() => {
3037
if (!api) return;
3138

@@ -42,7 +49,7 @@ export const FeaturedCampaigns = ({
4249
return () => clearInterval(interval);
4350
}, [api]);
4451

45-
if (!data?.length) {
52+
if (!featuredCampaigns.length) {
4653
return <></>;
4754
}
4855

@@ -53,7 +60,9 @@ export const FeaturedCampaigns = ({
5360
<h1 className="text-sm font-medium uppercase leading-6 tracking-[1.12px] text-[#292929]">
5461
Featured Campaigns
5562
</h1>
56-
<p className="text-[18px]">{current + 1}/4</p>
63+
<p className="text-[18px]">
64+
{current + 1}/{featuredCampaigns.length}
65+
</p>
5766
</div>
5867
<div className="flex items-center gap-4">
5968
<div className="flex gap-4">
@@ -72,7 +81,7 @@ export const FeaturedCampaigns = ({
7281
</div>
7382
{showViewAll && (
7483
<Button asChild variant="brand-tonal" className="h-8 shrink-0 bg-transparent text-xs">
75-
<Link href="/campaigns" className="text-brand-primary">
84+
<Link href={rootPathnames.CAMPAIGNS} className="text-brand-primary">
7685
VIEW ALL
7786
</Link>
7887
</Button>
@@ -81,10 +90,9 @@ export const FeaturedCampaigns = ({
8190
</div>
8291
<Carousel opts={{ loop: true }} setApi={setApi}>
8392
<CarouselContent>
84-
{data?.length &&
85-
data
86-
?.filter((data) => [131, 106, 101, 91].includes(data?.on_chain_id))
87-
?.map((data) => <CampaignCarouselItem key={data.on_chain_id} data={data} />)}
93+
{featuredCampaigns.map((c) => (
94+
<CampaignCarouselItem key={c.on_chain_id} data={c} />
95+
))}
8896
</CarouselContent>
8997
</Carousel>
9098
</div>

src/pages/feed/[account]/[block]/index.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import remarkGfm from "remark-gfm";
66

77
import { fetchSinglePost, fetchTimeByBlockHeight } from "@/common/api/near-social-indexer";
88
import { IPFS_NEAR_SOCIAL_URL } from "@/common/constants";
9+
import { Skeleton } from "@/common/ui/layout/components";
910
import { LazyImage } from "@/common/ui/layout/components/LazyImage";
1011
import { AccountProfilePicture } from "@/entities/_shared/account";
1112

@@ -43,7 +44,26 @@ export default function FeedAccountBlockPostPage() {
4344
}, [account, block]);
4445

4546
if (isLoading) {
46-
return <div>Loading...</div>;
47+
return (
48+
<div
49+
style={{ boxShadow: "0px 8px 24px rgba(149, 157, 165, 0.2)" }}
50+
className="2xl-container w-full rounded-2xl p-8 px-5 pb-12 md:px-10"
51+
>
52+
<div className="mb-4 flex items-center space-x-2">
53+
<Skeleton className="h-8 w-8 rounded-full" />
54+
<Skeleton className="h-5 w-40" />
55+
<Skeleton className="h-4 w-24" />
56+
</div>
57+
<Skeleton className="mb-3 h-4 w-1/3" />
58+
<div className="space-y-2">
59+
<Skeleton className="h-4 w-full" />
60+
<Skeleton className="h-4 w-11/12" />
61+
<Skeleton className="h-4 w-10/12" />
62+
<Skeleton className="h-4 w-8/12" />
63+
</div>
64+
<Skeleton className="mt-6 h-64 w-full" />
65+
</div>
66+
);
4767
}
4868

4969
if (!post) {

0 commit comments

Comments
 (0)