Skip to content

Commit 5f55409

Browse files
committed
Large Updates in description
DONATIONS: We added donations and any is appreciated. You get a 3D badge within the chatroom made by d9 (dark) THEMES: Added Themes (Dark, Black, Blue, Red, Purple) — change in settings (dark) HIGHLIGHTS & MENTIONS TAB: View all highlights in that tab. Add it the same place you add chatrooms (dark) CUSTOM NOTIFICATION SOUNDS: Upload custom highlight sounds from Settings (ftk - dark) HIGHLIGHT REPLIES: Replies to messages are now included in highlights (dark) MESSAGE BATCHING: New batching mode in settings for slower-paced message flow, like Twitch (ftk) 7TV EMOTE SET UPDATES: Set auto-sync now when changes are made. View changes (added/removed/renamed) in the chatroom (dark) NEW MESSAGE INDICATOR: Visual indicator for unseen messages in chatrooms (dark) ZERO-WIDTH 7TV EMOTES: Now supported (dark) HIGHLIGHT OPACITY: Control opacity of highlight pings in Settings (dark) EMOTE AUTOCOMPLETE FIX: Fixed emote order when using colon or tab (dark) CTRL + SCROLL ZOOM: Zoom in/out of chat using Ctrl + Mouse scroll (dark) PINNED MESSAGES: Show 'sent by' at the top now (dark) EMOTE DIALOG SCROLL FIX: Fixed scrolling issue when hovering emotes (dark) 7TV EMOTE DATE: Hover an emote to see the date it was added (dark) EMOTE RIGHT CLICK LINKS: View 7tv/1x/2x/4x links via right click (dark) PIN FROM USER DIALOG: Added pinning functionality in user dialog messages (dark) HIDE INFO BAR: Toggle to hide info bar (followers only mode, etc) in settings (dark) RIGHT CLICK SEARCH: Right click streamer name bar to search (dark) TIMESTAMP DROPDOWN: Timestamps setting now uses a single dropdown with a 'Disabled' option (dark)
1 parent efa1bbb commit 5f55409

96 files changed

Lines changed: 6945 additions & 1709 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.

electron.vite.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import react from "@vitejs/plugin-react";
44

