Remove Matrix Widget API connection and logging
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 20s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 20s
This commit is contained in:
26
package-lock.json
generated
26
package-lock.json
generated
@@ -13,7 +13,6 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"matrix-widget-api": "^1.16.0",
|
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0"
|
"react-dom": "^19.2.0"
|
||||||
},
|
},
|
||||||
@@ -1425,12 +1424,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/events": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.15",
|
"version": "7.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||||
@@ -2547,15 +2540,6 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/events": {
|
|
||||||
"version": "3.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
|
||||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.8.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||||
@@ -3232,16 +3216,6 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/matrix-widget-api": {
|
|
||||||
"version": "1.16.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/matrix-widget-api/-/matrix-widget-api-1.16.0.tgz",
|
|
||||||
"integrity": "sha512-OCsCzEN54jWamvWkBa7PqcKdlOhLA+nJbUyqsATHvzb4/NMcjdUZWSDurZxyNE5eYlNwxClA6Hw20mzJEKJbvg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/events": "^3.0.0",
|
|
||||||
"events": "^3.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/media-typer": {
|
"node_modules/media-typer": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"lodash.debounce": "^4.0.8",
|
"lodash.debounce": "^4.0.8",
|
||||||
"lucide-react": "^0.562.0",
|
"lucide-react": "^0.562.0",
|
||||||
"matrix-widget-api": "^1.16.0",
|
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0"
|
"react-dom": "^19.2.0"
|
||||||
},
|
},
|
||||||
|
|||||||
116
src/App.tsx
116
src/App.tsx
@@ -9,14 +9,7 @@ import { GifGrid } from './components/GifGrid';
|
|||||||
import './styles/global.css';
|
import './styles/global.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [logs, setLogs] = useState<string[]>([]);
|
const { theme, sendGif } = useMatrixWidget();
|
||||||
|
|
||||||
const addLog = (msg: string) => {
|
|
||||||
setLogs(prev => [`[${new Date().toLocaleTimeString()}] ${msg}`, ...prev].slice(0, 5));
|
|
||||||
console.log(msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { theme, sendGif, ready, refreshPermissions } = useMatrixWidget(addLog);
|
|
||||||
const { favorites, toggleFavorite, isFavorite } = useFavorites();
|
const { favorites, toggleFavorite, isFavorite } = useFavorites();
|
||||||
|
|
||||||
const [categories, setCategories] = useState<string[]>([]);
|
const [categories, setCategories] = useState<string[]>([]);
|
||||||
@@ -26,8 +19,6 @@ function App() {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [sendingState, setSendingState] = useState<{ id: string, status: 'sending' | 'sent' | 'error', errorMsg?: string } | null>(null);
|
const [sendingState, setSendingState] = useState<{ id: string, status: 'sending' | 'sent' | 'error', errorMsg?: string } | null>(null);
|
||||||
|
|
||||||
// Apply theme
|
|
||||||
|
|
||||||
// Apply theme
|
// Apply theme
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.documentElement.setAttribute('data-theme', theme);
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
@@ -36,7 +27,6 @@ function App() {
|
|||||||
// Load categories on mount
|
// Load categories on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
gifService.getCategories().then(setCategories);
|
gifService.getCategories().then(setCategories);
|
||||||
addLog("App mounted, waiting for Matrix...");
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Fetch GIFs
|
// Fetch GIFs
|
||||||
@@ -60,7 +50,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
setGifs(results);
|
setGifs(results);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
addLog(`Fetch error: ${error.message}`);
|
console.error(`Fetch error: ${error.message}`);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -84,22 +74,15 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleGifSelect = async (gif: GifObject) => {
|
const handleGifSelect = async (gif: GifObject) => {
|
||||||
if (!ready) {
|
|
||||||
addLog("⚠️ Widget not ready, trying direct send...");
|
|
||||||
}
|
|
||||||
|
|
||||||
setSendingState({ id: gif.id, status: 'sending' });
|
setSendingState({ id: gif.id, status: 'sending' });
|
||||||
addLog(`Sending GIF: ${gif.id}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sendGif(gif);
|
await sendGif(gif);
|
||||||
setSendingState({ id: gif.id, status: 'sent' });
|
setSendingState({ id: gif.id, status: 'sent' });
|
||||||
addLog("Send success!");
|
|
||||||
setTimeout(() => setSendingState(null), 2000);
|
setTimeout(() => setSendingState(null), 2000);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
const msg = e.message || JSON.stringify(e);
|
const msg = e.message || JSON.stringify(e);
|
||||||
setSendingState({ id: gif.id, status: 'error', errorMsg: msg });
|
setSendingState({ id: gif.id, status: 'error', errorMsg: msg });
|
||||||
addLog(`Send Failed: ${msg}`);
|
|
||||||
setTimeout(() => setSendingState(null), 5000);
|
setTimeout(() => setSendingState(null), 5000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -108,81 +91,26 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app-container">
|
<div className="app-container">
|
||||||
<div style={{
|
{sendingState && (
|
||||||
padding: '8px 12px',
|
<div style={{
|
||||||
fontSize: '12px',
|
position: 'fixed',
|
||||||
color: ready ? '#4ECDC4' : '#FF6B6B',
|
top: 0,
|
||||||
display: 'flex',
|
left: 0,
|
||||||
justifyContent: 'space-between',
|
right: 0,
|
||||||
alignItems: 'center'
|
zIndex: 1000,
|
||||||
}}>
|
background: sendingState.status === 'error' ? '#FF6B6B' : '#4ECDC4',
|
||||||
<span>Status: {ready ? 'Connected' : 'Connecting...'}</span>
|
color: '#1A1B1E',
|
||||||
{sendingState && (
|
padding: '8px',
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
textAlign: 'center',
|
||||||
<span style={{
|
fontWeight: 'bold',
|
||||||
fontWeight: 'bold',
|
fontSize: '14px',
|
||||||
color: sendingState.status === 'error' ? '#FF6B6B' : '#FFEEAD'
|
boxShadow: '0 2px 4px rgba(0,0,0,0.2)'
|
||||||
}}>
|
}}>
|
||||||
{sendingState.status === 'sending' && 'Sending...'}
|
{sendingState.status === 'sending' && 'Sending GIF...'}
|
||||||
{sendingState.status === 'sent' && 'Sent!'}
|
{sendingState.status === 'sent' && 'GIF Sent!'}
|
||||||
{sendingState.status === 'error' && 'Failed'}
|
{sendingState.status === 'error' && `Error: ${sendingState.errorMsg || 'Failed to send'}`}
|
||||||
</span>
|
</div>
|
||||||
{sendingState.status === 'error' && (
|
)}
|
||||||
<div style={{ display: 'flex', gap: '4px' }}>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
const id = prompt("Please paste the Widget ID from the logs (looks like !abc...):");
|
|
||||||
if (id) {
|
|
||||||
const url = new URL(window.location.href);
|
|
||||||
url.searchParams.set('widgetId', id);
|
|
||||||
window.location.href = url.toString();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
background: '#2F3136', border: '1px solid #555', borderRadius: '4px',
|
|
||||||
color: 'white', padding: '2px 6px', fontSize: '10px', cursor: 'pointer'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Set ID
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
addLog("Requesting permissions...");
|
|
||||||
refreshPermissions();
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
background: '#5865F2', border: 'none', borderRadius: '4px',
|
|
||||||
color: 'white', padding: '2px 6px', fontSize: '10px', cursor: 'pointer'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Fix Permissions
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Debug Log Box - Always visible */}
|
|
||||||
<div style={{
|
|
||||||
fontSize: '11px',
|
|
||||||
color: '#ccc',
|
|
||||||
padding: '8px 12px',
|
|
||||||
marginBottom: '8px',
|
|
||||||
fontFamily: 'monospace',
|
|
||||||
maxHeight: '120px',
|
|
||||||
minHeight: '60px',
|
|
||||||
overflowY: 'auto',
|
|
||||||
backgroundColor: 'rgba(0,0,0,0.3)',
|
|
||||||
borderRadius: '4px',
|
|
||||||
border: '1px solid rgba(255,255,255,0.1)'
|
|
||||||
}}>
|
|
||||||
{logs.length === 0 ? (
|
|
||||||
<div style={{ color: '#666' }}>Waiting for logs...</div>
|
|
||||||
) : (
|
|
||||||
logs.map((log, i) => <div key={i}>{log}</div>)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SearchBar onSearch={handleSearch} />
|
<SearchBar onSearch={handleSearch} />
|
||||||
|
|
||||||
|
|||||||
@@ -1,94 +1,16 @@
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useState } from 'react';
|
||||||
import { WidgetApi } from 'matrix-widget-api';
|
|
||||||
import type { GifObject } from '../types';
|
import type { GifObject } from '../types';
|
||||||
|
|
||||||
export const useMatrixWidget = (onLog?: (msg: string) => void) => {
|
export const useMatrixWidget = () => {
|
||||||
const widgetApiRef = useRef<WidgetApi | null>(null);
|
|
||||||
const [theme] = useState<'light' | 'dark'>(() => {
|
const [theme] = useState<'light' | 'dark'>(() => {
|
||||||
const themeParam = new URLSearchParams(window.location.search).get('theme');
|
const themeParam = new URLSearchParams(window.location.search).get('theme');
|
||||||
return (themeParam === 'light' || themeParam === 'dark') ? themeParam : 'dark';
|
return (themeParam === 'light' || themeParam === 'dark') ? themeParam : 'dark';
|
||||||
});
|
});
|
||||||
const [ready, setReady] = useState(false);
|
|
||||||
const initialized = useRef(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (initialized.current) return;
|
|
||||||
initialized.current = true;
|
|
||||||
|
|
||||||
const log = (msg: string) => {
|
|
||||||
console.log(`[Widget] ${msg}`);
|
|
||||||
onLog?.(msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
|
||||||
const hashParams = new URLSearchParams(window.location.hash.substring(1));
|
|
||||||
|
|
||||||
log(`URL Search: ${window.location.search}`);
|
|
||||||
log(`URL Hash: ${window.location.hash}`);
|
|
||||||
|
|
||||||
const widgetId = searchParams.get('widgetId') || hashParams.get('widgetId');
|
|
||||||
// Decode the ID in case it was double-encoded or raw from URL
|
|
||||||
const finalId = widgetId ? decodeURIComponent(widgetId) : 'my-widget-id';
|
|
||||||
|
|
||||||
log(`Widget ID: ${finalId}`);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const api = new WidgetApi(finalId);
|
|
||||||
|
|
||||||
log("Requesting capabilities...");
|
|
||||||
api.requestCapabilities([
|
|
||||||
'm.send_event:m.room.message',
|
|
||||||
'm.sticker',
|
|
||||||
'm.send_event:m.sticker',
|
|
||||||
]);
|
|
||||||
|
|
||||||
log("Starting Widget API...");
|
|
||||||
api.start();
|
|
||||||
|
|
||||||
api.on("ready", () => {
|
|
||||||
log("Widget API Ready!");
|
|
||||||
setReady(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
api.on("error", (err: Error) => {
|
|
||||||
log(`Widget Error: ${err.message}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
widgetApiRef.current = api;
|
|
||||||
|
|
||||||
// Set a timeout to warn if not ready after 5 seconds
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!ready) {
|
|
||||||
log("⚠️ Still not ready after 5s. Are you running inside a Matrix widget?");
|
|
||||||
}
|
|
||||||
}, 5000);
|
|
||||||
|
|
||||||
} catch (error: any) {
|
|
||||||
log(`Init Error: ${error.message}`);
|
|
||||||
}
|
|
||||||
}, [onLog]);
|
|
||||||
|
|
||||||
const refreshPermissions = async () => {
|
|
||||||
if (!widgetApiRef.current) return;
|
|
||||||
onLog?.("Manual permission refresh triggered...");
|
|
||||||
try {
|
|
||||||
await widgetApiRef.current.requestCapabilities([
|
|
||||||
'm.sticker',
|
|
||||||
'm.send_event:m.sticker',
|
|
||||||
'm.send_event:m.room.message',
|
|
||||||
]);
|
|
||||||
onLog?.("Capabilities request sent.");
|
|
||||||
} catch (e) {
|
|
||||||
onLog?.(`Cap Request Error: ${e}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const sendGif = async (gif: GifObject) => {
|
const sendGif = async (gif: GifObject) => {
|
||||||
const { tinygif } = gif.media_formats;
|
const { tinygif } = gif.media_formats;
|
||||||
const title = gif.title || "GIF";
|
const title = gif.title || "GIF";
|
||||||
|
|
||||||
onLog?.(`Sending: ${title}`);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get Matrix homeserver and access token from URL params
|
// Get Matrix homeserver and access token from URL params
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
@@ -103,8 +25,6 @@ export const useMatrixWidget = (onLog?: (msg: string) => void) => {
|
|||||||
// Use tinygif for smaller size
|
// Use tinygif for smaller size
|
||||||
const gifUrl = tinygif.url;
|
const gifUrl = tinygif.url;
|
||||||
|
|
||||||
onLog?.('Uploading to Matrix...');
|
|
||||||
|
|
||||||
// Upload GIF through our proxy
|
// Upload GIF through our proxy
|
||||||
const uploadResponse = await fetch(
|
const uploadResponse = await fetch(
|
||||||
`/api/gif-upload?url=${encodeURIComponent(gifUrl)}&homeserver=${encodeURIComponent(homeserver)}&accessToken=${encodeURIComponent(accessToken)}`
|
`/api/gif-upload?url=${encodeURIComponent(gifUrl)}&homeserver=${encodeURIComponent(homeserver)}&accessToken=${encodeURIComponent(accessToken)}`
|
||||||
@@ -117,8 +37,6 @@ export const useMatrixWidget = (onLog?: (msg: string) => void) => {
|
|||||||
|
|
||||||
const { mxcUrl, size } = await uploadResponse.json();
|
const { mxcUrl, size } = await uploadResponse.json();
|
||||||
|
|
||||||
onLog?.(`Uploaded ${(size / 1024).toFixed(1)}KB, sending...`);
|
|
||||||
|
|
||||||
// Send directly via Matrix Client-Server API
|
// Send directly via Matrix Client-Server API
|
||||||
const txnId = `m${Date.now()}.${Math.random()}`;
|
const txnId = `m${Date.now()}.${Math.random()}`;
|
||||||
const sendUrl = `${homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/send/m.sticker/${txnId}`;
|
const sendUrl = `${homeserver}/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/send/m.sticker/${txnId}`;
|
||||||
@@ -146,12 +64,11 @@ export const useMatrixWidget = (onLog?: (msg: string) => void) => {
|
|||||||
throw new Error(error.error || 'Send failed');
|
throw new Error(error.error || 'Send failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
onLog?.('✓ GIF sent!');
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
onLog?.(`✗ Failed: ${error.message}`);
|
console.error(`Failed to send GIF: ${error.message}`);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { theme, sendGif, ready, refreshPermissions };
|
return { theme, sendGif };
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user