Skip to content
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

feat: X と Twitter へのリンクをOGPを取得して埋め込み風に表示するようにする #136

Merged
merged 4 commits into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc.js → .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ module.exports = {
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"prettier/@typescript-eslint",
"plugin:react-hooks/recommended"
"plugin:react-hooks/recommended",
"prettier",
],
globals: {
Atomics: "readonly",
Expand Down
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@mui/icons-material": "^5.8.4",
"@mui/material": "^5.9.3",
"@mui/styles": "^5.9.3",
"dom-parser": "^1.1.5",
"gh-pages": "^2.2.0",
"isomorphic-unfetch": "^3.0.0",
"next": "^12.2.4",
Expand All @@ -30,28 +31,28 @@
"@types/react-helmet": "^6.1.2",
"@types/react-redux": "^7.1.24",
"@types/styled-components": "^5.1.25",
"@typescript-eslint/eslint-plugin": "^2.30.0",
"@typescript-eslint/parser": "^2.30.0",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"babel-plugin-styled-components": "^1.13.2",
"chai": "^4.3.4",
"date-fns": "^2.22.1",
"dotenv": "^10.0.0",
"eslint": "^7.31.0",
"eslint-config-next": "^11.0.1",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint": "^8.56.0",
"eslint-config-next": "^14.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.2",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"express": "^4.17.3",
"googleapis": "39",
"husky": "^4.2.5",
"lint-staged": "^10.1.6",
"mocha": "^9.0.2",
"oauth2-provider": "^1.3.0",
"open": "^8.2.1",
"prettier": "~1.19.1",
"prettier": "^3.1.1",
"ts-node": "^10.1.0",
"typescript": "~4.3.2",
"typescript": "^5.3.3",
"yarn": "^1.22.4"
},
"license": "MIT",
Expand Down
22 changes: 18 additions & 4 deletions pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,43 @@ import audioList from "../static/audiolist.json";
import styled from "@emotion/styled";

import metainfo from "../static/metainfo.json";
import { fetchOGP, type OGPInfo } from "src/model/OGP/OGP";
import fetchYoutubeChannelOGP from "src/model/OGP/youtube";

type Props = {
isStatic: boolean;
voiceList: VoiceList;
ogpInfo: Record<string, OGPInfo>;
};

