Skip to content

Commit 18d81a5

Browse files
committed
[refactor] isolate Internationalization data with MobX-i18n 0.7 & React context
[optimize] reduce Dependency conflicts with Independent Configuration module [optimize] use TS for Next.js & ESLint configuration [migrate] upgrade to PNPM 10, Next-SSR-middleware 1.0 & other latest Upstream packages Signed-off-by: TechQuery <[email protected]>
1 parent 5ebda87 commit 18d81a5

File tree

94 files changed

+5301
-4886
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+5301
-4886
lines changed

components/Activity/ActivityCard.tsx

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import {
2-
faCalendarDay,
3-
faMapLocationDot,
4-
faTags,
5-
} from '@fortawesome/free-solid-svg-icons';
1+
import { faCalendarDay, faMapLocationDot, faTags } from '@fortawesome/free-solid-svg-icons';
62
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
73
import { Hackathon } from '@kaiyuanshe/openhackathon-service';
84
import classNames from 'classnames';
5+
import { FC, useContext } from 'react';
96
import { Card, Col, Row } from 'react-bootstrap';
107

11-
import { t } from '../../models/Base/Translation';
8+
import { I18nContext } from '../../models/Base/Translation';
129
import { convertDatetime } from '../../utils/time';
1310
import { ActivityControl, ActivityControlProps } from './ActivityControl';
1411
import { ActivityEntry } from './ActivityEntry';
@@ -18,7 +15,7 @@ export interface ActivityCardProps extends Hackathon, ActivityControlProps {
1815
controls?: boolean;
1916
}
2017

