Skip to content

Commit

Permalink
[fix] Share URL & file of Candidate Poster
Browse files Browse the repository at this point in the history
  • Loading branch information
TechQuery committed Feb 2, 2024
1 parent 84f9bd4 commit d5048c2
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 92 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
EOF
cat > .env.local <<EOF
${{ secrets.ENV_FILE }}
VERCEL_URL=kaiyuanshe.cn
EOF
- uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2
Expand Down
10 changes: 8 additions & 2 deletions components/Base/ShareBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import { createRef, MouseEvent, PropsWithChildren, PureComponent } from 'react';
import { Image } from 'react-bootstrap';
import { blobOf } from 'web-utility';

export async function elementToImage(
element: HTMLElement,
Expand Down Expand Up @@ -56,10 +57,15 @@ export class ShareBox extends PureComponent<ShareBoxProps> {
this.loading = false;
};

share = (event: MouseEvent<HTMLImageElement>) => {
share = async (event: MouseEvent<HTMLImageElement>) => {
event.stopPropagation();

return navigator.share?.(this.props);
const image = await blobOf(this.imageURI);

const file = new File([image], `${this.props.title}.png`, {
type: image.type,
});
await navigator.share?.({ files: [file], ...this.props });
};

render() {
Expand Down
2 changes: 2 additions & 0 deletions pages/activity/[id]/agenda/[agendaId]/invitation.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { text2color } from 'idea-react';
import { TableCellLocation, TableCellValue } from 'mobx-lark';
import { observer } from 'mobx-react';
import { compose, errorLogger, router } from 'next-ssr-middleware';
import { QRCodeSVG } from 'qrcode.react';
import { PureComponent } from 'react';
Expand Down Expand Up @@ -37,6 +38,7 @@ export const getServerSideProps = compose<
};
});

@observer
export default class InvitationPage extends PureComponent<InvitationPageProps> {
sharedURL = `${API_Host}/activity/${this.props.activity.id}/agenda/${this.props.agenda.id}`;

Expand Down
188 changes: 98 additions & 90 deletions pages/election/[year]/candidate/[recipient]/poster/[position].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
translator,
} from 'next-ssr-middleware';
import { QRCodeSVG } from 'qrcode.react';
import { FC } from 'react';
import { PureComponent } from 'react';
import { Col, Container, Row } from 'react-bootstrap';

import { LarkImage } from '../../../../../../components/Base/LarkImage';
Expand Down Expand Up @@ -52,22 +52,99 @@ export const VoteForm = {
'https://kaiyuanshe.feishu.cn/share/base/form/shrcnXIXPn0lOt4YomFsvhjnzjf',
};

const CandidatePoster: FC<CandidatePosterProps> = observer(
({
route: { params },
overview,
applicants,
recipient,
recipientAvatar,
position,
reason,
contribution,
proposition,
recommenders,
recommendation1,
recommendation2,
}) => {
const { year } = params!;
@observer
export default class CandidatePoster extends PureComponent<CandidatePosterProps> {
rootURL = `${API_Host}/election/${this.props.route.params!.year}`;
sharePath = `/candidate/${this.props.recipient}/poster/${this.props.position}`;

renderContent(title: string, subTitle: string) {
const {
overview,
applicants,
recipientAvatar,
position,
reason,
contribution,
proposition,
recommenders,
recommendation1,
recommendation2,
} = this.props;

return (
<Container
className="d-flex flex-column gap-3 py-5"
style={{
background: `url(/api/lark/file/SC8ibClWyoi5usxY8KtcnpCgntg) center no-repeat`,
backgroundSize: 'cover',
}}
>
<header className="d-flex gap-3 align-items-center justify-content-center">
<h1>
{title}
<br />
{subTitle}
</h1>

<LarkImage
roundedCircle
className="object-fit-contain"
style={{ width: '5rem', height: '5rem' }}
src={recipientAvatar}
/>
</header>
{[
{ title: t('nomination_reason'), content: reason as string },
{
title: t('previous_term_contribution'),
content: contribution as string,
},
{
title: t('this_term_proposition'),
content: proposition as string,
},
{
title: `${applicants} ${t('recommendation')}`,
content: recommendation1 as string,
},
{
title: `${recommenders} ${t('recommendation')}`,
content: recommendation2 as string,
},
].map(
({ title, content }) =>
content && (
<section key={title}>
<h2>{title}</h2>
<article
className="text-break"
dangerouslySetInnerHTML={{ __html: marked(content) }}
/>
</section>
),
)}
<Row as="footer" className="text-center">
<Col xs={6}>
<QRCodeSVG value={`${this.rootURL}#${position}`} />
<div className="my-3">
{textJoin(position as string, t('candidate'))}
</div>
</Col>
<Col xs={6}>
<QRCodeSVG
value={`${VoteForm[position as keyof typeof VoteForm] || VoteForm.common}?prefill_赞成=${overview}`}
/>
<div className="my-3">{t('vote_for_me')}</div>
</Col>
<Col xs={12}>{t('press_to_share')}</Col>
</Row>
</Container>
);
}

render() {
const { route, recipient, position } = this.props;
const { year } = route.params!;
const title = `${t('KaiYuanShe')} ${year}`,
subTitle = `${textJoin(position as string, t('candidate'))} ${recipient}`;

Expand All @@ -77,80 +154,11 @@ const CandidatePoster: FC<CandidatePosterProps> = observer(

<ShareBox
title={`${title} ${subTitle}`}
url={`${API_Host}/election/${year}/candidate/${recipient}/poster/${position}`}
url={this.rootURL + this.sharePath}
>
<Container
className="d-flex flex-column gap-3 p-5"
style={{
backgroundImage: `url(/api/lark/file/SC8ibClWyoi5usxY8KtcnpCgntg)`,
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
backgroundSize: 'cover',
}}
>
<header className="d-flex gap-3 align-items-center justify-content-center">
<h1>
{title}
<br />
{subTitle}
</h1>

<LarkImage
roundedCircle
className="object-fit-contain"
style={{ width: '5rem', height: '5rem' }}
src={recipientAvatar}
/>
</header>
{[
{ title: t('nomination_reason'), content: reason as string },
{
title: t('previous_term_contribution'),
content: contribution as string,
},
{
title: t('this_term_proposition'),
content: proposition as string,
},
{
title: `${applicants} ${t('recommendation')}`,
content: recommendation1 as string,
},
{
title: `${recommenders} ${t('recommendation')}`,
content: recommendation2 as string,
},
].map(
({ title, content }) =>
content && (
<section key={title}>
<h2>{title}</h2>
<article
dangerouslySetInnerHTML={{ __html: marked(content) }}
/>
</section>
),
)}
<Row as="footer" className="text-center">
<Col xs={6}>
<QRCodeSVG value={`${API_Host}/election/${year}#${position}`} />
<div className="my-3">
{textJoin(position as string, t('candidate'))}
</div>
</Col>
<Col xs={6}>
<QRCodeSVG
value={`${VoteForm[position as keyof typeof VoteForm] || VoteForm.common}?prefill_赞成=${overview}`}
/>
<div className="my-3">{t('vote_for_me')}</div>
</Col>
<Col xs={12}>{t('press_to_share')}</Col>
</Row>
</Container>
{this.renderContent(title, subTitle)}
</ShareBox>
</>
);
},
);

export default CandidatePoster;
}
}

1 comment on commit d5048c2

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for kaiyuanshe ready!

✅ Preview
https://kaiyuanshe-j7weijl8s-techquery.vercel.app

Built with commit d5048c2.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.