export default (props: Props) => {
export default ({ ogpInfo, voiceList }: Props) => {
return (
<>
<Header />
<Header x={ogpInfo["x"]} youtube={ogpInfo["youtube"]} />
<ButtonCount>
<div>button count: {metainfo.count}</div>
</ButtonCount>
<VoiceButtonsContainer voiceList={props.voiceList} />
<VoiceButtonsContainer voiceList={voiceList} />
<Footer />
</>
);
};

export async function getStaticProps(): Promise<{ props: Props }> {
const voiceList = AudioList2Store(audioList);
const ogps = await Promise.all([
fetchYoutubeChannelOGP("UCCzUftO8KOVkV4wQG1vkUvg"),
fetchOGP("https://twitter.com/houshoumarine"),
]);
return {
props: { isStatic: true, voiceList },
props: {
isStatic: true,
voiceList,
ogpInfo: {
youtube: ogps[0],
x: ogps[1],
},
},
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/Notice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function Notice(props: Props) {
<article className="notice-container">
<div className="font-weight-bold notice-title">お知らせ</div>
<div className="border rounded text-mid font-weight-bold">
{Children.map(props.children, child => (
{Children.map(props.children, (child) => (
<div className="notice-content monospace">{child}</div>
))}
</div>
Expand Down
85 changes: 85 additions & 0 deletions src/components/OGP.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { CSSProperties, ReactNode } from "react";
import { OGPInfo } from "src/model/OGP/OGP";
import styled from "styled-components";

type Props = {
href: string;
ogp: OGPInfo;
serviceIcon?: string;

colorTheme: {
color: string;
backgroundColor: string;
fontColor: string;
};
};

export default function OGP({ href, ogp, serviceIcon, colorTheme }: Props) {
return (
<Container
style={{
color: colorTheme.fontColor,
borderColor: colorTheme.color,
backgroundColor: colorTheme.backgroundColor,
}}
href={href}
target="_blank"
rel="noopener"
>
<Image src={ogp.image} />
<TitleContainer>
<Title>
{serviceIcon && <ServiceImage src={serviceIcon} />} {ogp.title}
</Title>
<Description>{ogp.description}</Description>
</TitleContainer>
</Container>
);
}

const Container = styled.a`
font-family: Arial, Helvetica, sans-serif;
font-size: 1rem;
width: 100%;
height: 5rem;
border-radius: 5px;
gap: 5px;
border: 3px solid #ccc;
text-decoration: none;
padding: 5px;
display: flex;
`;

const TitleContainer = styled.div`
display: flex;
flex-direction: column;
gap: 5px;

overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`;

const Title = styled.div`
font-size: 1.5rem;
font-weight: bold;
overflow: hidden;
`;

const Description = styled.div`
font-size: 0.8rem;
overflow: hidden;
line-clamp: 3;
text-overflow: ellipsis;
`;

const Image = styled.img`
height: 100%;
aspect-ratio: 1 / 1;
border-radius: 5px;
`;

const ServiceImage = styled.img`
margin-top: auto;
height: 1.5rem;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function AudioControllerContainer(props: Props) {
</div>
<DropButton
onClick={() => {
setIsExpand(state => !state);
setIsExpand((state) => !state);
}}
>
<ArrowDropUp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ export default function ButtonSectionContainer({
const groups = useMemo(
() =>
allGroups
.map(item =>
item.filter(itemm => filter == null || itemm.label.includes(filter))
.map((item) =>
item.filter((itemm) => filter == null || itemm.label.includes(filter))
)
.filter(item => item.length > 0),
.filter((item) => item.length > 0),
[allGroups, filter]
);

Expand All @@ -39,7 +39,7 @@ export default function ButtonSectionContainer({
<section className="border">
{groups.map((group, i) => (
<div key={`group-${i}`} className="btn-container">
{group.map(button => {
{group.map((button) => {
return (
<VoiceButton
key={button.path + button.label}
Expand Down
122 changes: 60 additions & 62 deletions src/container/HeaderContainer/HeaderContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,65 +1,64 @@
import { OGPInfo } from "src/model/OGP/OGP";
import ExternalLink from "../../components/ExternalLink";

import styled from "@emotion/styled";
import OGP from "src/components/OGP";

export default function HeaderContainer() {
type Props = {
x: OGPInfo;
youtube: OGPInfo;
};

export default function HeaderContainer({ x, youtube }: Props) {
return (
<Header className="app-header">
<div>
<HeaderArticle>
<ExternalLink
className="fork-me-on-github"
href="https://github.com/happou31/marine_button"
>
<img
width="120"
height="120"
src="https://github.blog/wp-content/uploads/2008/12/forkme_right_red_aa0000.png?resize=149%2C149"
className="attachment-full size-full"
alt="Fork me on GitHub"
data-recalc-dims="1"
/>
</ExternalLink>

<Logo
alt="宝鐘マリンボタン"
className="site-logo"
src="static/image/icon.png"
/>

<Nicomoji className="header-container-title">
宝鐘マリンボタン
</Nicomoji>
</HeaderArticle>
</div>
<div style={{ display: "flex", alignItems: "center" }}>
<HeaderArticle>
<ExternalLink
className="social-icon-youtube"
href="https://www.youtube.com/channel/UCCzUftO8KOVkV4wQG1vkUvg"
className="fork-me-on-github"
href="https://github.com/happou31/marine_button"
>
<YoutubeContainer>
<img
height="30px"
src="static/image/youtube_social_icon_red.png"
alt="Marine Ch. 宝鐘マリン"
/>
</YoutubeContainer>
<img
width="120"
height="120"
src="https://github.blog/wp-content/uploads/2008/12/forkme_right_red_aa0000.png?resize=149%2C149"
className="attachment-full size-full"
alt="Fork me on GitHub"
data-recalc-dims="1"
/>
</ExternalLink>

<ExternalLink href="https://twitter.com/houshoumarine">
<TwitterContainer>
<img
width="50px"
height="50px"
src="static/image/Twitter_Logo_Blue.svg"
alt="
宝鐘マリン🏴‍☠️@ホロライブ3期生"
/>
</TwitterContainer>
</ExternalLink>
</div>
<Logo
alt="宝鐘マリンボタン"
className="site-logo"
src="static/image/icon.png"
/>

<Nicomoji className="header-container-title">宝鐘マリンボタン</Nicomoji>
</HeaderArticle>
<OGPContainer>
<OGP
href="https://www.youtube.com/channel/UCCzUftO8KOVkV4wQG1vkUvg"
serviceIcon="static/image/youtube_social_icon_red.png"
ogp={youtube}
colorTheme={{
color: "#ff0000",
backgroundColor: "#000",
fontColor: "#fff",
}}
/>
<OGP
href="https://twitter.com/houshoumarine"
ogp={x}
serviceIcon="static/image/x-logo.svg"
colorTheme={{
color: "#000",
backgroundColor: "#fff",
fontColor: "#000",
}}
/>
</OGPContainer>
<HeaderArticle>
トゥイッターで共有
宝鐘マリンボタンを共有
<a
href="https://twitter.com/share?ref_src=twsrc%5Etfw"
className="twitter-share-button"
Expand All @@ -78,7 +77,6 @@ export default function HeaderContainer() {
const Header = styled.header`
font-family: "Nico Moji Plus", "Nico Moji", "Noto Sans JP";
color: black;
margin: 5px;
`;

const Nicomoji = styled.h3`
Expand All @@ -94,21 +92,21 @@ const HeaderArticle = styled.article`
display: flex;
height: 30px;
align-items: center;
margin-bottom: 10px;
padding: 10px;
`;

const Logo = styled.img`
object-fit: contain;
vertical-align: top;
`;

const TwitterContainer = styled.div`
object-fit: contain;
display: inline-block;
`;

const YoutubeContainer = styled.div`
object-fit: contain;
display: inline-block;
height: 32px;
const OGPContainer = styled.div`
width: 100%;
display: flex;
flex-direction: column;
gap: 15px;
padding: 10px 5px 7px;
@media screen and (max-width: 500px) {
font-size: 15px;
}
`;
Loading
Loading