feat: notifications open the message #10

This commit is contained in:
SethBurkart123
2025-02-23 21:54:59 +11:00
parent a51049154b
commit c446217916
4 changed files with 252 additions and 164 deletions
+8 -21
View File
@@ -13,11 +13,12 @@ import { StorageChangeHandler } from '@/seqta/utils/listeners/StorageChanges'
import { eventManager } from '@/seqta/utils/listeners/EventManager' import { eventManager } from '@/seqta/utils/listeners/EventManager'
// UI and theme management // UI and theme management
import loading, { AppendLoadingSymbol } from '@/seqta/ui/Loading' import RegisterClickListeners from './seqta/utils/listeners/ClickListeners'
import { enableCurrentTheme } from '@/seqta/ui/themes/enableCurrent'
import { updateAllColors } from '@/seqta/ui/colors/Manager'
import { SettingsResizer } from '@/seqta/ui/SettingsResizer'
import { AddBetterSEQTAElements } from '@/seqta/ui/AddBetterSEQTAElements' import { AddBetterSEQTAElements } from '@/seqta/ui/AddBetterSEQTAElements'
import { enableCurrentTheme } from '@/seqta/ui/themes/enableCurrent'
import loading, { AppendLoadingSymbol } from '@/seqta/ui/Loading'
import { SettingsResizer } from '@/seqta/ui/SettingsResizer'
import { updateAllColors } from '@/seqta/ui/colors/Manager'
// JSON content // JSON content
import MenuitemSVGKey from '@/seqta/content/MenuItemSVGKey.json' import MenuitemSVGKey from '@/seqta/content/MenuItemSVGKey.json'
@@ -38,7 +39,6 @@ import documentLoadCSS from '@/css/documentload.scss?inline'
import renderSvelte from '@/interface/main' import renderSvelte from '@/interface/main'
import Settings from '@/interface/pages/settings.svelte' import Settings from '@/interface/pages/settings.svelte'
import { settingsPopup } from './interface/hooks/SettingsPopup' import { settingsPopup } from './interface/hooks/SettingsPopup'
import ReactFiber from './seqta/utils/ReactFiber'
let SettingsClicked = false let SettingsClicked = false
export let MenuOptionsOpen = false export let MenuOptionsOpen = false
@@ -74,6 +74,7 @@ async function init() {
await initializeSettingsState(); await initializeSettingsState();
if (settingsState.onoff) { if (settingsState.onoff) {
browser.runtime.sendMessage({ type: 'injectMainScript' })
enableCurrentTheme() enableCurrentTheme()
if (typeof settingsState.assessmentsAverage == 'undefined') { if (typeof settingsState.assessmentsAverage == 'undefined') {
@@ -91,22 +92,6 @@ async function init() {
} }
console.info('[BetterSEQTA+] Successfully initalised BetterSEQTA+, starting to load assets.') console.info('[BetterSEQTA+] Successfully initalised BetterSEQTA+, starting to load assets.')
main() main()
/* waitForElm('.Viewer__Viewer___32BH-', true).then(async () => {
console.log('Element exists')
await browser.runtime.sendMessage({ type: 'injectMainScript' })
const nice = ReactFiber.find(".Viewer__Viewer___32BH-", { debug: true })
console.log(nice.getState())
nice.setState({ selected: new Set([999431]) })
//console.log(nice)
}) */
} catch (error: any) { } catch (error: any) {
console.error(error) console.error(error)
} }
@@ -804,6 +789,8 @@ async function LoadPageElements(): Promise<void> {
}, handleAssessments); }, handleAssessments);
} }
RegisterClickListeners();
await handleSublink(sublink); await handleSublink(sublink);
} }
+150 -104
View File
@@ -1,120 +1,163 @@
// pageState.ts
class ReactFiber { class ReactFiber {
constructor(selector, options = {}) { constructor(selector, options = {}) {
this.selector = selector; this.selector = selector;
this.debug = options.debug || false; this.debug = options.debug || false;
this.nodes = [...document.querySelectorAll(selector)]; // Support multiple elements this.nodes = [...document.querySelectorAll(selector)]; // Support multiple elements
this.fibers = this.nodes.map(node => this.getFiberNode(node)); this.fibers = this.nodes.map(node => this.getFiberNode(node));
this.components = this.fibers.map(fiber => this.getOwnerComponent(fiber)); this.components = this.fibers.map(fiber => this.getOwnerComponent(fiber));
if (this.debug) { if (this.debug) {
console.log("📌 Selected Nodes:", this.nodes); console.log("Selected Nodes:", this.nodes);
console.log("🔍 Found Fibers:", this.fibers); console.log("🔍 Found Fibers:", this.fibers);
console.log("🛠 Found Components:", this.components); console.log("🛠 Found Components:", this.components);
}
} }
}
static find(selector, options = {}) { static find(selector, options = {}) {
return new ReactFiber(selector, options); return new ReactFiber(selector, options);
} }
getFiberNode(node) { getFiberNode(node) {
if (!node) return null; if (!node) return null;
const fiberKey = Object.getOwnPropertyNames(node).find(name => const fiberKey = Object.getOwnPropertyNames(node).find(name =>
name.startsWith('__reactFiber') || name.startsWith('__reactInternalInstance') name.startsWith('__reactFiber') || name.startsWith('__reactInternalInstance')
); );
return fiberKey ? node[fiberKey] : null; return fiberKey ? node[fiberKey] : null;
} }
getOwnerComponent(fiberNode) { getOwnerComponent(fiberNode) {
let current = fiberNode; let current = fiberNode;
while (current) { while (current) {
if (current.stateNode && (current.stateNode.setState || current.stateNode.forceUpdate)) { if (current.stateNode && (current.stateNode.setState || current.stateNode.forceUpdate)) {
return current.stateNode; return current.stateNode;
}
current = current.return;
}
return null;
}
getState(key) {
if (!this.components.length) return null;
const state = this.components[0]?.state || null;
if (key === undefined) {
return state; // Return entire state
} else if (typeof key === 'string') {
return state?.[key]; // Return single key
} else if (Array.isArray(key)) {
// Return object with only specified keys
const filteredState = {};
for (const k of key) {
if (state && Object.hasOwn(state, k)) { // Use Object.hasOwn for safety
filteredState[k] = state[k];
}
}
return filteredState;
} }
return null; // Invalid key type current = current.return;
} }
return null;
}
setState(update) { getState(key) {
this.components.forEach(component => { if (!this.components.length) return null;
if (component?.setState) { const state = this.components[0]?.state || null;
if (typeof update === 'function') {
// Functional update if (key === undefined) {
component.setState(prevState => { return state;
const newState = update(prevState); } else if (typeof key === 'string') {
if (this.debug) console.log("✅ Updated State (Functional):", newState); return state?.[key];
return newState; } else if (Array.isArray(key)) {
}); const filteredState = {};
} else { for (const k of key) {
// Object update (merge with existing state) if (state && Object.hasOwn(state, k)) {
component.setState(prevState => { filteredState[k] = state[k];
const newState = { ...prevState, ...update }; // Merge here!
if (this.debug) console.log("✅ Updated State (Object Merge):", newState);
return newState;
});
}
} }
}); }
return this; return filteredState;
} }
return null;
}
getProp(propName) { setState(update) {
if (!this.fibers.length) return null; this.components.forEach(component => {
return this.fibers[0]?.memoizedProps?.[propName]; if (component?.setState) {
} if (typeof update === 'function') {
// Functional update
setProp(propName) { component.setState(prevState => {
this.fibers.forEach(fiber => { const newState = update(prevState);
if (fiber?.memoizedProps) { if (this.debug) console.log("✅ Updated State (Functional):", newState);
fiber.memoizedProps[propName] = value; return newState;
});
} else {
// Object update (merge with existing state)
component.setState(prevState => {
const newState = {
...prevState,
...update
};
if (this.debug) console.log("✅ Updated State (Object Merge):", newState);
return newState;
});
} }
}); }
return this; // Enable chaining });
return this;
}
getProp(propName) {
if (!this.fibers.length) return null;
if (propName === undefined) {
return this.fibers[0]?.memoizedProps;
} }
forceUpdate() { return this.fibers[0]?.memoizedProps?.[propName];
this.components.forEach(component => { }
if (component?.forceUpdate) {
component.forceUpdate(); setProp(propName) {
if (this.debug) console.log("🔄 Forced React Re-render"); this.fibers.forEach(fiber => {
} if (fiber?.memoizedProps) {
}); fiber.memoizedProps[propName] = value;
return this; // Enable chaining }
} });
return this; // Enable chaining
}
forceUpdate() {
this.components.forEach(component => {
if (component?.forceUpdate) {
component.forceUpdate();
if (this.debug) console.log("🔄 Forced React Re-render");
}
});
return this; // Enable chaining
}
} }
console.log("Window cat: ", window.cat); function makeSerializable(obj) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(item => makeSerializable(item));
}
const serializableObj = {};
for (const key in obj) {
if (Object.hasOwn(obj, key)) {
let value = obj[key];
if (typeof value === 'function') {
value = '[Function]';
} else if (value instanceof HTMLElement) {
value = {
type: 'HTMLElement',
id: value.id,
tagName: value.tagName
}; // Replace DOM node with ID/tag info
} else if (typeof value === 'symbol') {
value = value.toString();
} else if (typeof value === 'object' && value !== null) {
value = makeSerializable(value);
}
serializableObj[key] = value;
}
}
return serializableObj;
}
// Listen for messages from the background script (via window.postMessage)
window.addEventListener('message', (event) => { window.addEventListener('message', (event) => {
console.log(event)
if (event.data.type === "reactFiberRequest") { if (event.data.type === "reactFiberRequest") {
const { selector, action, payload, debug, messageId } = event.data; const {
const fiberInstance = ReactFiber.find(selector, { debug }); selector,
action,
payload,
debug,
messageId
} = event.data;
const fiberInstance = ReactFiber.find(selector, {
debug
});
let response; let response;
switch (action) { switch (action) {
@@ -124,12 +167,12 @@ window.addEventListener('message', (event) => {
case "setState": case "setState":
// Handle both function and object updates // Handle both function and object updates
if (payload.updateFn) { if (payload.updateFn) {
const updateFn = eval(`(${payload.updateFn})`); const updateFn = eval(`(${payload.updateFn})`);
fiberInstance.setState(updateFn); fiberInstance.setState(updateFn);
} else { } else {
fiberInstance.setState(payload.updateObject); fiberInstance.setState(payload.updateObject);
} }
response = {}; // Acknowledge response = {};
break; break;
case "getProp": case "getProp":
@@ -137,18 +180,21 @@ window.addEventListener('message', (event) => {
break; break;
case "setProp": case "setProp":
fiberInstance.setProp(payload.propName, payload.value); fiberInstance.setProp(payload.propName, payload.value);
response = {}; // Acknowledge response = {};
break; break;
case "forceUpdate": case "forceUpdate":
fiberInstance.forceUpdate(); fiberInstance.forceUpdate();
response = {}; // Acknowledge response = {};
break; break;
default: default:
console.warn(`[pageState] Unknown action: ${action}`); console.warn(`[pageState] Unknown action: ${action}`);
response = null; response = null;
} }
// Send the response back to the background script using window.postMessage if (response !== null && typeof response === 'object') {
response = makeSerializable(response);
}
window.postMessage({ window.postMessage({
type: "reactFiberResponse", type: "reactFiberResponse",
response, response,
+48 -39
View File
@@ -1,72 +1,81 @@
import browser from 'webextension-polyfill';
class ReactFiber { class ReactFiber {
private selector: string; private selector: string;
private debug: boolean; private debug: boolean;
private messageIdCounter: number = 0; // Counter for unique message IDs private messageIdCounter: number = 0; // Counter for unique message IDs
constructor(selector: string, options: { debug?: boolean } = {}) { constructor(selector: string, options: {
debug ? : boolean
} = {}) {
this.selector = selector; this.selector = selector;
this.debug = options.debug || false; this.debug = options.debug || false;
} }
static find(selector: string, options: { debug?: boolean } = {}) { static find(selector: string, options: {
debug ? : boolean
} = {}) {
return new ReactFiber(selector, options); return new ReactFiber(selector, options);
} }
private async sendMessage(action: string, payload: any = {}): Promise<any> { private async sendMessage(action: string, payload: any = {}): Promise < any > {
return new Promise((resolve, _) => { return new Promise((resolve, _) => {
const messageId = this.messageIdCounter++; const messageId = this.messageIdCounter++;
const message = { const message = {
type: "reactFiberRequest", type: "reactFiberRequest",
selector: this.selector, selector: this.selector,
action, action,
payload, payload,
debug: this.debug, debug: this.debug,
messageId, // Include the unique message ID messageId,
}; };
const listener = (response: any) => { const listener = (response: any) => {
if (response.data?.type === 'reactFiberResponse' && response.data?.messageId === messageId) { if (response.data?.type === 'reactFiberResponse' && response.data?.messageId === messageId) {
if (this.debug) { if (this.debug) {
console.log("Content Received Response:", response.data.response); console.log("Content Received Response:", response.data.response);
} }
resolve(response.data.response); resolve(response.data.response);
window.removeEventListener("message", listener) window.removeEventListener("message", listener)
} }
}; };
window.addEventListener('message', listener); window.addEventListener('message', listener);
window.postMessage(message, "*"); window.postMessage(message, "*");
}); });
} }
async getState(key?: string | string[]): Promise<any> { // Type change: allow string or string[] async getState(key ? : string | string[]): Promise < any > {
return this.sendMessage("getState", { key }); return this.sendMessage("getState", {
key
});
} }
async setState(update: any | ((prevState: any) => any)): Promise<ReactFiber> { async setState(update: any | ((prevState: any) => any)): Promise < ReactFiber > {
// Now async again.
const updateFnString = typeof update === 'function' ? update.toString() : null; const updateFnString = typeof update === 'function' ? update.toString() : null;
const updateObject = typeof update !== 'function' ? update : null; const updateObject = typeof update !== 'function' ? update : null;
await this.sendMessage("setState", { updateFn: updateFnString, updateObject }); await this.sendMessage("setState", {
updateFn: updateFnString,
updateObject
});
return this; return this;
} }
async getProp(propName: string): Promise<any> { async getProps(propName ? : string): Promise < any > {
return this.sendMessage("getProp", { propName }); return this.sendMessage("getProp", {
propName
});
} }
async setProp(propName: string, value: any): Promise<ReactFiber> { async setProp(propName: string, value: any): Promise < ReactFiber > {
// Now async again await this.sendMessage("setProp", {
await this.sendMessage("setProp", { propName, value }); propName,
value
});
return this; return this;
} }
async forceUpdate(): Promise<ReactFiber> { async forceUpdate(): Promise < ReactFiber > {
// Now async again
await this.sendMessage("forceUpdate"); await this.sendMessage("forceUpdate");
return this; return this;
} }
@@ -0,0 +1,46 @@
import { waitForElm } from "@/SEQTA";
import ReactFiber from "../ReactFiber";
const handleNotificationClick = async (target: HTMLElement) => {
const notificationItem = target.closest('.notifications__item___2ErJN') as HTMLElement | null;
if (!notificationItem) return;
const buttonType = notificationItem.getAttribute('data-type');
if (buttonType !== 'message') return;
const notificationList = await ReactFiber.find('.notifications__list___rp2L2').getState();
const buttonId = notificationItem.getAttribute('data-id');
if (!buttonId) return;
const matchingNotification = notificationList.storeState.notifications.items.find(
(item: any) => item.notificationID === parseInt(buttonId)
);
await waitForElm('.Viewer__Viewer___32BH-', true, 20);
// Select the specific direct message
ReactFiber.find('.Viewer__Viewer___32BH-').setState({ selected: new Set([matchingNotification.message.messageID]) });
};
const clickListeners = [
{
selector: '.notifications__item___2ErJN',
handler: handleNotificationClick,
},
];
const registerClickListeners = () => {
console.log("Registering click listeners...");
document.addEventListener('click', (e) => {
const target = e.target as HTMLElement;
clickListeners.forEach(({ selector, handler }) => {
if (target.closest(selector)) {
handler(target);
}
});
});
};
export default registerClickListeners;