feat: Add activity tracking toggle and customize status text
Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Has been cancelled
Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Has been cancelled
- Add toggle button to enable/disable activity tracking - Store tracking preference in localStorage - Change 'Do Not Disturb' to 'Nicht stören' (German) - Add better status text translations (online, offline, away) - Show disabled state when tracking is off - Stop fetching activity data when tracking is disabled
This commit is contained in:
@@ -13,6 +13,8 @@ import {
|
|||||||
ChevronUp,
|
ChevronUp,
|
||||||
Activity,
|
Activity,
|
||||||
X,
|
X,
|
||||||
|
Eye,
|
||||||
|
EyeOff,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
// Types matching your n8n output
|
// Types matching your n8n output
|
||||||
@@ -54,6 +56,14 @@ export default function ActivityFeed() {
|
|||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(true);
|
||||||
const [isMinimized, setIsMinimized] = useState(false);
|
const [isMinimized, setIsMinimized] = useState(false);
|
||||||
const [hasActivity, setHasActivity] = useState(false);
|
const [hasActivity, setHasActivity] = useState(false);
|
||||||
|
const [isTrackingEnabled, setIsTrackingEnabled] = useState(() => {
|
||||||
|
// Check localStorage for tracking preference
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const stored = localStorage.getItem('activityTrackingEnabled');
|
||||||
|
return stored !== 'false'; // Default to true if not set
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
const [quote, setQuote] = useState<{
|
const [quote, setQuote] = useState<{
|
||||||
content: string;
|
content: string;
|
||||||
author: string;
|
author: string;
|
||||||
@@ -61,6 +71,11 @@ export default function ActivityFeed() {
|
|||||||
|
|
||||||
// Fetch data every 30 seconds (optimized to match server cache)
|
// Fetch data every 30 seconds (optimized to match server cache)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Don't fetch if tracking is disabled
|
||||||
|
if (!isTrackingEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
// Add timestamp to prevent aggressive caching but respect server cache
|
// Add timestamp to prevent aggressive caching but respect server cache
|
||||||
@@ -109,7 +124,7 @@ export default function ActivityFeed() {
|
|||||||
// The n8n API already has 30s cache, so faster polling doesn't help
|
// The n8n API already has 30s cache, so faster polling doesn't help
|
||||||
const interval = setInterval(fetchData, 30000);
|
const interval = setInterval(fetchData, 30000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [isMinimized]);
|
}, [isMinimized, isTrackingEnabled]);
|
||||||
|
|
||||||
// Fetch nerdy quote when idle
|
// Fetch nerdy quote when idle
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -1144,6 +1159,45 @@ export default function ActivityFeed() {
|
|||||||
}
|
}
|
||||||
}, [hasActivity, quote]);
|
}, [hasActivity, quote]);
|
||||||
|
|
||||||
|
// Toggle tracking on/off
|
||||||
|
const toggleTracking = () => {
|
||||||
|
const newValue = !isTrackingEnabled;
|
||||||
|
setIsTrackingEnabled(newValue);
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
localStorage.setItem('activityTrackingEnabled', String(newValue));
|
||||||
|
}
|
||||||
|
// Clear data when disabling
|
||||||
|
if (!newValue) {
|
||||||
|
setData(null);
|
||||||
|
setHasActivity(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't render if tracking is disabled and no data
|
||||||
|
if (!isTrackingEnabled && !data) return null;
|
||||||
|
|
||||||
|
// If tracking disabled but we have data, show a disabled state
|
||||||
|
if (!isTrackingEnabled && data) {
|
||||||
|
return (
|
||||||
|
<div className="fixed bottom-4 right-4 md:bottom-6 md:right-6 z-40 pointer-events-auto">
|
||||||
|
<motion.div
|
||||||
|
initial={{ scale: 0, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
className="bg-black/80 backdrop-blur-xl border border-white/10 rounded-xl p-3 shadow-2xl"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={toggleTracking}
|
||||||
|
className="flex items-center gap-2 text-white/60 hover:text-white transition-colors"
|
||||||
|
title="Activity tracking is disabled. Click to enable."
|
||||||
|
>
|
||||||
|
<EyeOff size={16} />
|
||||||
|
<span className="text-xs">Tracking disabled</span>
|
||||||
|
</button>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
|
|
||||||
const activeCount = [
|
const activeCount = [
|
||||||
@@ -1206,6 +1260,22 @@ export default function ActivityFeed() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
{/* Toggle Tracking Button */}
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleTracking();
|
||||||
|
}}
|
||||||
|
className="p-1.5 hover:bg-white/10 rounded-lg transition-colors"
|
||||||
|
title={isTrackingEnabled ? "Disable activity tracking" : "Enable activity tracking"}
|
||||||
|
aria-label={isTrackingEnabled ? "Disable tracking" : "Enable tracking"}
|
||||||
|
>
|
||||||
|
{isTrackingEnabled ? (
|
||||||
|
<Eye size={14} className="text-white/60 hover:text-white" />
|
||||||
|
) : (
|
||||||
|
<EyeOff size={14} className="text-white/60 hover:text-white" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -1487,7 +1557,13 @@ export default function ActivityFeed() {
|
|||||||
/>
|
/>
|
||||||
<span className="text-[11px] font-medium text-white/50 capitalize">
|
<span className="text-[11px] font-medium text-white/50 capitalize">
|
||||||
{data.status.text === "dnd"
|
{data.status.text === "dnd"
|
||||||
? "Do Not Disturb"
|
? "Nicht stören"
|
||||||
|
: data.status.text === "online"
|
||||||
|
? "Online"
|
||||||
|
: data.status.text === "offline"
|
||||||
|
? "Offline"
|
||||||
|
: data.status.text === "away"
|
||||||
|
? "Abwesend"
|
||||||
: data.status.text}
|
: data.status.text}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user