Skip to content

Commit bd40dee

Browse files
Copilotsarisia
andauthored
Enable local development preview with mock data (#4)
* Initial plan * Add local development preview with mock data Co-authored-by: sarisia <33576079+sarisia@users.noreply.github.com> * Address code review feedback: simplify cleanup and Firebase initialization Co-authored-by: sarisia <33576079+sarisia@users.noreply.github.com> * Update mock data with real VSPO streamer thumbnails and info Co-authored-by: sarisia <33576079+sarisia@users.noreply.github.com> * Replace external images with bundled SVG placeholders Co-authored-by: sarisia <33576079+sarisia@users.noreply.github.com> * Fix platform field typo in twitCasting channel configs Co-authored-by: sarisia <33576079+sarisia@users.noreply.github.com> * Simplify mock images to use single streamer icon and stream thumbnail Co-authored-by: sarisia <33576079+sarisia@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sarisia <33576079+sarisia@users.noreply.github.com>
1 parent e767070 commit bd40dee

9 files changed

Lines changed: 236 additions & 14 deletions

File tree

.env.development

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Local development configuration
2+
# Use mock data instead of Firebase for local development
3+
VITE_USE_MOCK_DATA=true
4+
5+
# These are not needed when using mock data, but kept for reference
6+
# VITE_STREAM_COLLECTION_NAME=streams
7+
# VITE_STREAMER_COLLECTION_NAME=streamers

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,25 @@
1313
* **配信中**または**配信予定**の配信のみが本サイトで一覧できます。
1414
* 10分に1回配信情報を更新しています。
1515

16+
## ローカル開発 / Local Development
17+
18+
プロジェクトのフロントエンドをローカルで開発する場合、Firebase設定なしでモックデータを使用できます。
19+
20+
To develop the frontend locally without Firebase configuration:
21+
22+
```bash
23+
# 依存関係のインストール / Install dependencies
24+
npm install
25+
26+
# 開発サーバーの起動 / Start development server
27+
npm run dev
28+
```
29+
30+
開発モードでは自動的にモックデータが使用されます(`.env.development`で設定)。
31+
In development mode, mock data is automatically used (configured in `.env.development`).
32+
33+
本番環境では、Firebase設定が必要です。
34+
For production, Firebase configuration is required.
35+
1636
## バグ・要望
1737
* [GitHub issues](https://github.com/mnsinri/vspo-stream-schedule/issues)
Lines changed: 10 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading

src/firebase.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
import { initializeApp } from "firebase/app";
2-
import { getFirestore } from "firebase/firestore";
3-
4-
const firebaseConfig = {
5-
apiKey: import.meta.env.VITE_API_KEY,
6-
authDomain: import.meta.env.VITE_AUTH_DOMAIN,
7-
projectId: import.meta.env.VITE_PROJECT_ID,
8-
storageBucket: import.meta.env.VITE_STORAGE_BUCKET,
9-
messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID,
10-
databaseURL: import.meta.env.VITE_DATABASE_URL,
11-
appId: import.meta.env.VITE_APP_ID,
12-
};
13-
14-
const app = initializeApp(firebaseConfig);
15-
export const firestore = getFirestore(app);
2+
import { getFirestore, Firestore } from "firebase/firestore";
3+
4+
const useMockData = import.meta.env.VITE_USE_MOCK_DATA === "true";
5+
6+
let firestore: Firestore;
7+
8+
if (useMockData) {
9+
// In mock mode, firestore is not used as VspoStreamProvider returns early
10+
// We export a placeholder to satisfy the import, but it will never be called
11+
console.log("Firebase: Using mock data mode - Firebase initialization skipped");
12+
firestore = {} as Firestore;
13+
} else {
14+
const firebaseConfig = {
15+
apiKey: import.meta.env.VITE_API_KEY,
16+
authDomain: import.meta.env.VITE_AUTH_DOMAIN,
17+
projectId: import.meta.env.VITE_PROJECT_ID,
18+
storageBucket: import.meta.env.VITE_STORAGE_BUCKET,
19+
messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID,
20+
databaseURL: import.meta.env.VITE_DATABASE_URL,
21+
appId: import.meta.env.VITE_APP_ID,
22+
};
23+
24+
const app = initializeApp(firebaseConfig);
25+
firestore = getFirestore(app);
26+
}
27+
28+
export { firestore };

src/mocks/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { mockStreamers } from "./streamers";
2+
export { mockStreams } from "./streams";

src/mocks/streamers.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { StreamerResponse } from "@types";
2+
3+
export const mockStreamers: Record<string, StreamerResponse> = {
4+
streamer1: {
5+
youtube: {
6+
id: "UCvUc0m317LWTTPZoBQV479A",
7+
name: "一ノ瀬うるは",
8+
icon: "/mock-images/streamer-icon.svg",
9+
platform: "youtube",
10+
},
11+
twitch: {
12+
id: "uruhaichinose",
13+
name: "一ノ瀬うるは",
14+
icon: "/mock-images/streamer-icon.svg",
15+
platform: "twitch",
16+
},
17+
twitCasting: {
18+
id: "uruhaichinose",
19+
name: "一ノ瀬うるは",
20+
icon: "/mock-images/streamer-icon.svg",
21+
platform: "twitCasting",
22+
},
23+
order: 1,
24+
},
25+
streamer2: {
26+
youtube: {
27+
id: "UC61OwuYOVuKkpKnid-43Twg",
28+
name: "紫宮るな",
29+
icon: "/mock-images/streamer-icon.svg",
30+
platform: "youtube",
31+
},
32+
twitch: {
33+
id: "shinomiyaruna",
34+
name: "紫宮るな",
35+
icon: "/mock-images/streamer-icon.svg",
36+
platform: "twitch",
37+
},
38+
twitCasting: {
39+
id: "shinomiyaruna",
40+
name: "紫宮るな",
41+
icon: "/mock-images/streamer-icon.svg",
42+
platform: "twitCasting",
43+
},
44+
order: 2,
45+
},
46+
streamer3: {
47+
youtube: {
48+
id: "UCD5W21JqNMv_tV9nfjvF9sw",
49+
name: "英リサ",
50+
icon: "/mock-images/streamer-icon.svg",
51+
platform: "youtube",
52+
},
53+
twitch: {
54+
id: "hanabusarisa",
55+
name: "英リサ",
56+
icon: "/mock-images/streamer-icon.svg",
57+
platform: "twitch",
58+
},
59+
twitCasting: {
60+
id: "hanabusarisa",
61+
name: "英リサ",
62+
icon: "/mock-images/streamer-icon.svg",
63+
platform: "twitCasting",
64+
},
65+
order: 3,
66+
},
67+
};

src/mocks/streams.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { StreamResponse } from "@types";
2+
3+
const now = new Date();
4+
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
5+
const twoHoursLater = new Date(now.getTime() + 2 * 60 * 60 * 1000);
6+
const fourHoursLater = new Date(now.getTime() + 4 * 60 * 60 * 1000);
7+
const sixHoursLater = new Date(now.getTime() + 6 * 60 * 60 * 1000);
8+
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
9+
const ttl = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); // 30 days from now
10+
11+
export const mockStreams: StreamResponse[] = [
12+
{
13+
id: "stream1",
14+
streamerId: "streamer1",
15+
channelId: "UCvUc0m317LWTTPZoBQV479A",
16+
platform: "youtube",
17+
title: "【LIVE】Morning Gaming Stream - Apex Legends Ranked",
18+
thumbnail: "/mock-images/stream-thumbnail.svg",
19+
url: "https://www.youtube.com/live/YkySMsypko8",
20+
scheduledStartTime: oneHourAgo.toISOString(),
21+
startTime: oneHourAgo.toISOString(),
22+
ttl,
23+
},
24+
{
25+
id: "stream2",
26+
streamerId: "streamer2",
27+
channelId: "shinomiyaruna",
28+
platform: "twitch",
29+
title: "【Twitch】Chatting and Game Testing Stream",
30+
thumbnail: "/mock-images/stream-thumbnail.svg",
31+
url: "https://www.twitch.tv/shinomiyaruna",
32+
scheduledStartTime: twoHoursLater.toISOString(),
33+
ttl,
34+
},
35+
{
36+
id: "stream3",
37+
streamerId: "streamer1",
38+
channelId: "UCvUc0m317LWTTPZoBQV479A",
39+
platform: "youtube",
40+
title: "【Minecraft】Building a new house! Come hang out!",
41+
thumbnail: "/mock-images/stream-thumbnail.svg",
42+
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
43+
scheduledStartTime: fourHoursLater.toISOString(),
44+
ttl,
45+
},
46+
{
47+
id: "stream4",
48+
streamerId: "streamer3",
49+
channelId: "hanabusarisa",
50+
platform: "twitCasting",
51+
title: "Late Night Chat Stream 💤",
52+
thumbnail: "/mock-images/stream-thumbnail.svg",
53+
url: "https://twitcasting.tv/hanabusarisa",
54+
scheduledStartTime: sixHoursLater.toISOString(),
55+
ttl,
56+
},
57+
{
58+
id: "stream5",
59+
streamerId: "streamer2",
60+
channelId: "UC61OwuYOVuKkpKnid-43Twg",
61+
platform: "youtube",
62+
title: "【歌枠】Singing Stream - Taking Requests!",
63+
thumbnail: "/mock-images/stream-thumbnail.svg",
64+
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
65+
scheduledStartTime: tomorrow.toISOString(),
66+
ttl,
67+
},
68+
{
69+
id: "stream6",
70+
streamerId: "streamer3",
71+
channelId: "UCD5W21JqNMv_tV9nfjvF9sw",
72+
platform: "youtube",
73+
title: "【Valorant】Competitive Ranked Grind",
74+
thumbnail: "/mock-images/stream-thumbnail.svg",
75+
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
76+
scheduledStartTime: tomorrow.toISOString(),
77+
ttl,
78+
},
79+
];

src/providers/vspoStream/provider.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { collection, onSnapshot } from "firebase/firestore";
1212
import { firestore } from "@/firebase";
1313
import { vspoStreamContext, vspoStreamerContext } from "./context";
1414
import { useSettings } from "../setting";
15+
import { mockStreamers, mockStreams } from "@/mocks";
1516

1617
const parseToStream = (streamRes: StreamResponse, channel: Channel): Stream => {
1718
const endAt = streamRes.endTime ? new Date(streamRes.endTime) : undefined;
@@ -50,6 +51,24 @@ export const VspoStreamProvider = ({ children }: { children: ReactNode }) => {
5051
const { filteredStreamerIds } = useSettings();
5152

5253
useEffect(() => {
54+
const useMockData = import.meta.env.VITE_USE_MOCK_DATA === "true";
55+
56+
if (useMockData) {
57+
// Use mock data for local development
58+
console.log("Using mock data for local development");
59+
setStreamsResponse(mockStreams);
60+
61+
const map = Object.fromEntries(
62+
Object.entries(mockStreamers).map(([id, data]) => [
63+
id,
64+
parseToStreamer(id, data),
65+
])
66+
);
67+
setStreamerMap(map);
68+
return;
69+
}
70+
71+
// Use Firebase for production
5372
const streamCollectionName = import.meta.env.VITE_STREAM_COLLECTION_NAME;
5473
const streamerCollectionName = import.meta.env
5574
.VITE_STREAMER_COLLECTION_NAME;

0 commit comments

Comments
 (0)