55
export default defineConfig({
66
main: {
7-
plugins: [externalizeDepsPlugin({ exclude: ["electron-store", "electron-util", "electron-timber"] })],
7+
plugins: [externalizeDepsPlugin({ exclude: ["electron-store", "electron-util"] })],
88
},
99
preload: {
10-
plugins: [externalizeDepsPlugin({ exclude: ["electron-store", "electron-util", "electron-timber"] })],
10+
plugins: [externalizeDepsPlugin({ exclude: ["electron-store", "electron-util"] })],
1111
},
1212
renderer: {
1313
build: {

package.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "kick-talk",
3-
"version": "1.1.1",
3+
"version": "1.1.2",
44
"description": "KickTalk",
55
"contributors": [
66
{
@@ -17,7 +17,8 @@
1717
"main": "./out/main/index.js",
1818
"scripts": {
1919
"format": "prettier --write .",
20-
"lint": "eslint --cache .",
20+
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
21+
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
2122
"start": "cross-env NODE_ENV=production electron-vite preview",
2223
"dev": "cross-env NODE_ENV=development electron-vite dev",
2324
"dev-hr": "cross-env NODE_ENV=development electron-vite dev --watch",
@@ -29,6 +30,10 @@
2930
"build:linux": "npm run build && electron-builder --linux"
3031
},
3132
"repository": "https://github.com/KickTalkOrg/KickTalk",
33+
"publish": {
34+
"provider": "github",
35+
"releaseType": "release"
36+
},
3237
"product-name": "KickTalk",
3338
"win": {
3439
"target": {
@@ -50,13 +55,11 @@
5055
"@radix-ui/react-switch": "^1.2.4",
5156
"@radix-ui/react-tooltip": "^1.2.7",
5257
"axios": "^1.8.4",
53-
"cloudscraper": "^4.6.0",
5458
"clsx": "^2.1.1",
5559
"dayjs": "^1.11.13",
5660
"dotenv": "^16.4.7",
5761
"electron-log": "^5.4.0",
5862
"electron-store": "^10.0.1",
59-
"electron-timber": "^1.0.0",
6063
"electron-updater": "^6.6.2",
6164
"electron-util": "^0.18.1",
6265
"emoji-picker-react": "^4.12.2",
@@ -81,10 +84,6 @@
8184
"electron": "^34.2.0",
8285
"electron-builder": "^25.1.8",
8386
"electron-vite": "^3.0.0",
84-
"eslint": "^9.20.1",
85-
"eslint-plugin-react": "^7.37.4",
86-
"eslint-plugin-react-hooks": "^5.1.0",
87-
"eslint-plugin-react-refresh": "^0.4.19",
8887
"react": "^18.3.1",
8988
"react-dom": "^18.3.1",
9089
"sass-embedded": "^1.87.0",

resources/sounds/bells.wav

147 KB
Binary file not shown.

resources/sounds/intuition.wav

33.2 KB
Binary file not shown.

resources/sounds/job-done.wav

42.1 KB
Binary file not shown.
147 KB
Binary file not shown.

src/main/index.js

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { app, shell, BrowserWindow, ipcMain, screen, session, Tray, dialog } from "electron";
2-
import { join } from "path";
1+
const { app, shell, BrowserWindow, ipcMain, screen, session, Tray, dialog } = require("electron");
2+
import { join, basename } from "path";
33
import { electronApp, optimizer } from "@electron-toolkit/utils";
44
import { update } from "./utils/update";
55
import Store from "electron-store";
@@ -9,6 +9,7 @@ import dotenv from "dotenv";
99
dotenv.config();
1010

1111
const isDev = process.env.NODE_ENV === "development";
12+
const iconPath = join(__dirname, "../../resources/icons/win/KickTalk_v1.ico");
1213

1314
const authStore = new Store({
1415
fileExtension: "env",
@@ -97,6 +98,51 @@ const getNotificationSounds = () => {
9798
return availableNotificationSounds;
9899
};
99100

101+
const openNotificationFolder = async () => {
102+
const result = await dialog.showOpenDialog(settingsDialog || mainWindow, {
103+
title: "Select Notification Sound",
104+
filters: [
105+
{ name: "Audio Files", extensions: ["mp3", "wav"] },
106+
{ name: "All Files", extensions: ["*"] },
107+
],
108+
properties: ["openFile"],
109+
});
110+
111+
if (result.canceled || !result.filePaths.length) {
112+
return null;
113+
}
114+
115+
const selectedFile = result.filePaths[0];
116+
const fileName = basename(selectedFile);
117+
118+
const basePath = app.isPackaged
119+
? join(process.resourcesPath, "app.asar.unpacked/resources/sounds")
120+
: join(__dirname, "../../resources/sounds");
121+
122+
const destPath = join(basePath, fileName);
123+
124+
try {
125+
if (!fs.existsSync(basePath)) {
126+
fs.mkdirSync(basePath, { recursive: true });
127+
}
128+
129+
// Copy file to sounds directory
130+
fs.copyFileSync(selectedFile, destPath);
131+
getNotificationSounds();
132+
133+
console.log("[Notification Sounds]: File uploaded successfully:", fileName);
134+
135+
return {
136+
name: fileName.split(".")[0],
137+
value: destPath,
138+
fileName: fileName,
139+
};
140+
} catch (error) {
141+
console.error("[Notification Sounds]: Error uploading file:", error);
142+
return null;
143+
}
144+
};
145+
100146
getNotificationSounds(); // Load initially
101147

102148
const handleNotificationSound = (soundFile) => {
@@ -123,6 +169,10 @@ const getSoundUrl = (soundObject) => {
123169
}
124170
};
125171

172+
ipcMain.handle("notificationSounds:openFolder", async () => {
173+
return await openNotificationFolder();
174+
});
175+
126176
ipcMain.handle("notificationSounds:getAvailable", () => {
127177
getNotificationSounds();
128178
return availableNotificationSounds;
@@ -155,7 +205,10 @@ ipcMain.handle("store:get", async (e, { key }) => {
155205
ipcMain.handle("store:set", (e, { key, value }) => {
156206
const result = store.set(key, value);
157207

158-
mainWindow.webContents.send("store:updated", { [key]: value });
208+
// Broadcast to all windows
209+
BrowserWindow.getAllWindows().forEach((window) => {
210+
window.webContents.send("store:updated", { [key]: value });
211+
});
159212

160213
if (key === "general") {
161214
if (process.platform === "darwin") {
@@ -385,7 +438,7 @@ const createWindow = () => {
385438
autoHideMenuBar: true,
386439
titleBarStyle: "hidden",
387440
roundedCorners: true,
388-
icon: join(__dirname, "../../resources/icons/win/KickTalk_v1.ico"),
441+
icon: iconPath,
389442
webPreferences: {
390443
devTools: true,
391444
nodeIntegration: false,
@@ -466,8 +519,7 @@ const loginToKick = async (method) => {
466519
autoHideMenuBar: true,
467520
parent: authDialog,
468521
roundedCorners: true,
469-
470-
icon: join(__dirname, "../../resources/icons/win/KickTalk_v1.ico"),
522+
icon: iconPath,
471523
webPreferences: {
472524
autoplayPolicy: "user-gesture-required",
473525
nodeIntegration: false,
@@ -543,10 +595,30 @@ const loginToKick = async (method) => {
543595
};
544596

545597
const setupLocalShortcuts = () => {
598+
mainWindow.webContents.on("zoom-changed", (event, zoomDirection) => {
599+
if (zoomDirection === "in") {
600+
event.preventDefault();
601+
if (mainWindow.webContents.getZoomFactor() < 1.5) {
602+
const newZoomFactor = mainWindow.webContents.getZoomFactor() + 0.1;
603+
mainWindow.webContents.setZoomFactor(newZoomFactor);
604+
store.set("zoomFactor", newZoomFactor);
605+
}
606+
} else if (zoomDirection === "out") {
607+
event.preventDefault();
608+
if (mainWindow.webContents.getZoomFactor() > 0.8) {
609+
const newZoomFactor = mainWindow.webContents.getZoomFactor() - 0.1;
610+
mainWindow.webContents.setZoomFactor(newZoomFactor);
611+
store.set("zoomFactor", newZoomFactor);
612+
}
613+
}
614+
});
615+
546616
mainWindow.webContents.on("before-input-event", (event, input) => {
547617
if (!mainWindow.isFocused()) return;
548618

549619
if (input.control || input.meta) {
620+
// if mouse scroll up zoom in mouse only
621+
550622
if (input.key === "=" || input.key === "+") {
551623
event.preventDefault();
552624
if (mainWindow.webContents.getZoomFactor() < 1.5) {
@@ -652,7 +724,6 @@ ipcMain.handle("userDialog:open", (e, { data }) => {
652724
if (userDialog) {
653725
userDialog.setPosition(newX, newY);
654726
userDialog.webContents.send("userDialog:data", { ...data, pinned: false });
655-
656727
return;
657728
}
658729

@@ -666,6 +737,7 @@ ipcMain.handle("userDialog:open", (e, { data }) => {
666737
frame: false,
667738
transparent: true,
668739
parent: mainWindow,
740+
backgroundColor: "#020a05",
669741
webPreferences: {
670742
devtools: true,
671743
nodeIntegration: false,
@@ -684,7 +756,9 @@ ipcMain.handle("userDialog:open", (e, { data }) => {
684756

685757
userDialog.once("ready-to-show", () => {
686758
userDialog.show();
759+
687760
userDialog.setAlwaysOnTop(false);
761+
userDialog.setVisibleOnAllWorkspaces(false);
688762
userDialog.focus();
689763

690764
userDialog.webContents.send("userDialog:data", { ...data, pinned: false });
@@ -697,11 +771,11 @@ ipcMain.handle("userDialog:open", (e, { data }) => {
697771
userDialog.on("blur", () => {
698772
if (userDialog && !userDialog.isAlwaysOnTop()) {
699773
userDialog.close();
700-
mainWindow.setAlwaysOnTop(store.get("general.alwaysOnTop"));
701774
}
702775
});
703776

704777
userDialog.on("closed", () => {
778+
setAlwaysOnTop(mainWindow);
705779
dialogInfo = null;
706780
userDialog = null;
707781
});
@@ -712,10 +786,11 @@ ipcMain.handle("userDialog:pin", async (e, forcePinState) => {
712786
const newPinState = forcePinState !== undefined ? forcePinState : !userDialog.isAlwaysOnTop();
713787

714788
if (isDev && newPinState) {
715-
userDialog.webContents.openDevTools();
789+
// userDialog.webContents.openDevTools();
716790
}
717791

718-
await userDialog.setAlwaysOnTop(newPinState, "screen-saver");
792+
// Don't persist pin state - it should reset when dialog closes
793+
await userDialog.setAlwaysOnTop(newPinState);
719794
await userDialog.setVisibleOnAllWorkspaces(newPinState);
720795
}
721796
});
@@ -746,7 +821,7 @@ ipcMain.handle("authDialog:open", (e) => {
746821
transparent: true,
747822
roundedCorners: true,
748823
parent: mainWindow,
749-
icon: join(__dirname, "../../resources/icons/win/KickTalk_v1.ico"),
824+
icon: iconPath,
750825
webPreferences: {
751826
devtools: true,
752827
nodeIntegration: false,
@@ -821,14 +896,6 @@ ipcMain.on("close", () => {
821896
}
822897
});
823898

824-
// Window drag handler
825-
ipcMain.handle("window-drag", (e, { mouseX, mouseY }) => {
826-
const win = BrowserWindow.fromWebContents(e.sender);
827-
if (win) {
828-
win.setPosition(mouseX, mouseY);
829-
}
830-
});
831-
832899
// Get App Info
833900
ipcMain.handle("get-app-info", () => {
834901
return {
@@ -875,7 +942,7 @@ ipcMain.handle("chattersDialog:open", (e, { data }) => {
875942
transparent: true,
876943
roundedCorners: true,
877944
parent: mainWindow,
878-
icon: join(__dirname, "../../resources/icons/win/KickTalk_v1.ico"),
945+
icon: iconPath,
879946
webPreferences: {
880947
devtools: true,
881948
nodeIntegration: false,
@@ -943,7 +1010,7 @@ ipcMain.handle("searchDialog:open", (e, { data }) => {
9431010
transparent: true,
9441011
roundedCorners: true,
9451012
parent: mainWindow,
946-
icon: join(__dirname, "../../resources/icons/win/KickTalk_v1.ico"),
1013+
icon: iconPath,
9471014
webPreferences: {
9481015
devtools: true,
9491016
nodeIntegration: false,
@@ -1018,9 +1085,10 @@ ipcMain.handle("settingsDialog:open", async (e, { data }) => {
10181085
resizable: true,
10191086
frame: false,
10201087
transparent: true,
1088+
backgroundColor: "#020a05",
10211089
roundedCorners: true,
10221090
parent: mainWindow,
1023-
icon: join(__dirname, "../../resources/icons/win/KickTalk_v1.ico"),
1091+
icon: iconPath,
10241092
webPreferences: {
10251093
devtools: true,
10261094
nodeIntegration: false,

src/preload/index.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ import {
3232

3333
// Kick Auth for Events
3434
getKickAuthForEvents,
35+
getUpdateTitle,
36+
getClearChatroom,
3537
} from "../../utils/services/kick/kickAPI";
36-
import { getUserStvId, getChannelEmotes } from "../../utils/services/seventv/stvAPI";
38+
import { getUserStvProfile, getChannelEmotes } from "../../utils/services/seventv/stvAPI";
3739

3840
import Store from "electron-store";
3941

@@ -104,10 +106,13 @@ const validateSessionToken = async () => {
104106

105107
// Get STV ID with error handling
106108
try {
107-
const stvId = await getUserStvId(data.id);
108-
if (stvId) {
109-
localStorage.setItem("stvId", stvId);
110-
console.log("[Session Validation]: Updated stvId");
109+
const stvData = await getUserStvProfile(data.id);
110+
console.log("[Session Validation]: STV Data:", stvData);
111+
const personalEmoteSets = stvData?.emoteSets?.filter((set) => set.type === "personal");
112+
if (stvData) {
113+
localStorage.setItem("stvId", stvData.user_id);
114+
localStorage.setItem("stvPersonalEmoteSets", JSON.stringify(personalEmoteSets));
115+
console.log("[Session Validation]: Updated stvId and stvPersonalEmoteSets");
111116
}
112117
} catch (stvError) {
113118
console.warn("[Session Validation]: Failed to get STV ID:", stvError);
@@ -186,6 +191,7 @@ if (process.contextIsolated) {
186191
notificationSounds: {
187192
getAvailable: () => ipcRenderer.invoke("notificationSounds:getAvailable"),
188193
getSoundUrl: (soundFile) => ipcRenderer.invoke("notificationSounds:getSoundUrl", { soundFile }),
194+
openFolder: () => ipcRenderer.invoke("notificationSounds:openFolder"),
189195
},
190196

191197
authDialog: {
@@ -200,7 +206,9 @@ if (process.contextIsolated) {
200206
move: (x, y) => ipcRenderer.send("userDialog:move", { x, y }),
201207
pin: (pinState) => ipcRenderer.invoke("userDialog:pin", pinState),
202208
onData: (callback) => {
203-
const handler = (_, data) => callback(data);
209+
const handler = (_, data) => {
210+
callback(data);
211+
};
204212

205213
ipcRenderer.on("userDialog:data", handler);
206214
return () => ipcRenderer.removeListener("userDialog:data", handler);
@@ -346,6 +354,16 @@ if (process.contextIsolated) {
346354
getChatroomViewers: (chatroomId) => getChatroomViewers(chatroomId),
347355
},
348356

357+
// kickChannelActions: {
358+
// // Broadcaster Actions
359+
360+
// // Channel Commands
361+
// getUpdateTitle: (channelName, title) => withAuth((token, session) => getUpdateTitle(channelName, title, token, session)),
362+
// getClearChatroom: (channelName) => withAuth((token, session) => getClearChatroom(channelName, token, session)),
363+
// getUpdateSlowmode: (channelName, slowmodeOptions) =>
364+
// withAuth((token, session) => getUpdateSlowmode(channelName, slowmodeOptions, token, session)),
365+
// },
366+
349367
// 7TV API
350368
stv: {
351369
getChannelEmotes,

src/renderer/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!doctype html>
2-
<html id="main">
2+
<html id="main" data-theme="dark">
33

44
<head>
55
<meta charset="UTF-8" />
Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)