Skip to content

Commit

Permalink
feat: optimize ui
Browse files Browse the repository at this point in the history
  • Loading branch information
dudu0506 committed Sep 9, 2024
1 parent d23a323 commit a609165
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 80 deletions.
Binary file added public/bg.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/checked.png
Binary file not shown.
Binary file added public/selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/app/api/frames/images/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { IMAGE_ZOOM_SCALE } from '@/constants';
const imagesWorker = createImagesWorker({
imageOptions: {
sizes: {
'1.91:1': { width: 600 * IMAGE_ZOOM_SCALE, height: 400 * IMAGE_ZOOM_SCALE },
'1:1': { width: 600 * IMAGE_ZOOM_SCALE, height: 400 * IMAGE_ZOOM_SCALE },
'1.91:1': { width: 764 * IMAGE_ZOOM_SCALE, height: 400 * IMAGE_ZOOM_SCALE },
'1:1': { width: 600 * IMAGE_ZOOM_SCALE, height: 600 * IMAGE_ZOOM_SCALE },
},
},
});
Expand Down
205 changes: 144 additions & 61 deletions src/components/PollCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,32 @@ function VoteButton({ text, theme }: VoteButtonProps) {
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.optionTextColor,
height: 40 * IMAGE_ZOOM_SCALE,
color: theme.secondTextColor,
height: 20 * IMAGE_ZOOM_SCALE,
width: '100%',
borderRadius: 10 * IMAGE_ZOOM_SCALE,
fontSize: 16 * IMAGE_ZOOM_SCALE,
fontWeight: 'bold',
backgroundColor: theme.optionBgColor,
fontSize: 18 * IMAGE_ZOOM_SCALE,
}}
>
{text}
<span
style={{
marginRight: 9 * IMAGE_ZOOM_SCALE,
width: 12 * IMAGE_ZOOM_SCALE,
height: 12 * IMAGE_ZOOM_SCALE,
borderRadius: '50%',
backgroundColor: theme.secondTextColor,
}}
/>
<div
style={{
display: 'flex',
maxWidth: '90%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
{text}
</div>
</div>
);
}
Expand All @@ -52,57 +67,64 @@ function VoteResult({ choice, theme, isMax }: VoteResultProps) {
style={{
position: 'relative',
display: 'flex',
height: 40 * IMAGE_ZOOM_SCALE,
height: 20 * IMAGE_ZOOM_SCALE,
backgroundColor: theme.optionBgColor,
borderRadius: 4 * IMAGE_ZOOM_SCALE,
fontSize: 12 * IMAGE_ZOOM_SCALE,
color: theme.secondTextColor,
overflow: 'hidden',
}}
>
<div
style={{
display: 'flex',
width: choice.percent ? `${choice.percent}%` : `${10 * IMAGE_ZOOM_SCALE}px`,
position: 'absolute',
left: 0,
top: 0,
display: 'flex',
zIndex: 0,
height: '100%',
borderRadius: 10 * IMAGE_ZOOM_SCALE,
backgroundColor: choice.is_select ? theme.optionSelectedBgColor : theme.optionBgColor,
width: `${choice.percent}%`,
backgroundColor: isMax ? theme.optionSelectedBgColor : theme.optionBgColor,
}}
/>
<div
style={{
position: 'absolute',
left: 0,
top: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: 20 * IMAGE_ZOOM_SCALE,
height: '100%',
alignItems: 'center',
zIndex: 1,
width: '100%',
fontSize: 16 * IMAGE_ZOOM_SCALE,
fontWeight: 'bold',
color: choice.is_select ? theme.optionSelectedTextColor : theme.optionTextColor,
height: '100%',
paddingLeft: 4 * IMAGE_ZOOM_SCALE,
paddingRight: 8 * IMAGE_ZOOM_SCALE,
}}
>
<span
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8 * IMAGE_ZOOM_SCALE,
width: '86%',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
}}
>
<span style={{ display: 'flex' }}>{choice.name}</span>
{choice.is_select ? (
// eslint-disable-next-line @next/next/no-img-element
<img

Check warning on line 116 in src/components/PollCard.tsx

View workflow job for this annotation

GitHub Actions / eslint

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
alt={choice.name}
src={`${env.external.NEXT_PUBLIC_HOST}/checked.png`}
alt="selected"
style={{
display: 'flex',
width: 20 * IMAGE_ZOOM_SCALE,
height: 20 * IMAGE_ZOOM_SCALE,
width: 16 * IMAGE_ZOOM_SCALE,
height: 16 * IMAGE_ZOOM_SCALE,
}}
src={`${env.external.NEXT_PUBLIC_HOST}/selected.png`}
/>
) : null}
</span>
<span style={{ display: 'flex', color: isMax ? theme.optionTextColor : theme.percentColor }}>
{choice.percent}%
</span>
{choice.name}
</div>
<span>{choice.percent}%</span>
</div>
</div>
);
Expand All @@ -114,62 +136,123 @@ export function PollCard({ poll, locale, profileId }: PollCardProps) {
const t = createFrameTranslator(locale);

const maxPercent = Math.max(...choice_detail.map((choice) => choice.percent || 0));
const showResult = is_end || choice_detail.some((choice) => choice.is_select);

return (
<div
style={{
display: 'flex',
alignItems: 'flex-end',
justifyContent: 'center',
flexDirection: 'column',
justifyContent: 'space-between',
width: '100%',
height: '100%',
backgroundColor: themeConfig.cardBgColor,
lineHeight: 1.2,
fontSize: 24 * IMAGE_ZOOM_SCALE,
backgroundImage: `url(${env.external.NEXT_PUBLIC_HOST}/bg.jpeg)`,
backgroundSize: '100% 100%',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
width: '100%',
gap: 12 * IMAGE_ZOOM_SCALE,
padding: 20 * IMAGE_ZOOM_SCALE,
}}
>
{choice_detail.map((choice, index) =>
(!!profileId && is_end) || choice_detail.some((choice) => choice.is_select) ? (
<VoteResult
key={index}
choice={choice}
theme={themeConfig}
isMax={!!choice.percent && choice.percent === maxPercent}
/>
) : (
<VoteButton key={index} theme={themeConfig} text={choice.name} />
),
)}
<div
style={{
display: 'flex',
marginTop: 120 * IMAGE_ZOOM_SCALE,
justifyContent: 'center',
width: '100%',
fontSize: 12 * IMAGE_ZOOM_SCALE,
color: is_end ? themeConfig.secondTextColor : themeConfig.optionSelectedTextColor,
opacity: is_end ? 0.5 : 1,
}}
>
{t`${vote_count} vote${vote_count !== 1 ? 'S' : ''}`} · {getPollTimeLeft(poll, locale)}
</div>
<div
style={{
display: 'flex',
justifyContent: 'center',
marginTop: (profileId ? 6 : 30) * IMAGE_ZOOM_SCALE,
padding: `0 ${72 * IMAGE_ZOOM_SCALE}px`,
width: '100%',
color: themeConfig.secondTextColor,
fontSize: 16 * IMAGE_ZOOM_SCALE,
textAlign: 'center',
}}
>
<div style={{
display: '-webkit-box',
WebkitLineClamp: profileId ? 2 : 5,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
textOverflow: 'ellipsis',
wordBreak: 'break-word',
maxWidth: '100%',
}}>
{poll.title}
</div>
</div>
{profileId ? (
<div
style={{
display: 'flex',
gap: 4 * IMAGE_ZOOM_SCALE,
justifyContent: 'center',
marginTop: 12 * IMAGE_ZOOM_SCALE,
}}
>
{[0, 1, 2].map((index) => (
<span
key={index}
style={{
width: 4 * IMAGE_ZOOM_SCALE,
height: 4 * IMAGE_ZOOM_SCALE,
backgroundColor: themeConfig.secondTextColor,
}}
/>
))}
</div>
) : null}
</div>
{profileId ? (
<div
style={{
display: 'flex',
flexDirection: 'column',
width: '100%',
gap: 7 * IMAGE_ZOOM_SCALE,
padding: `0 ${114 * IMAGE_ZOOM_SCALE}px`,
}}
>
{choice_detail.map((choice, index) => {
return showResult ? (
<VoteResult
key={index}
choice={choice}
theme={themeConfig}
isMax={!!choice.percent && choice.percent === maxPercent}
/>
) : (
<VoteButton key={index} text={choice.name} theme={themeConfig} />
);
})}
</div>
) : null}
<div
style={{
display: 'flex',
justifyContent: 'space-between',
marginTop: 40 * IMAGE_ZOOM_SCALE,
padding: 20 * IMAGE_ZOOM_SCALE,
width: '100%',
justifyContent: 'center',
paddingBottom: 16 * IMAGE_ZOOM_SCALE,
fontSize: 10 * IMAGE_ZOOM_SCALE,
color: themeConfig.secondTextColor,
fontSize: 12 * IMAGE_ZOOM_SCALE,
}}
>
<span>
{profileId ? (
<span>
{t`${vote_count} vote${vote_count !== 1 ? 's' : ''}`} · {getPollTimeLeft(poll, locale)}
</span>
) : null}
</span>
<span>{t`via Firefly`}</span>
{t`Via Firefly`}
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/constants/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export enum IMAGE_THEME {

export const DARK_THEME: PollTheme = {
titleColor: '#f5f5f5',
optionBgColor: '#ffffff21',
optionBgColor: 'rgba(172, 157, 246, 0.2)',
optionTextColor: '#f5f5f5',
optionSelectedTextColor: '#AC9DF6',
optionSelectedBgColor: '#ffffff21',
optionSelectedBgColor: '#AC9DF6',
cardBgColor: '#181818',
secondTextColor: '#ffffff',
percentColor: '#FFFFFF70',
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/getPollTimeLeft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ export const getPollTimeLeft = (poll: Poll, locale: LOCALE) => {

const days = Math.floor(timeLeft / (1000 * 60 * 60 * 24));
if (days >= 1) {
return t`${days} day${days > 1 ? 's' : ''} left`;
return t`${days} day${days > 1 ? 'S' : ''} left`;
}

const hours = Math.floor(timeLeft / (1000 * 60 * 60));
if (hours >= 1) {
return t`${hours} hour${hours > 1 ? 's' : ''} left`;
return t`${hours} hour${hours > 1 ? 'S' : ''} left`;
}

const minutes = Math.floor(timeLeft / (1000 * 60));
if (minutes >= 1) {
return t`${minutes} minute${minutes > 1 ? 's' : ''} left`;
return t`${minutes} minute${minutes > 1 ? 'S' : ''} left`;
}

return t`Less than a minute left`;
Expand Down
3 changes: 2 additions & 1 deletion src/helpers/parsePollWithZod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ export const parsePollWithZod = (poll: Poll | null, locale: LOCALE, currentVoteI
.object(
{
poll_id: z.string(),
title: z.string(),
created_time: z.number().int().positive(),
end_time: z.number().int().positive(),
is_end: z.boolean(),
vote_count: z.number().int().min(0),
type: z.nativeEnum(POLL_CHOICE_TYPE),
multiple_count: z.number().int().min(0).optional().default(PER_USER_VOTE_LIMIT),
multiple_count: z.number().int().min(0).default(PER_USER_VOTE_LIMIT).catch(PER_USER_VOTE_LIMIT),
choice_detail: z.array(
z.object({
id: z.number().int().positive(),
Expand Down
14 changes: 7 additions & 7 deletions src/locales/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ const locales = {
'No poll found': 'No poll found',
'No profile found': 'No profile found',
Expired: 'Expired',
'_ day_ left': '{0} day{1} left',
'_ hour_ left': '{0} hour{1} left',
'_ minute_ left': '{0} minute{1} left',
'Less than a minute left': 'Less than a minute left',
'_ day_ left': '{0} DAY{1} LEFT',
'_ hour_ left': '{0} HOUR{1} LEFT',
'_ minute_ left': '{0} MINUTE{1} LEFT',
'Less than a minute left': 'LESS THAN A MINUTE LEFT',
'Please fill all fields': 'Please fill all fields',
'An error occurred: _': 'An error occurred: {0}',
Question: 'Question',
'Option _': 'Option {0}',
'Create Poll': 'Create Poll',
'Creating...': 'Creating...',
'Try again': 'Try again',
'_ vote_': '{0} Vote{1}',
'via Firefly': 'via Firefly',
'Final results': 'Final results',
'_ vote_': '{0} VOTE{1}',
'Via Firefly': 'Via Firefly',
'Final results': 'POLL HAS ENDED',
'Vote Now': 'Vote Now',
'Go to Firefly': 'Go to Firefly',
'Not supported frame client protocol': 'Not supported frame client protocol',
Expand Down
4 changes: 2 additions & 2 deletions src/locales/zh-Hans/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const locales = {
'Creating...': '创建中...',
'Try again': '重试',
'_ vote_': '{0} 次投票',
'via Firefly': '来自Firefly',
'Final results': '最终结果',
'Via Firefly': '来自Firefly',
'Final results': '投票已结束',
'Vote Now': '立即投票',
'Go to Firefly': '前往Firefly',
'Not supported frame client protocol': '不支持的frame客户端协议',
Expand Down
4 changes: 2 additions & 2 deletions src/locales/zh-Hant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const locales = {
'Creating...': '創建中...',
'Try again': '重試',
'_ vote_': '{0} 次投票',
'via Firefly': '來自Firefly',
'Final results': '最終結果',
'Via Firefly': '來自Firefly',
'Final results': '投票已結束',
'Vote Now': '立即投票',
'Go to Firefly': '前往Firefly',
'Not supported frame client protocol': '不支持的frame客戶端協議',
Expand Down
Loading

0 comments on commit a609165

Please sign in to comment.