21-
export function ActivityCard({
18+
export const ActivityCard: FC<ActivityCardProps> = ({
2219
className,
2320
controls,
2421
name,
@@ -32,8 +29,9 @@ export function ActivityCard({
3229
onPublish,
3330
onDelete,
3431
...rest
35-
}: ActivityCardProps) {
36-
const eventStartedAtText = convertDatetime(eventStartedAt);
32+
}) => {
33+
const { t } = useContext(I18nContext),
34+
eventStartedAtText = convertDatetime(eventStartedAt);
3735

3836
return (
3937
<Card className={classNames('border-success', className)}>
@@ -47,16 +45,11 @@ export function ActivityCard({
4745
{displayName}
4846
</Card.Title>
4947
<Row as="small" className="g-4" xs={1}>
50-
<Col
51-
className="text-truncate border-bottom pb-2"
52-
title={eventStartedAtText}
53-
>
54-
<FontAwesomeIcon className="text-success" icon={faCalendarDay} />{' '}
55-
{eventStartedAtText}
48+
<Col className="text-truncate border-bottom pb-2" title={eventStartedAtText}>
49+
<FontAwesomeIcon className="text-success" icon={faCalendarDay} /> {eventStartedAtText}
5650
</Col>
5751
<Col className="text-truncate border-bottom pb-2" title={location}>
58-
<FontAwesomeIcon className="text-success" icon={faMapLocationDot} />{' '}
59-
{location}
52+
<FontAwesomeIcon className="text-success" icon={faMapLocationDot} /> {location}
6053
</Col>
6154
</Row>
6255
<Card.Text className="text-success mt-2">
@@ -80,11 +73,9 @@ export function ActivityCard({
8073
{controls ? (
8174
<ActivityControl {...{ name, status, onPublish, onDelete }} />
8275
) : (
83-
<ActivityEntry
84-
{...{ ...rest, status, eventStartedAt, href: `/activity/${name}` }}
85-
/>
76+
<ActivityEntry {...{ ...rest, status, eventStartedAt, href: `/activity/${name}` }} />
8677
)}
8778
</Card.Footer>
8879
</Card>
8980
);
90-
}
81+
};
Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,35 @@
11
import { Hackathon } from '@kaiyuanshe/openhackathon-service';
22
import { observer } from 'mobx-react';
3-
import { FC } from 'react';
3+
import { FC, useContext } from 'react';
44
import { Button } from 'react-bootstrap';
55

6-
import { t } from '../../models/Base/Translation';
6+
import { I18nContext } from '../../models/Base/Translation';
77
import platformAdmin from '../../models/User/PlatformAdmin';
88

9-
export interface ActivityControlProps
10-
extends Pick<Hackathon, 'name' | 'status'> {
9+
export interface ActivityControlProps extends Pick<Hackathon, 'name' | 'status'> {
1110
onPublish?: (name: string) => any;
1211
onDelete?: (name: string) => any;
1312
}
1413

1514
export const ActivityControl: FC<ActivityControlProps> = observer(
16-
({ name, status, onPublish, onDelete }) => (
17-
<>
18-
<Button
19-
className="w-100 mt-2"
20-
variant="info"
21-
href={`/activity/${name}/manage/edit`}
22-
>
23-
{t('manage_this_hackathon')}
24-
</Button>
25-
{status !== 'online' ? (
26-
<Button
27-
className="w-100 mt-2"
28-
variant="success"
29-
onClick={() => onPublish?.(name)}
30-
>
31-
{platformAdmin.isPlatformAdmin ? t('publish') : t('apply_publish')}
32-
</Button>
33-
) : (
34-
<Button
35-
className="w-100 mt-2"
36-
variant="warning"
37-
onClick={() => onDelete?.(name)}
38-
>
39-
{t('offline')}
15+
({ name, status, onPublish, onDelete }) => {
16+
const { t } = useContext(I18nContext);
17+
18+
return (
19+
<>
20+
<Button className="w-100 mt-2" variant="info" href={`/activity/${name}/manage/edit`}>
21+
{t('manage_this_hackathon')}
4022
</Button>
41-
)}
42-
</>
43-
),
23+
{status !== 'online' ? (
24+
<Button className="w-100 mt-2" variant="success" onClick={() => onPublish?.(name)}>
25+
{platformAdmin.isPlatformAdmin ? t('publish') : t('apply_publish')}
26+
</Button>
27+
) : (
28+
<Button className="w-100 mt-2" variant="warning" onClick={() => onDelete?.(name)}>
29+
{t('offline')}
30+
</Button>
31+
)}
32+
</>
33+
);
34+
},
4435
);

components/Activity/ActivityEditor.tsx

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ import { Loading } from 'idea-react';
33
import { observable } from 'mobx';
44
import { textJoin } from 'mobx-i18n';
55
import { observer } from 'mobx-react';
6+
import { ObservedComponent } from 'mobx-react-helper';
67
import { BadgeInput, FileUploader } from 'mobx-restful-table';
78
import dynamic from 'next/dynamic';
8-
import { Component, FormEvent } from 'react';
9+
import { FormEvent } from 'react';
910
import { Button, Col, Form, Row } from 'react-bootstrap';
1011
import { formToJSON } from 'web-utility';
1112

1213
import activityStore from '../../models/Activity';
1314
import fileStore from '../../models/Base/File';
14-
import { t } from '../../models/Base/Translation';
15+
import { i18n, I18nContext } from '../../models/Base/Translation';
1516
import { DateTimeInput } from '../DateTimeInput';
1617

1718
const HTMLEditor = dynamic(() => import('../HTMLEditor'), { ssr: false });
@@ -25,7 +26,9 @@ export interface ActivityEditorProps {
2526
}
2627

2728
@observer
28-
export class ActivityEditor extends Component<ActivityEditorProps> {
29+
export class ActivityEditor extends ObservedComponent<ActivityEditorProps, typeof i18n> {
30+
static contextType = I18nContext;
31+
2932
@observable
3033
accessor detailHTML = '';
3134

@@ -54,7 +57,8 @@ export class ActivityEditor extends Component<ActivityEditorProps> {
5457

5558
if (!form.checkValidity()) return (this.validated = true);
5659

57-
const { name } = this.props,
60+
const { t } = this.observedContext,
61+
{ name } = this.props,
5862
data = formToJSON<ActivityFormData>(form);
5963

6064
data.detail = (data.detail || '') + '';
@@ -80,7 +84,8 @@ export class ActivityEditor extends Component<ActivityEditorProps> {
8084
};
8185

8286
render() {
83-
const {
87+
const { t } = this.observedContext,
88+
{
8489
name,
8590
displayName,
8691
tags = [],
@@ -137,12 +142,7 @@ export class ActivityEditor extends Component<ActivityEditorProps> {
137142
<span className="text-danger"> *</span>
138143
</Form.Label>
139144
<Col sm={10}>
140-
<Form.Control
141-
name="displayName"
142-
type="text"
143-
required
144-
defaultValue={displayName}
145-
/>
145+
<Form.Control name="displayName" type="text" required defaultValue={displayName} />
146146
<Form.Control.Feedback type="invalid">
147147
{textJoin(t('please_enter'), t('activity_name'))}
148148
</Form.Control.Feedback>
@@ -154,11 +154,7 @@ export class ActivityEditor extends Component<ActivityEditorProps> {
154154
{t('tag')}
155155
</Form.Label>
156156
<Col sm={10}>
157-
<BadgeInput
158-
name="tags"
159-
placeholder={t('tag_placeholder')}
160-
defaultValue={tags}
161-
/>
157+
<BadgeInput name="tags" placeholder={t('tag_placeholder')} defaultValue={tags} />
162158
</Col>
163159
</Form.Group>
164160

@@ -186,12 +182,7 @@ export class ActivityEditor extends Component<ActivityEditorProps> {
186182
<span className="text-danger"> *</span>
187183
</Form.Label>
188184
<Col sm={10}>
189-
<Form.Control
190-
name="location"
191-
type="text"
192-
required
193-
defaultValue={location}
194-
/>
185+
<Form.Control name="location" type="text" required defaultValue={location} />
195186
<Form.Control.Feedback type="invalid">
196187
{textJoin(t('please_enter'), t('activity_address'))}
197188
</Form.Control.Feedback>
@@ -254,12 +245,7 @@ export class ActivityEditor extends Component<ActivityEditorProps> {
254245
<span className="text-danger"> *</span>
255246
</Form.Label>
256247
<Col sm={10}>
257-
<Form.Control
258-
name="summary"
259-
type="text"
260-
defaultValue={summary}
261-
required
262-
/>
248+
<Form.Control name="summary" type="text" defaultValue={summary} required />
263249
<Form.Control.Feedback type="invalid">
264250
{textJoin(t('please_enter'), t('activity_introduction'))}
265251
</Form.Control.Feedback>
@@ -272,16 +258,8 @@ export class ActivityEditor extends Component<ActivityEditorProps> {
272258
<span className="text-danger"> *</span>
273259
</Form.Label>
274260
<Col sm={10}>
275-
<HTMLEditor
276-
defaultValue={detail}
277-
onChange={code => (this.detailHTML = code)}
278-
/>
279-
<Form.Control
280-
hidden
281-
name="detail"
282-
required
283-
value={this.detailHTML}
284-
/>
261+
<HTMLEditor defaultValue={detail} onChange={code => (this.detailHTML = code)} />
262+
<Form.Control hidden name="detail" required value={this.detailHTML} />
285263
<Form.Control.Feedback type="invalid">
286264
{textJoin(t('please_enter'), t('activity_detail'))}
287265
</Form.Control.Feedback>

components/Activity/ActivityEntry.tsx

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Hackathon } from '@kaiyuanshe/openhackathon-service';
2-
import { FC } from 'react';
2+
import { FC, useContext } from 'react';
33
import { Button } from 'react-bootstrap';
44
import { diffTime } from 'web-utility';
55

6-
import { t } from '../../models/Base/Translation';
6+
import { i18n, I18nContext } from '../../models/Base/Translation';
77

88
export type ActivityStatusMeta = Pick<
99
Hackathon,
@@ -16,15 +16,18 @@ export type ActivityStatusMeta = Pick<
1616
| 'judgeEndedAt'
1717
>;
1818

19-
export const getActivityStatusText = ({
20-
status,
21-
enrollmentStartedAt,
22-
enrollmentEndedAt,
23-
eventStartedAt,
24-
eventEndedAt,
25-
judgeStartedAt,
26-
judgeEndedAt,
27-
}: ActivityStatusMeta) => {
19+
export const getActivityStatusText = (
20+
{ t }: typeof i18n,
21+
{
22+
status,
23+
enrollmentStartedAt,
24+
enrollmentEndedAt,
25+
eventStartedAt,
26+
eventEndedAt,
27+
judgeStartedAt,
28+
judgeEndedAt,
29+
}: ActivityStatusMeta,
30+
) => {
2831
const now = Date.now(),
2932
isOnline = status === 'online',
3033
enrollmentStart = new Date(enrollmentStartedAt),
@@ -66,20 +69,16 @@ export const ActivityEntry: FC<ActivityEntryProps> = ({
6669
judgeEndedAt,
6770
href,
6871
}) => {
69-
const now = Date.now(),
72+
const i18n = useContext(I18nContext),
73+
now = Date.now(),
7074
isOnline = status === 'online',
7175
enrollmentStart = new Date(enrollmentStartedAt),
7276
enrollmentEnd = new Date(enrollmentEndedAt),
7377
enrolling = isOnline && +enrollmentStart < now && now < +enrollmentEnd;
7478

7579
return (
76-
<Button
77-
className="my-2 w-100"
78-
variant="primary"
79-
href={href}
80-
disabled={!enrolling}
81-
>
82-
{getActivityStatusText({
80+
<Button className="my-2 w-100" variant="primary" href={href} disabled={!enrolling}>
81+
{getActivityStatusText(i18n, {
8382
status,
8483
enrollmentStartedAt,
8584
enrollmentEndedAt,

components/Activity/ActivityList.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FC, PureComponent } from 'react';
44
import { Col, Row } from 'react-bootstrap';
55

66
import { ActivityListType, ActivityModel } from '../../models/Activity';
7-
import { i18n, t } from '../../models/Base/Translation';
7+
import { i18n } from '../../models/Base/Translation';
88
import platformAdmin from '../../models/User/PlatformAdmin';
99
import sessionStore from '../../models/User/Session';
1010
import { XScrollListProps } from '../layout/ScrollList';
@@ -36,9 +36,7 @@ export const ActivityListLayout: FC<ActivityListLayoutProps> = ({
3636
<ActivityCard
3737
className="h-100"
3838
controls={
39-
!!userId &&
40-
userId === sessionStore.user?.id &&
41-
(type === 'admin' || type === 'created')
39+
!!userId && userId === sessionStore.user?.id && (type === 'admin' || type === 'created')
4240
}
4341
{...item}
4442
{...props}
@@ -59,15 +57,15 @@ export default class ActivityList extends PureComponent<ActivityListProps> {
5957
}
6058

6159
onPublish = async (name: string) => {
62-
if (!confirm(t('sure_publish', { name }))) return;
60+
if (!confirm(i18n.t('sure_publish', { name }))) return;
6361

6462
await this.store.publishOne(name);
6563

6664
this.props.onPublish?.(name);
6765
};
6866

6967
onDelete = async (name: string) => {
70-
if (!confirm(t('sure_offline', { name }))) return;
68+
if (!confirm(i18n.t('sure_offline', { name }))) return;
7169

7270
await this.store.deleteOne(name);
7371
this.props.onDelete?.(name);

0 commit comments

Comments
 (0)