Skip to content

Commit 4c40b3a

Browse files
authored
chore: v3 (#76)
* chore: v3 * delete: gomi * build通らん * git mv firebase.ts * feat: change build output dir * safari つらい * build通らん * safari つらい * safari turai * feat: github link * ぼだ忘れ
1 parent 79ca385 commit 4c40b3a

133 files changed

Lines changed: 9604 additions & 18901 deletions

File tree

Some content is hidden

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

.eslintrc.js

Lines changed: 0 additions & 34 deletions
This file was deleted.

.github/workflows/firebase-hosting-merge.yml

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@ name: Deploy to Firebase Hosting on merge
88
- main
99

1010
env:
11-
REACT_APP_API_KEY: ${{secrets.REACT_APP_API_KEY}}
12-
REACT_APP_AUTH_DOMAIN: ${{secrets.REACT_APP_AUTH_DOMAIN}}
13-
REACT_APP_DATABASE_URL: ${{secrets.REACT_APP_DATABASE_URL}}
14-
REACT_APP_PROJECT_ID: ${{secrets.REACT_APP_PROJECT_ID}}
15-
REACT_APP_STORAGE_BUCKET: ${{secrets.REACT_APP_STORAGE_BUCKET}}
16-
REACT_APP_MESSAGING_SENDER_ID: ${{secrets.REACT_APP_MESSAGING_SENDER_ID}}
17-
REACT_APP_APP_ID: ${{secrets.REACT_APP_APP_ID}}
18-
REACT_APP_DB_DATA_PATH: ${{secrets.REACT_APP_DB_DATA_PATH}}
19-
REACT_APP_STREAM_COLLECTION_NAME: ${{secrets.REACT_APP_STREAM_COLLECTION_NAME}}
20-
REACT_APP_STREAMER_COLLECTION_NAME: ${{secrets.REACT_APP_STREAMER_COLLECTION_NAME}}
11+
VITE_API_KEY: ${{secrets.VITE_API_KEY}}
12+
VITE_AUTH_DOMAIN: ${{secrets.VITE_AUTH_DOMAIN}}
13+
VITE_DATABASE_URL: ${{secrets.VITE_DATABASE_URL}}
14+
VITE_PROJECT_ID: ${{secrets.VITE_PROJECT_ID}}
15+
VITE_STORAGE_BUCKET: ${{secrets.VITE_STORAGE_BUCKET}}
16+
VITE_MESSAGING_SENDER_ID: ${{secrets.VITE_MESSAGING_SENDER_ID}}
17+
VITE_APP_ID: ${{secrets.VITE_APP_ID}}
18+
VITE_STREAM_COLLECTION_NAME: ${{secrets.VITE_STREAM_COLLECTION_NAME}}
19+
VITE_STREAMER_COLLECTION_NAME: ${{secrets.VITE_STREAMER_COLLECTION_NAME}}
2120
CI: false
2221

2322
jobs:

.github/workflows/firebase-hosting-pull-request.yml

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@ name: Deploy to Firebase Hosting on PR
55
"on": pull_request
66

77
env:
8-
REACT_APP_API_KEY: ${{secrets.REACT_APP_API_KEY}}
9-
REACT_APP_AUTH_DOMAIN: ${{secrets.REACT_APP_AUTH_DOMAIN}}
10-
REACT_APP_DATABASE_URL: ${{secrets.REACT_APP_DATABASE_URL}}
11-
REACT_APP_PROJECT_ID: ${{secrets.REACT_APP_PROJECT_ID}}
12-
REACT_APP_STORAGE_BUCKET: ${{secrets.REACT_APP_STORAGE_BUCKET}}
13-
REACT_APP_MESSAGING_SENDER_ID: ${{secrets.REACT_APP_MESSAGING_SENDER_ID}}
14-
REACT_APP_APP_ID: ${{secrets.REACT_APP_APP_ID}}
15-
REACT_APP_DB_DATA_PATH: ${{secrets.REACT_APP_DB_DATA_PATH}}
16-
REACT_APP_STREAM_COLLECTION_NAME: ${{secrets.REACT_APP_STREAM_COLLECTION_NAME}}
17-
REACT_APP_STREAMER_COLLECTION_NAME: ${{secrets.REACT_APP_STREAMER_COLLECTION_NAME}}
8+
VITE_API_KEY: ${{secrets.VITE_API_KEY}}
9+
VITE_AUTH_DOMAIN: ${{secrets.VITE_AUTH_DOMAIN}}
10+
VITE_DATABASE_URL: ${{secrets.VITE_DATABASE_URL}}
11+
VITE_PROJECT_ID: ${{secrets.VITE_PROJECT_ID}}
12+
VITE_STORAGE_BUCKET: ${{secrets.VITE_STORAGE_BUCKET}}
13+
VITE_MESSAGING_SENDER_ID: ${{secrets.VITE_MESSAGING_SENDER_ID}}
14+
VITE_APP_ID: ${{secrets.VITE_APP_ID}}
15+
VITE_STREAM_COLLECTION_NAME: ${{secrets.VITE_STREAM_COLLECTION_NAME}}
16+
VITE_STREAMER_COLLECTION_NAME: ${{secrets.VITE_STREAMER_COLLECTION_NAME}}
1817
CI: false
1918

2019
jobs:

.vscode/extensions.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"recommendations": [
3+
"dbaeumer.vscode-eslint",
4+
"kamikillerto.vscode-colorize",
5+
"bradlc.vscode-tailwindcss"
6+
]
7+
}

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"editor.formatOnSave": false,
2+
"editor.formatOnSave": true,
33
"editor.codeActionsOnSave": {
44
"source.fixAll": "explicit"
55
},
@@ -9,6 +9,7 @@
99
"Firestore",
1010
"HHMM",
1111
"hoverable",
12+
"VITE",
1213
"Vspo",
1314
"vssdb"
1415
]

