-
Notifications
You must be signed in to change notification settings - Fork 19
feat: add the open-collaborator-award page #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,154 @@ | ||||||||||||||||||||||||||
| .hero { | ||||||||||||||||||||||||||
| position: relative; | ||||||||||||||||||||||||||
| background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%); | ||||||||||||||||||||||||||
| padding: 5rem 0; | ||||||||||||||||||||||||||
| overflow: hidden; | ||||||||||||||||||||||||||
| color: #fff; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| &::before { | ||||||||||||||||||||||||||
| position: absolute; | ||||||||||||||||||||||||||
| top: -50%; | ||||||||||||||||||||||||||
| left: -50%; | ||||||||||||||||||||||||||
| animation: grid-animation 20s linear infinite; | ||||||||||||||||||||||||||
| background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 1px, transparent 1px); | ||||||||||||||||||||||||||
| background-size: 50px 50px; | ||||||||||||||||||||||||||
| width: 200%; | ||||||||||||||||||||||||||
| height: 200%; | ||||||||||||||||||||||||||
| pointer-events: none; | ||||||||||||||||||||||||||
| content: ''; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| @keyframes grid-animation { | ||||||||||||||||||||||||||
| to { | ||||||||||||||||||||||||||
| transform: translate(50px, 50px); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .heroContent { | ||||||||||||||||||||||||||
| position: relative; | ||||||||||||||||||||||||||
| z-index: 1; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .heroTitle { | ||||||||||||||||||||||||||
| font-weight: 700; | ||||||||||||||||||||||||||
| font-size: clamp(2.25rem, 6vw, 3.5rem); | ||||||||||||||||||||||||||
| text-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .heroDescription { | ||||||||||||||||||||||||||
| opacity: 0.9; | ||||||||||||||||||||||||||
| margin: 1rem auto 2rem; | ||||||||||||||||||||||||||
| max-width: 50rem; | ||||||||||||||||||||||||||
| font-size: 1.2rem; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .statNumber { | ||||||||||||||||||||||||||
| font-weight: 700; | ||||||||||||||||||||||||||
| font-size: clamp(2rem, 6vw, 3.5rem); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .section { | ||||||||||||||||||||||||||
| padding: 4rem 0; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .mutedSection { | ||||||||||||||||||||||||||
| background: #f8f9fa; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .sectionTitle { | ||||||||||||||||||||||||||
| margin-bottom: 2rem; | ||||||||||||||||||||||||||
| font-weight: 700; | ||||||||||||||||||||||||||
| text-align: center; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .infoCard, | ||||||||||||||||||||||||||
| .ruleCard, | ||||||||||||||||||||||||||
| .recipientCard, | ||||||||||||||||||||||||||
| .nomineeCard, | ||||||||||||||||||||||||||
| .faqCard { | ||||||||||||||||||||||||||
| box-shadow: 0 0.25rem 1rem rgba(15, 23, 42, 0.08); | ||||||||||||||||||||||||||
| border: 1px solid rgba(15, 23, 42, 0.06); | ||||||||||||||||||||||||||
| border-radius: 1rem; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .recipientCard { | ||||||||||||||||||||||||||
| display: flex; | ||||||||||||||||||||||||||
| flex-direction: column; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .initiative { | ||||||||||||||||||||||||||
| background: linear-gradient(135deg, #667eea, #764ba2); | ||||||||||||||||||||||||||
| color: #fff; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .videoWrapper { | ||||||||||||||||||||||||||
| position: relative; | ||||||||||||||||||||||||||
| background: #000; | ||||||||||||||||||||||||||
| padding-bottom: 56.25%; | ||||||||||||||||||||||||||
| height: 0; | ||||||||||||||||||||||||||
| overflow: hidden; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| iframe { | ||||||||||||||||||||||||||
| position: absolute; | ||||||||||||||||||||||||||
| inset: 0; | ||||||||||||||||||||||||||
| border: 0; | ||||||||||||||||||||||||||
| width: 100%; | ||||||||||||||||||||||||||
| height: 100%; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+92
to
+98
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
为何一定要悬浮呢? |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .initiativeVideo { | ||||||||||||||||||||||||||
| border-radius: 1rem; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .videoFallback { | ||||||||||||||||||||||||||
| background: linear-gradient(135deg, #667eea, #764ba2); | ||||||||||||||||||||||||||
| min-height: 14rem; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .ruleNumber { | ||||||||||||||||||||||||||
| display: inline-flex; | ||||||||||||||||||||||||||
| justify-content: center; | ||||||||||||||||||||||||||
| align-items: center; | ||||||||||||||||||||||||||
| margin-bottom: 1rem; | ||||||||||||||||||||||||||
| border-radius: 50%; | ||||||||||||||||||||||||||
| background: linear-gradient(135deg, #667eea, #764ba2); | ||||||||||||||||||||||||||
| width: 3rem; | ||||||||||||||||||||||||||
| height: 3rem; | ||||||||||||||||||||||||||
| color: #fff; | ||||||||||||||||||||||||||
| font-weight: 700; | ||||||||||||||||||||||||||
| font-size: 1.25rem; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .reason { | ||||||||||||||||||||||||||
| margin-bottom: 1rem; | ||||||||||||||||||||||||||
| border-left: 0.25rem solid #667eea; | ||||||||||||||||||||||||||
| border-radius: 0.5rem; | ||||||||||||||||||||||||||
| background: #f8f9fa; | ||||||||||||||||||||||||||
| padding: 1rem; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .progressTrack { | ||||||||||||||||||||||||||
| border-radius: 999px; | ||||||||||||||||||||||||||
| background: #e9ecef; | ||||||||||||||||||||||||||
| height: 0.5rem; | ||||||||||||||||||||||||||
| overflow: hidden; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .progressFill { | ||||||||||||||||||||||||||
| transition: width 0.3s ease; | ||||||||||||||||||||||||||
| background: linear-gradient(90deg, #667eea, #764ba2); | ||||||||||||||||||||||||||
| height: 100%; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .voteCount { | ||||||||||||||||||||||||||
| color: #667eea; | ||||||||||||||||||||||||||
| font-size: 1.25rem; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| .nominationForm { | ||||||||||||||||||||||||||
| border: 0; | ||||||||||||||||||||||||||
| width: 100%; | ||||||||||||||||||||||||||
| min-height: min(75vh, 50rem); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,118 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { FC, useContext } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Badge, Button, Card } from 'react-bootstrap'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Award } from '../../models/Award'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { I18nContext } from '../../models/Translation'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import styles from './Award.module.less'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // cspell:ignore bilibili | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| export const AWARD_VOTE_THRESHOLD = 10; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| export const voteCountOf = ({ votes }: Pick<Award, 'votes'>) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const count = Number(votes); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return Number.isFinite(count) && count > 0 ? Math.floor(count) : 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const bilibiliURLOf = (value: Award['videoUrl']) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = new URL(value?.toString() || ''), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| { hostname } = url; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| url.protocol !== 'https:' || | ||||||||||||||||||||||||||||||||||||||||||||||||||
| !(hostname === 'bilibili.com' || hostname.endsWith('.bilibili.com') || hostname === 'b23.tv') | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return url; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const dateTextOf = (value: Award['createdAt'], locale: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const rawValue = value?.toString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!rawValue) return ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const timestamp = Number(rawValue), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| normalizedTimestamp = timestamp < 1_000_000_000_000 ? timestamp * 1000 : timestamp, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| date = new Date(Number.isNaN(timestamp) ? rawValue : normalizedTimestamp), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| dateLocale = ['zh-CN', 'zh-TW', 'en-US'].includes(locale) ? locale : 'zh-CN'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return Number.isNaN(date.valueOf()) ? rawValue : date.toLocaleDateString(dateLocale); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface AwardCardProps extends Award { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| className?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| export const AwardCard: FC<AwardCardProps> = ({ className = '', ...award }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const i18n = useContext(I18nContext), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| { t, currentLanguage } = i18n; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const { awardName, nomineeName, nomineeDesc, videoUrl, reason, nominator, createdAt } = award, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+52
to
+55
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| votes = voteCountOf(award), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| winner = votes >= AWARD_VOTE_THRESHOLD, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| videoURL = bilibiliURLOf(videoUrl), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| bilibiliId = videoURL?.pathname.match(/BV[0-9A-Za-z]{10}/)?.[0], | ||||||||||||||||||||||||||||||||||||||||||||||||||
| progress = Math.min((votes / AWARD_VOTE_THRESHOLD) * 100, 100), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| nominee = nomineeName?.toString() || t('award_unnamed_nominee'); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <Card | ||||||||||||||||||||||||||||||||||||||||||||||||||
| id={`award-${award.id}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| as="article" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| className={`${styles.nomineeCard} h-100 ${className}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {bilibiliId ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.videoWrapper}> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <iframe | ||||||||||||||||||||||||||||||||||||||||||||||||||
| src={`https://player.bilibili.com/player.html?bvid=${bilibiliId}&page=1&high_quality=1&danmaku=0&autoplay=0`} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| title={`${t('award_nomination_video_title')}: ${nominee}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| loading="lazy" | ||||||||||||||||||||||||||||||||||||||||||||||||||
| allowFullScreen | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : videoURL ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={`${styles.videoFallback} d-flex align-items-center justify-content-center`}> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button href={videoURL.href} target="_blank" rel="noreferrer" variant="light"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {t('award_watch_nomination_video')} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : null} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+84
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| <Card.Body className="d-flex flex-column"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="mb-2 d-flex align-items-start justify-content-between gap-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <Badge bg="primary">{awardName?.toString() || t('open_collaborator_award')}</Badge> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {winner && <Badge bg="success">{t('award_recognized')}</Badge>} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| <Card.Title as="h3">{nominee}</Card.Title> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {nomineeDesc && <Card.Text className="text-muted">{nomineeDesc.toString()}</Card.Text>} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| {reason && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.reason}> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>{t('award_nomination_reason')}</strong> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="mb-0 mt-2">{reason.toString()}</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| <footer className="mt-auto pt-3"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="text-muted small"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {t('award_nominated_by')}: {nominator?.toString() || t('award_anonymous')} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {createdAt && ` · ${dateTextOf(createdAt, currentLanguage)}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.progressTrack}> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className={styles.progressFill} style={{ width: `${progress}%` }} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+107
to
+109
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 如果没有很特殊的样式,可以直接用 HTML 5 的 |
||||||||||||||||||||||||||||||||||||||||||||||||||
| <p className="mb-0 mt-2 small text-muted"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong className={styles.voteCount}>{votes}</strong> / {AWARD_VOTE_THRESHOLD}{' '} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| {t('award_support_count')} | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </footer> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </Card.Body> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| </Card> | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,39 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { FC, useContext } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Button, Modal } from 'react-bootstrap'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { I18nContext } from '../../models/Translation'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import styles from './Award.module.less'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const AWARD_NOMINATION_FORM_URL = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'https://open-source-bazaar.feishu.cn/share/base/form/shrcniqv1nEnCrygFy0qX4fPcSg'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface NominationFormProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| show: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onHide: () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const NominationForm: FC<NominationFormProps> = ({ show, onHide }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { t } = useContext(I18nContext); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal show={show} size="lg" centered scrollable onHide={onHide}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2
to
+19
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
使用 MobX-i18n 的所有组件都要变成 MobX 的 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Header closeButton closeLabel={t('award_close')}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Title>{t('award_submit_nomination')}</Modal.Title> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal.Header> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Body className="p-0"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <iframe | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className={styles.nominationForm} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src={AWARD_NOMINATION_FORM_URL} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title={t('award_nomination_form_title')} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loading="lazy" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal.Body> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Modal.Footer> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <small className="me-auto text-muted">{t('award_form_refresh_hint')}</small> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button variant="secondary" onClick={onHide}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {t('award_close')} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal.Footer> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Modal> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,41 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { BiDataQueryOptions, BiDataTable, BiSearch, TableCellValue } from 'mobx-lark'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| BiDataQueryOptions, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| BiDataTable, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| BiSearch, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TableCellLink, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TableCellRelation, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TableCellUser, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TableCellValue, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| TableRecord, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from 'mobx-lark'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { larkClient } from './Base'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AwardTableId, LarkBitableId } from './configuration'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // cspell:ignore bilibili | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const bilibiliLinkOf = (value: TableCellValue) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { link = '', text = '' } = (value || {}) as Partial<TableCellLink>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| rawValue = `${link} ${text}`.trim() || value?.toString() || ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| rawValue.match(/https:\/\/(?:[\w-]+\.)?bilibili\.com\/\S+|https:\/\/b23\.tv\/\S+/i)?.[0] || | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| rawValue | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const relationCountOf = (value: TableCellValue) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!(value instanceof Array)) return value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return value.reduce( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| (count, relation) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| count + (((relation as TableCellRelation)?.record_ids || []) as string[]).length, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+35
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type Award = Record< | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| | 'id' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| | 'awardName' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| | `nominee${'Name' | 'Desc'}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| | 'videoUrl' | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -22,6 +54,21 @@ export class AwardModel extends BiDataTable<Award>() { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor(appId = LarkBitableId, tableId = AwardTableId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| super(appId, tableId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| extractFields({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| created_time, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fields: { videoUrl, nominator, votes, createdAt, ...fields }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: TableRecord<Award>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...fields, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| createdAt: createdAt ?? created_time, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| nominator: (nominator as TableCellUser)?.name || nominator, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| videoUrl: bilibiliLinkOf(videoUrl), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| votes: relationCountOf(votes), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+58
to
+71
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class SearchAwardModel extends BiSearch<Award>(AwardModel) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
优先用各类 Bootstrap CSS 工具类:https://getbootstrap.com/docs/5.3/utilities/