Skip to content

Commit 9ba4bfe

Browse files
aL0NEW0LFStormix
andauthored
fix/notification card display#13 (#14)
* fix(extension): firefox bug shifting stream page to the left * refactor(extension): code quality * fix(extension): firefox bug, endless waiting for request Fixed by adding logic for Manifest V2 which plasmo uses, as we were using client API only introduced in Manifest V3 * refactor(backend): added kick/twitch client id/secret placeholders for dev * chore(extension): bump version to 1.2.1 in package.json and constants.ts * refactor(extension): remove commented code * refactor(extension): remove commented code * fix(extension): notification display while watching on kick #13 * refactor(extension): remove testing code * chore(extension): bump version to 1.2.2 in package.json and constants.ts --------- Co-authored-by: Anas Mazouni <madadj4@gmail.com>
1 parent e83f6af commit 9ba4bfe

6 files changed

Lines changed: 90 additions & 82 deletions

File tree

apps/extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@lgamila/extension",
33
"displayName": "LGamila Live",
4-
"version": "1.2.1",
4+
"version": "1.2.2",
55
"description": "This extension shows you a real-time list of all Moroccan Twitch and Kick streamers who are currently live.",
66
"author": "Stormix <hello@stormix.dev>",
77
"scripts": {

apps/extension/src/components/molecules/notification-card.tsx

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import logo from 'data-base64:~assets/logo.svg';
2-
import { FiTwitch, FiX } from 'react-icons/fi';
3-
import { RiKickLine } from 'react-icons/ri';
4-
import { Avatar, AvatarImage } from '../ui/avatar';
5-
import { Button } from '../ui/button';
1+
import logo from "data-base64:~assets/logo.svg";
2+
import { FiTwitch, FiX } from "react-icons/fi";
3+
import { RiKickLine } from "react-icons/ri";
4+
import { Avatar, AvatarImage } from "../ui/avatar";
5+
import { Button } from "../ui/button";
66

77
interface NotificationCardProps {
88
name: string;
@@ -20,7 +20,7 @@ export function NotificationCard({
2020
onDismiss,
2121
}: NotificationCardProps) {
2222
return (
23-
<div className="bg-background py-2 px-4 rounded-lg items-center justify-center shadow-lg w-[250px] gap-2 mx-auto flex flex-col text-foreground animate-in slide-in-from-right-full fade-in duration-500 ease-out relative">
23+
<div className="bg-background py-2 px-4 rounded-lg items-center justify-center shadow-lg gap-2 mx-auto flex flex-col text-foreground animate-in slide-in-from-right-full fade-in duration-500 ease-out relative w-md min-w-md">
2424
<div className="flex flex-row gap-2 justify-start items-start w-full py-1">
2525
<img alt="logo" className="size-4" src={logo} />
2626
<p className="text-muted-foreground text-xs">LGamila Live</p>
@@ -35,19 +35,21 @@ export function NotificationCard({
3535
</button>
3636
)}
3737
</div>
38-
<div className="flex flex-row gap-4 items-center">
39-
<Avatar className="size-16">
40-
<AvatarImage src={avatar} />
41-
</Avatar>
42-
<div className="flex flex-col gap-0 flex-grow">
43-
<h3 className="font-medium">{name}</h3>
44-
<p className="text-muted-foreground">
45-
{name} is live on {platform}
46-
</p>
38+
<div className="flex flex-row gap-4 items-center justify-between w-full">
39+
<div className="flex flex-row gap-1 items-center justify-start">
40+
<Avatar className="size-16">
41+
<AvatarImage src={avatar} />
42+
</Avatar>
43+
<div className="flex flex-col gap-0 flex-grow">
44+
<h3 className="font-medium">{name}</h3>
45+
<p className="text-muted-foreground">
46+
{name} is live on {platform}
47+
</p>
48+
</div>
4749
</div>
4850
<Button
49-
icon={platform === 'twitch' ? <FiTwitch /> : <RiKickLine />}
50-
onClick={() => window.open(link, '_blank')}
51+
icon={platform === "twitch" ? <FiTwitch /> : <RiKickLine />}
52+
onClick={() => window.open(link, "_blank")}
5153
size="sm"
5254
>
5355
Watch Now

apps/extension/src/content.tsx

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/** biome-ignore-all lint/complexity/noExcessiveCognitiveComplexity: allow it */
2-
import styleText from 'data-text:./index.css';
3-
import { serverToClient } from '@lgamila/shared';
4-
import { Storage } from '@plasmohq/storage';
5-
import type { PlasmoCSConfig, PlasmoGetStyle } from 'plasmo';
6-
import { useCallback, useEffect, useState } from 'react';
7-
import { NotificationCard } from './components/molecules/notification-card';
8-
import { playNotificationSound } from './lib/audio';
9-
import { API_URL, NOTIFICATION_DURATION } from './lib/constants';
10-
import { logger } from './lib/logger';
11-
import type { Settings, Streamer } from './lib/types';
2+
import styleText from "data-text:./index.css";
3+
import { serverToClient } from "@lgamila/shared";
4+
import { Storage } from "@plasmohq/storage";
5+
import type { PlasmoCSConfig, PlasmoGetStyle } from "plasmo";
6+
import { useCallback, useEffect, useState } from "react";
7+
import { NotificationCard } from "./components/molecules/notification-card";
8+
import { playNotificationSound } from "./lib/audio";
9+
import { API_URL, NOTIFICATION_DURATION } from "./lib/constants";
10+
import { logger } from "./lib/logger";
11+
import type { Settings, Streamer } from "./lib/types";
1212

1313
interface StreamerData {
1414
name: string;
@@ -18,7 +18,7 @@ interface StreamerData {
1818
}
1919

2020
export const config: PlasmoCSConfig = {
21-
matches: ['https://*.twitch.tv/*', 'https://*.kick.com/*'],
21+
matches: ["https://*.twitch.tv/*", "https://*.kick.com/*"],
2222
all_frames: true,
2323
};
2424

@@ -27,7 +27,7 @@ const CHECK_INTERVAL = 10_000;
2727

2828
const getStreamer = async (
2929
name: string,
30-
platform: 'twitch' | 'kick'
30+
platform: "twitch" | "kick"
3131
): Promise<Streamer[]> => {
3232
const response = await fetch(
3333
`${API_URL}/api/streamers?search=${name}&platform=${platform}`
@@ -40,10 +40,10 @@ const getStreamer = async (
4040
};
4141

4242
const determinePlatform = (url: string) => {
43-
if (url.includes('twitch.tv')) {
44-
return 'twitch' as const;
43+
if (url.includes("twitch.tv")) {
44+
return "twitch" as const;
4545
}
46-
return 'kick' as const;
46+
return "kick" as const;
4747
};
4848

4949
const TWITCH_URL_REGEX =
@@ -110,7 +110,7 @@ const checkAndRedirect = async () => {
110110
currentStreamerData.livePlatforms?.[0] ??
111111
currentStreamerData.livePlatform;
112112
const username =
113-
platform === 'twitch'
113+
platform === "twitch"
114114
? currentStreamerData.twitchUsername
115115
: currentStreamerData.kickUsername;
116116
window.location.href = `https://www.${platform}.com/${username}`;
@@ -122,7 +122,7 @@ let checkInterval: number | null = null;
122122

123123
const startChecking = () => {
124124
if (checkInterval) return;
125-
logger.info('Starting auto-redirect checks');
125+
logger.info("Starting auto-redirect checks");
126126
checkAndRedirect(); // Initial check
127127
checkInterval = setInterval(
128128
checkAndRedirect,
@@ -132,7 +132,7 @@ const startChecking = () => {
132132

133133
const stopChecking = () => {
134134
if (!checkInterval) return;
135-
logger.info('Stopping auto-redirect checks');
135+
logger.info("Stopping auto-redirect checks");
136136
clearInterval(checkInterval);
137137
checkInterval = null;
138138
};
@@ -152,16 +152,16 @@ storage.watch({
152152
});
153153

154154
// Initial setup based on current settings
155-
storage.get<Settings>('settings').then((settings) => {
155+
storage.get<Settings>("settings").then((settings) => {
156156
if (settings?.autoRedirect) {
157157
startChecking();
158158
}
159159
});
160160

161161
// Notification Card
162162
export const getStyle: PlasmoGetStyle = () => {
163-
const style = document.createElement('style');
164-
style.textContent = styleText.replaceAll(':root', ':host(plasmo-csui)');
163+
const style = document.createElement("style");
164+
style.textContent = styleText.replaceAll(":root", ":host(plasmo-csui)");
165165
return style;
166166
};
167167

@@ -186,29 +186,29 @@ export default function Root() {
186186
chrome.runtime.onMessage.addListener(async (message) => {
187187
const parsed = serverToClient.safeParse(message);
188188
if (!parsed.success) {
189-
logger.warn('Invalid message', parsed.error);
189+
logger.warn("Invalid message", parsed.error);
190190
return;
191191
}
192-
if (parsed.data.type === 'streamer-live') {
192+
if (parsed.data.type === "streamer-live") {
193193
// Check settings before showing notification
194-
const settings = await storage.get<Settings>('settings');
194+
const settings = await storage.get<Settings>("settings");
195195
const favorites =
196-
(await storage.get<Streamer['id'][]>('favorites')) || [];
196+
(await storage.get<Streamer["id"][]>("favorites")) || [];
197197
const blockedStreamers =
198-
(await storage.get<Streamer['id'][]>('blocked')) || [];
198+
(await storage.get<Streamer["id"][]>("blocked")) || [];
199199

200200
// Skip if notifications are disabled
201201
if (settings?.notifyWhenStreamerIsLive === false) {
202202
logger.info(
203-
'Notifications disabled, skipping content script notification'
203+
"Notifications disabled, skipping content script notification"
204204
);
205205
return;
206206
}
207207

208208
// Skip if streamer is blocked
209209
if (blockedStreamers.includes(parsed.data.data.id)) {
210210
logger.info(
211-
'Streamer is blocked, skipping content script notification'
211+
"Streamer is blocked, skipping content script notification"
212212
);
213213
return;
214214
}
@@ -219,7 +219,7 @@ export default function Root() {
219219
!favorites.includes(parsed.data.data.id)
220220
) {
221221
logger.info(
222-
'Only favorite notifications enabled and streamer is not a favorite, skipping content script notification'
222+
"Only favorite notifications enabled and streamer is not a favorite, skipping content script notification"
223223
);
224224
return;
225225
}

apps/extension/src/index.css

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@
4444
--radius: 0.5rem;
4545
--shadow-2xs: 1px 2px 5px 1px hsl(0 0% 0% / 0.03);
4646
--shadow-xs: 1px 2px 5px 1px hsl(0 0% 0% / 0.03);
47-
--shadow-sm:
48-
1px 2px 5px 1px hsl(0 0% 0% / 0.06), 1px 1px 2px 0px hsl(0 0% 0% / 0.06);
49-
--shadow:
50-
1px 2px 5px 1px hsl(0 0% 0% / 0.06), 1px 1px 2px 0px hsl(0 0% 0% / 0.06);
51-
--shadow-md:
52-
1px 2px 5px 1px hsl(0 0% 0% / 0.06), 1px 2px 4px 0px hsl(0 0% 0% / 0.06);
53-
--shadow-lg:
54-
1px 2px 5px 1px hsl(0 0% 0% / 0.06), 1px 4px 6px 0px hsl(0 0% 0% / 0.06);
55-
--shadow-xl:
56-
1px 2px 5px 1px hsl(0 0% 0% / 0.06), 1px 8px 10px 0px hsl(0 0% 0% / 0.06);
47+
--shadow-sm: 1px 2px 5px 1px hsl(0 0% 0% / 0.06),
48+
1px 1px 2px 0px hsl(0 0% 0% / 0.06);
49+
--shadow: 1px 2px 5px 1px hsl(0 0% 0% / 0.06),
50+
1px 1px 2px 0px hsl(0 0% 0% / 0.06);
51+
--shadow-md: 1px 2px 5px 1px hsl(0 0% 0% / 0.06),
52+
1px 2px 4px 0px hsl(0 0% 0% / 0.06);
53+
--shadow-lg: 1px 2px 5px 1px hsl(0 0% 0% / 0.06),
54+
1px 4px 6px 0px hsl(0 0% 0% / 0.06);
55+
--shadow-xl: 1px 2px 5px 1px hsl(0 0% 0% / 0.06),
56+
1px 8px 10px 0px hsl(0 0% 0% / 0.06);
5757
--shadow-2xl: 1px 2px 5px 1px hsl(0 0% 0% / 0.15);
5858

5959
--sb-track-color: #202020;
@@ -100,3 +100,9 @@
100100
right: 25px;
101101
left: auto;
102102
}
103+
104+
#plasmo-overlay-0,
105+
.plasmo-csui-container {
106+
right: 0px !important;
107+
left: auto !important;
108+
}

apps/extension/src/lib/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export const APP_NAME = "LGamila Live";
2-
export const APP_VERSION = "1.2.1";
2+
export const APP_VERSION = "1.2.2";
33
export const APP_DESCRIPTION =
44
"This extension shows you a real-time list of all Moroccan Twitch and Kick streamers who are currently live.";
55
export const APP_AUTHOR = "Stormix <hello@s.dev>";

apps/extension/src/popup.tsx

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
import './index.css';
2-
import logo from 'data-base64:~assets/logo.svg';
3-
import { ArrowUpDown, Loader2, Search, X } from 'lucide-react';
4-
import { useRef, useState } from 'react';
5-
import { QueryClient, QueryClientProvider } from 'react-query';
6-
import { Toaster } from '@/components/ui/sonner';
7-
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
8-
import { Footer } from './components/molecules/footer';
9-
import { SettingsDialog } from './components/molecules/settings';
10-
import { SortDropdown } from './components/molecules/sort-dropdown';
11-
import type { SortOption } from './components/molecules/streamer-list';
12-
import { StreamerList } from './components/molecules/streamer-list';
13-
import { Button } from './components/ui/button';
14-
import { Card, CardContent } from './components/ui/card';
15-
import { Input } from './components/ui/input';
16-
import { useSearch } from './hooks/use-search';
17-
import { useStreamers } from './hooks/use-streamers';
18-
import { APP_NAME } from './lib/constants';
19-
import { cn } from './lib/utils';
1+
import "./index.css";
2+
import logo from "data-base64:~assets/logo.svg";
3+
import { ArrowUpDown, Loader2, Search, X } from "lucide-react";
4+
import { useRef, useState } from "react";
5+
import { QueryClient, QueryClientProvider } from "react-query";
6+
import { Toaster } from "@/components/ui/sonner";
7+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
8+
import { Footer } from "./components/molecules/footer";
9+
import { SettingsDialog } from "./components/molecules/settings";
10+
import { SortDropdown } from "./components/molecules/sort-dropdown";
11+
import type { SortOption } from "./components/molecules/streamer-list";
12+
import { StreamerList } from "./components/molecules/streamer-list";
13+
import { Button } from "./components/ui/button";
14+
import { Card, CardContent } from "./components/ui/card";
15+
import { Input } from "./components/ui/input";
16+
import { useSearch } from "./hooks/use-search";
17+
import { useStreamers } from "./hooks/use-streamers";
18+
import { APP_NAME } from "./lib/constants";
19+
import { cn } from "./lib/utils";
2020

2121
function StreamersPopup() {
2222
const [toggleSearch, setToggleSearch] = useState(false);
2323
const [showSort, setShowSort] = useState(false);
24-
const [sortBy, setSortBy] = useState<SortOption>('default');
24+
const [sortBy, setSortBy] = useState<SortOption>("default");
2525
const inputRef = useRef<HTMLInputElement>(null);
2626

2727
const { data: streamersData, isLoading } = useStreamers();
2828
const streamers = streamersData?.streamers || [];
2929
const { search, results } = useSearch(streamers, {
30-
keys: ['name', 'twitchUsername', 'kickUsername', 'category', 'title'],
30+
keys: ["name", "twitchUsername", "kickUsername", "category", "title"],
3131
threshold: 0.3,
3232
});
3333

@@ -44,7 +44,7 @@ function StreamersPopup() {
4444
<Tabs className="w-full" defaultValue="live">
4545
<div className="flex flex-row justify-between items-start">
4646
<div
47-
className={cn('flex flex-row justify-start', {
47+
className={cn("flex flex-row justify-start", {
4848
hidden: toggleSearch || showSort,
4949
})}
5050
>
@@ -56,8 +56,8 @@ function StreamersPopup() {
5656
</div>
5757
<div
5858
className={cn(
59-
'flex flex-row justify-center gap-2',
60-
(toggleSearch || showSort) && 'w-full'
59+
"flex flex-row justify-center gap-2",
60+
(toggleSearch || showSort) && "w-full"
6161
)}
6262
>
6363
<Button

0 commit comments

Comments
 (0)