components.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "new-york",
4+
"rsc": false,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "",
8+
"css": "src/index.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib",
18+
"hooks": "@/hooks"
19+
},
20+
"iconLibrary": "lucide"
21+
}

eslint.config.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import js from '@eslint/js'
2+
import globals from 'globals'
3+
import reactHooks from 'eslint-plugin-react-hooks'
4+
import reactRefresh from 'eslint-plugin-react-refresh'
5+
import tseslint from 'typescript-eslint'
6+
7+
export default tseslint.config(
8+
{ ignores: ['dist'] },
9+
{
10+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
11+
files: ['**/*.{ts,tsx}'],
12+
languageOptions: {
13+
ecmaVersion: 2020,
14+
globals: globals.browser,
15+
},
16+
plugins: {
17+
'react-hooks': reactHooks,
18+
'react-refresh': reactRefresh,
19+
},
20+
rules: {
21+
...reactHooks.configs.recommended.rules,
22+
'react-refresh/only-export-components': [
23+
'warn',
24+
{ allowConstantExport: true },
25+
],
26+
},
27+
},
28+
)

functions/src/index.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { initializeApp } from "firebase-admin/app";
22
import { getFirestore } from "firebase-admin/firestore";
33
import { onSchedule } from "firebase-functions/v2/scheduler";
44
import { onRequest } from "firebase-functions/v2/https";
5-
import { Streamer } from "../types";
5+
import { Streamer, Stream } from "../types";
66
import { YoutubeClient, TwitchClient, TwitCastingClient } from "./api";
77
import { defineConfig, sortStreams } from "./utils";
88
import { createMaster } from "./createMater";
@@ -73,7 +73,7 @@ export const getStreamers = onSchedule(
7373

7474
return result;
7575
},
76-
baseStreamers as Record<string, Streamer>,
76+
baseStreamers as Record<string, Streamer>
7777
);
7878

7979
// create and update db
@@ -84,7 +84,7 @@ export const getStreamers = onSchedule(
8484
batch.set(streamerRef.doc(key), data);
8585

8686
await batch.commit();
87-
},
87+
}
8888
);
8989

