diff --git a/src/components/audioSample.tsx b/src/components/audioSample.tsx index 608e04d1..bf797260 100644 --- a/src/components/audioSample.tsx +++ b/src/components/audioSample.tsx @@ -2,64 +2,66 @@ import React, { useMemo } from "react" import PlayButton from "./playButton" import StyleDropdown, { useStyleDropdownController } from "./styleDropdown" -const AudioSample = ({ - audioSamples, - characterName, - className, -}: { - audioSamples: { style: string; urls: readonly string[] }[] - characterName: string -} & React.HTMLAttributes) => { - const styles = useMemo( - () => audioSamples.map(value => value.style), - [audioSamples] - ) - const { selectedStyle, setSelectedStyle } = useStyleDropdownController({ - styles, - }) - const selectedAudioUrls = useMemo( - () => audioSamples.find(({ style }) => style == selectedStyle)!.urls, - [audioSamples, selectedStyle] - ) +const AudioSample = React.memo( + ({ + audioSamples, + characterName, + className, + }: { + audioSamples: { style: string; urls: readonly string[] }[] + characterName: string + } & React.HTMLAttributes) => { + const styles = useMemo( + () => audioSamples.map(value => value.style), + [audioSamples] + ) + const { selectedStyle, setSelectedStyle } = useStyleDropdownController({ + styles, + }) + const selectedAudioUrls = useMemo( + () => audioSamples.find(({ style }) => style == selectedStyle)!.urls, + [audioSamples, selectedStyle] + ) - return ( -
-
-
-
- 音声サンプル -
-
- {selectedAudioUrls.map((url, index) => ( - - ))} -
-
- {styles.length > 1 && ( + return ( +
+
- スタイル + 音声サンプル
- + {selectedAudioUrls.map((url, index) => ( + + ))}
- )} -
-
- ) -} + {styles.length > 1 && ( +
+
+ スタイル +
+
+ +
+
+ )} +
+
+ ) + } +) export default AudioSample diff --git a/src/hooks/useDetailedCharacterInfo.ts b/src/hooks/useDetailedCharacterInfo.ts index 46ca3318..db9fb31d 100644 --- a/src/hooks/useDetailedCharacterInfo.ts +++ b/src/hooks/useDetailedCharacterInfo.ts @@ -1,9 +1,6 @@ import { graphql, useStaticQuery } from "gatsby" -import { - CharacterInfo, - CharacterKey, - Generation, -} from "../types/dormitoryCharacter" +import { useMemo } from "react" +import { CharacterInfo, CharacterKey } from "../types/dormitoryCharacter" import { useCharacterInfo } from "./useCharacterInfo" export const useDetailedCharacterInfo = () => { @@ -251,7 +248,9 @@ export const useDetailedCharacterInfo = () => { return item } - const characterInfos: { + // キャラクターの詳細情報 + // ネストを浅くするために一旦変数に格納 + const _characterInfos: { [key in CharacterKey]: CharacterInfo } = { 四国めたん: { @@ -918,28 +917,16 @@ export const useDetailedCharacterInfo = () => { }, } as const - const generationInfos: { - [key in Generation]: { characterKeys: readonly CharacterKey[] } - } = { - 一期生: { characterKeys: ["四国めたん", "ずんだもん"] }, - 二期生: { - characterKeys: ["春日部つむぎ", "雨晴はう", "波音リツ"], - }, - 三期生: { - characterKeys: [ - "玄野武宏", - "白上虎太郎", - "青山龍星", - "冥鳴ひまり", - "九州そら", - ], - }, - } as const + const characterInfos = useMemo(() => _characterInfos, []) + const callNameInfos = useMemo(() => _callNameInfos, []) - return { characterInfos, callNameInfos, generationInfos } as const + return { + characterInfos, + callNameInfos, + } as const } -const callNameInfos: { +const _callNameInfos: { [key in CharacterKey]: { [key in CharacterKey]?: string | undefined } & { me: readonly string[]; you: readonly string[] } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 27bc8ebd..d906d0f4 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -15,36 +15,22 @@ import landingMovieThumb from "../images/landing-movie-thumb.png" import shareThumb from "../images/landing-share-thumb.jpg" import Logo from "../images/logo.svg" import landingMovie from "../movies/landing.mp4" -import { CharacterKey } from "../types/dormitoryCharacter" +import { CharacterInfo, CharacterKey } from "../types/dormitoryCharacter" import { getProductPageUrl } from "../urls" -const Main: React.FC<{ setShowingHeader: (show: boolean) => void }> = ({ - setShowingHeader, -}) => { - const { characterInfos } = useDetailedCharacterInfo() - - const { characterKeys } = useContext(CharacterContext) - - // ファーストビュー用のビューを超えたらヘッダーを表示する - const firstViewRef = useRef(null) - useEffect(() => { - if (!firstViewRef.current) return - const observer = new IntersectionObserver(entries => { - entries.forEach(entry => { - setShowingHeader(!entry.isIntersecting) - }) - }) - observer.observe(firstViewRef.current) - }, [firstViewRef]) - - const [ - showingLibraryReadmeModalCharacterKey, +// キャラクター表示 +const CharacterCard = React.memo( + ({ + characterInfo, + characterKey, setShowingLibraryReadmeModalCharacterKey, - ] = useState(undefined) - - // キャラクター表示 - const CharacterCard = ({ characterKey }: { characterKey: CharacterKey }) => { - const characterInfo = characterInfos[characterKey] + }: { + characterInfo: CharacterInfo + characterKey: CharacterKey + setShowingLibraryReadmeModalCharacterKey: ( + characterKey: CharacterKey + ) => void + }) => { if (!characterInfo) throw new Error(`characterInfo is undefined. (${characterKey})`) const LinkToProductPage = ({ @@ -109,230 +95,265 @@ const Main: React.FC<{ setShowingHeader: (show: boolean) => void }> = ({ ) } +) - return ( - <> - +const Main = React.memo( + ({ setShowingHeader }: { setShowingHeader: (show: boolean) => void }) => { + const { characterInfos } = useDetailedCharacterInfo() + + const { characterKeys } = useContext(CharacterContext) + + // ファーストビュー用のビューを超えたらヘッダーを表示する + const firstViewRef = useRef(null) + useEffect(() => { + if (!firstViewRef.current) return + const observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + setShowingHeader(!entry.isIntersecting) + }) + }) + observer.observe(firstViewRef.current) + }, [firstViewRef]) -
-
-
-
-
-
- + const [ + showingLibraryReadmeModalCharacterKey, + setShowingLibraryReadmeModalCharacterKey, + ] = useState(undefined) + + return ( + <> + + +
+
+
+
+
+
+ +
+

+ 無料で使える中品質なテキスト読み上げソフトウェア +

-

- 無料で使える中品質なテキスト読み上げソフトウェア -

-
-
-
-
-
- -
- -
-
-
- -
-
-
-

- - キャラクター一覧 - -

-
- {characterKeys.map((characterKey, index) => ( - - ))} + +
+
+
+ +
+
-
-
+ +
-
-
-

- - オープンソース - -

-

- VOICEVOX は OSS(オープンソース・ソフトウェア)版 VOICEVOX - をもとに構築されています。 -

-

- 製品版と OSS 版の違いやモジュール構成は  - - VOICEVOX の全体構成 - -  をご参照ください。 -

-

- ソフトウェア部分は Electron + Vue 、音声合成エンジン部分は - Python + FastAPI です。 -

-

- 追加したい・改善したい機能があれば、ぜひ開発にご参加ください。 -

-
- - - - - VOICEVOX エディター - - +
+
+

- - - - VOICEVOX エンジン - + + キャラクター一覧 + +

+
+ {characterKeys.map(characterKey => ( + + ))} +
-
-
+ -
-
-

- - コアライブラリ - -

-

- VOICEVOXの音声合成をアプリケーションやサービスに組み込める、VOICEVOXのコアライブラリを配布しています。 -

-

- 詳しくは  - - VOICEVOX CORE - -  をご参照ください。 -

-
-
- -
-
- -
+ +
+ +
+
+

+ + コアライブラリ - -
  • +
  • +

    + VOICEVOXの音声合成をアプリケーションやサービスに組み込める、VOICEVOXのコアライブラリを配布しています。 +

    +

    + 詳しくは  - pixivFANBOX + VOICEVOX CORE - - -

    -
    - - - setShowingLibraryReadmeModalCharacterKey(undefined)} - {...(showingLibraryReadmeModalCharacterKey != undefined - ? { - isActive: true, - characterKey: showingLibraryReadmeModalCharacterKey, - } - : { - isActive: false, - characterKey: undefined, - })} - /> - - ) -} +  をご参照ください。 +

    + + + +
    +
    + +
      +
    • + + 利用規約 + +
    • +
    • + + 使い方 + +
    • +
    • + + Q&A + +
    • +
    • + + ボイボ寮 + +
    • +
    • + + 変更履歴 + +
    • +
    • + + pixivFANBOX + +
    • +
    +
    +
    + + + setShowingLibraryReadmeModalCharacterKey(undefined)} + {...(showingLibraryReadmeModalCharacterKey != undefined + ? { + isActive: true, + characterKey: showingLibraryReadmeModalCharacterKey, + } + : { + isActive: false, + characterKey: undefined, + })} + /> + + ) + } +) -export default () => { +export default React.memo(() => { const [showingHeader, setShowingHeader] = useState(false) return (
    ) -} +})