9090
export const getStreams = onSchedule(
@@ -98,8 +98,6 @@ export const getStreams = onSchedule(
9898
region: "asia-northeast1",
9999
},
100100
async () => {
101-
const endTime = new Date().toISOString();
102-
103101
// init
104102
const master = await getStreamerMaster();
105103
const tokenDoc = db
@@ -115,15 +113,20 @@ export const getStreams = onSchedule(
115113
twitchClient.getStreams([...master.twitch.keys()]),
116114
twitClient.getStreams([...master.twitCasting.keys()]),
117115
];
118-
const streams = (await Promise.all(getStreams)).flat();
116+
const currentStreams = (await Promise.all(getStreams)).flat();
119117

120118
// create and update db
121119
const batch = db.batch();
122120
const streamRef = db.collection(config.collection.streams.value());
123121
const snap = await streamRef.get();
124-
const { endedStreams, newStreams } = sortStreams(streams, snap.docs);
122+
const pastStreams = snap.docs.map((doc) => ({
123+
id: doc.id,
124+
data: doc.data() as Stream,
125+
}));
126+
const streams = sortStreams(currentStreams, pastStreams);
125127

126-
for await (const { id, data } of endedStreams) {
128+
const endTime = new Date().toISOString();
129+
for await (const { id, data } of streams.ended) {
127130
let stream = data;
128131

129132
// if twitch stream, update stream info
@@ -139,13 +142,16 @@ export const getStreams = onSchedule(
139142
batch.update(streamRef.doc(id), { ...stream, endTime });
140143
}
141144

142-
for (const newStream of newStreams) {
145+
for (const { id, data } of streams.updated)
146+
batch.update(streamRef.doc(id), data);
147+
148+
for (const newStream of streams.new) {
143149
const streamerId = master[newStream.platform].get(newStream.channelId);
144150
batch.set(streamRef.doc(), { ...newStream, streamerId });
145151
}
146152

147153
await batch.commit();
148-
},
154+
}
149155
);
150156

151157
export const createStreamerMaster = onRequest(
@@ -155,5 +161,5 @@ export const createStreamerMaster = onRequest(
155161
async (_, res) => {
156162
createMaster(db, config.collection.master.value());
157163
res.status(200).send("updated");
158-
},
164+
}
159165
);

functions/src/utils/sortStreams.ts

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { QueryDocumentSnapshot, DocumentData } from "firebase-admin/firestore";
22
import { BaseStream, Stream } from "../../types";
33

4-
type Returns<T extends BaseStream> = {
4+
type Returns2<T extends BaseStream> = {
55
endedStreams: { id: string; data: Stream }[]; // {document id, document data}[]
66
newStreams: T[];
77
};
88

99
// TODO: まとも
10-
export const sortStreams = <T extends BaseStream>(
10+
export const sortStreams2 = <T extends BaseStream>(
1111
streams: T[],
12-
snaps: QueryDocumentSnapshot<DocumentData>[],
13-
): Returns<T> => {
12+
snaps: QueryDocumentSnapshot<DocumentData>[]
13+
): Returns2<T> => {
1414
const existingStreams = snaps.map((doc) => ({
1515
id: doc.id,
1616
data: doc.data() as Stream,
@@ -19,16 +19,65 @@ export const sortStreams = <T extends BaseStream>(
1919
const endedStreams = existingStreams.filter(
2020
({ data }) =>
2121
!streams.find(
22-
({ id, platform }) => data.id === id && data.platform === platform,
23-
) && !data.endTime,
22+
({ id, platform }) => data.id === id && data.platform === platform
23+
) && !data.endTime
2424
);
2525

2626
const newStreams = streams.filter(
2727
({ id, platform }) =>
2828
!existingStreams.find(
29-
({ data }) => data.id === id && data.platform === platform,
30-
),
29+
({ data }) => data.id === id && data.platform === platform
30+
)
3131
);
3232

3333
return { endedStreams, newStreams };
3434
};
35+
36+
type Returns<T extends BaseStream> = {
37+
new: T[];
38+
updated: { id: string; data: T }[]; // {document id, document data}[]
39+
ended: { id: string; data: T }[]; // {document id, document data}[]
40+
};
41+
42+
const diff = <T extends BaseStream>(a: T, b: T) => {
43+
return (
44+
a.title !== b.title ||
45+
a.scheduledStartTime !== b.scheduledStartTime ||
46+
a.thumbnail !== b.thumbnail
47+
);
48+
};
49+
50+
export const sortStreams = <T extends BaseStream>(
51+
currentStreams: T[],
52+
pastStreams: { id: string; data: T }[]
53+
): Returns<T> => {
54+
const currentStreamMap = new Map(currentStreams.map((s) => [s.id, s]));
55+
56+
const { updated, ended } = pastStreams.reduce(
57+
(result: Pick<Returns<T>, "ended" | "updated">, pastStream) => {
58+
const currentStream = currentStreamMap.get(pastStream.data.id);
59+
60+
if (!currentStream) {
61+
result.ended.push(pastStream);
62+
return result;
63+
}
64+
65+
currentStreamMap.delete(currentStream.id);
66+
67+
if (diff(pastStream.data, currentStream))
68+
result.updated.push({ ...pastStream, data: currentStream });
69+
70+
return result;
71+
},
72+
{
73+
updated: [],
74+
ended: [],
75+
}
76+
);
77+
78+
return {
79+
new: [...currentStreamMap.values()],
80+
updated,
81+
ended,
82+
};
83+
};

index.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<html lang="ja">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<meta
7+
name="description"
8+
content="ぶいすぽっ!所属Vtuberの配信スケジュールが一覧できるウェブサイトです。"
9+
/>
10+
<meta property="og:title" content="Vspo stream schedule" />
11+
<meta
12+
property="og:description"
13+
content="ぶいすぽっ!所属Vtuberの配信スケジュールが一覧できるウェブサイトです。"
14+
/>
15+
<meta property="og:url" content="https://vspo-stream-schedule.web.app/" />
16+
<meta property="og:image" content="android-chrome-512x512.png" />
17+
<meta property="og:type" content="website" />
18+
<meta property="og:site_name" content="Vspo stream schedule" />
19+
<meta name="twitter:card" content="summary_large_image" />
20+
21+
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png" />
22+
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png" />
23+
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png" />
24+
<link rel="manifest" href="site.webmanifest" />
25+
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#5bbad5" />
26+
<meta name="msapplication-TileColor" content="#ffffff" />
27+
<meta name="theme-color" content="#ffffff" />
28+
<title>Vspo stream schedule</title>
29+
</head>
30+
<body>
31+
<div id="root"></div>
32+
<script type="module" src="/src/main.tsx"></script>
33+
</body>
34+
</html>

0 commit comments

Comments
 (0)