Fix Aktivitätenstatus
Dieser Commit ist enthalten in:
@ -5,9 +5,9 @@
|
||||
## Project Overview
|
||||
|
||||
- **Path**: `C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main`
|
||||
- **Files**: 220 files
|
||||
- **Size**: 76.9 MB
|
||||
- **Last Modified**: 2025-07-07 22:34
|
||||
- **Files**: 227 files
|
||||
- **Size**: 77.0 MB
|
||||
- **Last Modified**: 2025-07-08 08:19
|
||||
|
||||
## Technology Stack
|
||||
|
||||
@ -196,3 +196,4 @@ This project is managed with Claude Project Manager. To work with this project:
|
||||
- README updated on 2025-07-07 21:50:23
|
||||
- README updated on 2025-07-07 22:12:28
|
||||
- README updated on 2025-07-07 22:34:45
|
||||
- README updated on 2025-07-08 08:19:16
|
||||
|
||||
@ -51,12 +51,12 @@
|
||||
"name": "ClaudeProjectManager-main",
|
||||
"path": "C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main",
|
||||
"created_at": "2025-07-07T21:38:23.820122",
|
||||
"last_accessed": "2025-07-07T22:34:45.602409",
|
||||
"last_accessed": "2025-07-08T08:19:16.600227",
|
||||
"readme_path": "C:/Users/hendr/Desktop/IntelSight/ClaudeProjectManager-main\\CLAUDE_PROJECT_README.md",
|
||||
"description": "",
|
||||
"tags": [],
|
||||
"gitea_repo": null
|
||||
}
|
||||
],
|
||||
"last_updated": "2025-07-07T22:34:45.602409"
|
||||
"last_updated": "2025-07-08T08:19:16.600227"
|
||||
}
|
||||
@ -2966,10 +2966,16 @@ class MainWindow:
|
||||
def update_activity_status(self):
|
||||
"""Periodic update of activity status"""
|
||||
from services.activity_sync import activity_service
|
||||
from utils.logger import logger
|
||||
|
||||
logger.debug("Periodic activity status update triggered")
|
||||
|
||||
if activity_service.connected:
|
||||
# Update display with current activities
|
||||
logger.debug(f"Updating activity display with {len(activity_service.activities)} activities")
|
||||
self.update_activity_display(activity_service.activities)
|
||||
else:
|
||||
logger.debug("Activity service not connected, skipping update")
|
||||
|
||||
# Schedule next update
|
||||
self.root.after(5000, self.update_activity_status) # Update every 5 seconds
|
||||
|
||||
@ -25,7 +25,9 @@ class ProjectTile(ctk.CTkFrame):
|
||||
width=TILE_SIZE['width'],
|
||||
height=TILE_SIZE['height'],
|
||||
fg_color=COLORS['bg_vps'] if is_vps else COLORS['bg_tile'],
|
||||
corner_radius=10
|
||||
corner_radius=10,
|
||||
border_width=3,
|
||||
border_color=COLORS['bg_vps'] if is_vps else COLORS['bg_tile']
|
||||
)
|
||||
|
||||
self.project = project
|
||||
@ -38,6 +40,9 @@ class ProjectTile(ctk.CTkFrame):
|
||||
self.is_vps = is_vps
|
||||
self.is_running = is_running
|
||||
self.is_selected = False
|
||||
self.activity_animation_id = None
|
||||
self.activity_pulse_value = 0
|
||||
self.has_team_activity = False
|
||||
|
||||
self.grid_propagate(False)
|
||||
self.setup_ui()
|
||||
@ -582,39 +587,52 @@ pause
|
||||
|
||||
def _start_activity(self):
|
||||
"""Start activity for this project"""
|
||||
# Use main_window reference if available
|
||||
if hasattr(self, 'main_window') and self.main_window:
|
||||
self.main_window.start_activity(self.project)
|
||||
return
|
||||
|
||||
# Otherwise try to find in widget hierarchy
|
||||
widget = self
|
||||
while widget:
|
||||
if hasattr(widget, 'start_activity'):
|
||||
widget.start_activity(self.project)
|
||||
return
|
||||
widget = widget.master if hasattr(widget, 'master') else None
|
||||
from services.activity_sync import activity_service
|
||||
|
||||
# If not found, log error
|
||||
logger.error("Could not find start_activity method in widget hierarchy")
|
||||
logger.info(f"Starting activity for project: {self.project.name}")
|
||||
|
||||
# Update UI optimistically immediately
|
||||
self.update_activity_status(True, activity_service.user_name, True)
|
||||
|
||||
success = activity_service.start_activity(
|
||||
self.project.name,
|
||||
self.project.path,
|
||||
self.project.description if hasattr(self.project, 'description') else ""
|
||||
)
|
||||
|
||||
logger.info(f"Activity start result for {self.project.name}: success={success}")
|
||||
|
||||
if not success:
|
||||
# Revert on failure
|
||||
logger.error(f"Failed to start activity for {self.project.name}, reverting UI")
|
||||
self.update_activity_status(False)
|
||||
from tkinter import messagebox
|
||||
messagebox.showerror(
|
||||
"Fehler",
|
||||
"Aktivität konnte nicht gestartet werden."
|
||||
)
|
||||
|
||||
def _stop_activity(self):
|
||||
"""Stop current activity"""
|
||||
# Use main_window reference if available
|
||||
if hasattr(self, 'main_window') and self.main_window:
|
||||
self.main_window.stop_activity()
|
||||
return
|
||||
|
||||
# Otherwise try to find in widget hierarchy
|
||||
widget = self
|
||||
while widget:
|
||||
if hasattr(widget, 'stop_activity'):
|
||||
widget.stop_activity()
|
||||
return
|
||||
widget = widget.master if hasattr(widget, 'master') else None
|
||||
from services.activity_sync import activity_service
|
||||
|
||||
# If not found, log error
|
||||
logger.error("Could not find stop_activity method in widget hierarchy")
|
||||
logger.info(f"Stopping activity for project: {self.project.name}")
|
||||
|
||||
# Update UI optimistically immediately
|
||||
self.update_activity_status(False)
|
||||
|
||||
success = activity_service.stop_activity()
|
||||
|
||||
logger.info(f"Activity stop result: success={success}")
|
||||
|
||||
if not success:
|
||||
# Revert on failure - check if we're still the active project
|
||||
current = activity_service.get_current_activity()
|
||||
if current and current.get('projectName') == self.project.name:
|
||||
logger.error(f"Failed to stop activity for {self.project.name}, reverting UI")
|
||||
self.update_activity_status(True, activity_service.user_name, True)
|
||||
else:
|
||||
logger.error(f"Failed to stop activity, but no current activity found")
|
||||
|
||||
def _toggle_activity(self):
|
||||
"""Toggle activity for this project"""
|
||||
@ -653,7 +671,17 @@ pause
|
||||
|
||||
def update_activity_status(self, is_active: bool = False, user_name: str = None, is_own_activity: bool = False):
|
||||
"""Update activity indicator on tile"""
|
||||
logger.debug(f"update_activity_status called for {self.project.name}: is_active={is_active}, user_name={user_name}, is_own_activity={is_own_activity}")
|
||||
|
||||
if is_active:
|
||||
# Start border animation for team activity
|
||||
if not is_own_activity:
|
||||
self.has_team_activity = True
|
||||
self._start_activity_animation()
|
||||
else:
|
||||
self.has_team_activity = False
|
||||
self._stop_activity_animation()
|
||||
|
||||
# Show indicator with appropriate color
|
||||
if is_own_activity:
|
||||
# Green for own activity
|
||||
@ -680,6 +708,10 @@ pause
|
||||
# Hide indicator
|
||||
self.activity_indicator.pack_forget()
|
||||
|
||||
# Stop border animation
|
||||
self.has_team_activity = False
|
||||
self._stop_activity_animation()
|
||||
|
||||
# Update activity button if exists
|
||||
if hasattr(self, 'activity_button'):
|
||||
self.activity_button.configure(text="▶")
|
||||
@ -725,7 +757,16 @@ pause
|
||||
"""Check if this project has active users"""
|
||||
from services.activity_sync import activity_service
|
||||
|
||||
# Get all activities for this project
|
||||
logger.debug(f"check_activity called for project: {self.project.name}")
|
||||
|
||||
# First check if this is our own current activity
|
||||
current_activity = activity_service.get_current_activity()
|
||||
is_own_current = (current_activity and
|
||||
current_activity.get('projectName') == self.project.name)
|
||||
|
||||
logger.debug(f"Current activity check - is_own_current: {is_own_current}, current_activity: {current_activity}")
|
||||
|
||||
# Get all activities for this project from server
|
||||
active_users = []
|
||||
is_own_activity = False
|
||||
has_other_users = False
|
||||
@ -740,13 +781,76 @@ pause
|
||||
else:
|
||||
has_other_users = True
|
||||
|
||||
logger.debug(f"Server activities - active_users: {active_users}, is_own_activity: {is_own_activity}, has_other_users: {has_other_users}")
|
||||
|
||||
# If we have a local current activity, ensure it's included
|
||||
if is_own_current:
|
||||
is_own_activity = True
|
||||
if activity_service.user_name not in active_users:
|
||||
active_users.append(activity_service.user_name)
|
||||
logger.debug(f"Added local user to active_users: {activity_service.user_name}")
|
||||
|
||||
if active_users:
|
||||
# Show indicator with all active users
|
||||
user_text = ", ".join(active_users)
|
||||
# If both own and others are active, show as others (orange) to indicate collaboration
|
||||
self.update_activity_status(True, user_text, is_own_activity and not has_other_users)
|
||||
final_is_own = is_own_activity and not has_other_users
|
||||
logger.info(f"Updating activity status for {self.project.name}: active=True, users={user_text}, is_own={final_is_own}")
|
||||
self.update_activity_status(True, user_text, final_is_own)
|
||||
else:
|
||||
logger.info(f"Updating activity status for {self.project.name}: active=False")
|
||||
self.update_activity_status(False)
|
||||
|
||||
def _start_activity_animation(self):
|
||||
"""Start animated border for team activity"""
|
||||
if self.activity_animation_id:
|
||||
return # Animation already running
|
||||
|
||||
def animate():
|
||||
if not self.has_team_activity:
|
||||
self.activity_animation_id = None
|
||||
return
|
||||
|
||||
# Calculate pulsing color
|
||||
self.activity_pulse_value = (self.activity_pulse_value + 5) % 100
|
||||
pulse = abs(50 - self.activity_pulse_value) / 50 # 0 to 1 pulsing
|
||||
|
||||
# Interpolate between orange and a brighter orange
|
||||
base_color = COLORS['accent_warning'] # Orange
|
||||
bright_factor = 0.3 + (0.7 * pulse) # Pulse between 0.3 and 1.0 brightness
|
||||
|
||||
# Create pulsing border color
|
||||
if base_color.startswith('#'):
|
||||
# Convert hex to RGB, apply brightness, convert back
|
||||
r = int(base_color[1:3], 16)
|
||||
g = int(base_color[3:5], 16)
|
||||
b = int(base_color[5:7], 16)
|
||||
|
||||
# Apply brightness
|
||||
r = min(255, int(r + (255 - r) * (1 - bright_factor)))
|
||||
g = min(255, int(g + (255 - g) * (1 - bright_factor)))
|
||||
b = min(255, int(b + (255 - b) * (1 - bright_factor)))
|
||||
|
||||
pulse_color = f"#{r:02x}{g:02x}{b:02x}"
|
||||
else:
|
||||
pulse_color = base_color
|
||||
|
||||
self.configure(border_color=pulse_color)
|
||||
|
||||
# Continue animation
|
||||
self.activity_animation_id = self.after(50, animate)
|
||||
|
||||
animate()
|
||||
|
||||
def _stop_activity_animation(self):
|
||||
"""Stop animated border"""
|
||||
if self.activity_animation_id:
|
||||
self.after_cancel(self.activity_animation_id)
|
||||
self.activity_animation_id = None
|
||||
|
||||
# Reset border to default
|
||||
default_color = COLORS['bg_vps'] if self.is_vps else COLORS['bg_tile']
|
||||
self.configure(border_color=default_color)
|
||||
|
||||
class AddProjectTile(ctk.CTkFrame):
|
||||
"""Special tile for adding new projects"""
|
||||
|
||||
@ -85,10 +85,23 @@ class ActivitySyncService:
|
||||
@self.sio.event
|
||||
def activities_update(data):
|
||||
"""Handle activities update from server"""
|
||||
self.activities = data
|
||||
logger.debug(f"Received activities update: {len(data)} activities")
|
||||
logger.debug(f"Received raw activities update: {len(data)} items")
|
||||
|
||||
# Filter out inactive entries and ensure we only keep active ones
|
||||
active_activities = [
|
||||
activity for activity in data
|
||||
if activity.get('isActive', False)
|
||||
]
|
||||
|
||||
# Log details about the activities
|
||||
for activity in active_activities:
|
||||
logger.debug(f"Active: {activity.get('userName')} on {activity.get('projectName')}")
|
||||
|
||||
self.activities = active_activities
|
||||
logger.info(f"Activities update: {len(active_activities)} active (of {len(data)} total)")
|
||||
|
||||
if self.on_activities_update:
|
||||
self.on_activities_update(data)
|
||||
self.on_activities_update(active_activities)
|
||||
|
||||
@self.sio.event
|
||||
def connect_error(data):
|
||||
@ -131,36 +144,57 @@ class ActivitySyncService:
|
||||
|
||||
def start_activity(self, project_name: str, project_path: str, description: str = ""):
|
||||
"""Start a new activity"""
|
||||
logger.debug(f"start_activity called for: {project_name}")
|
||||
|
||||
if not self.connected or not self.sio:
|
||||
logger.warning("Not connected to activity server")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Set current activity immediately
|
||||
self.current_activity = {
|
||||
'projectName': project_name,
|
||||
'projectPath': project_path,
|
||||
'userId': self.user_id,
|
||||
'userName': self.user_name,
|
||||
'isActive': True
|
||||
}
|
||||
logger.debug(f"Set current_activity: {self.current_activity}")
|
||||
|
||||
# Emit to server
|
||||
self.sio.emit('activity-start', {
|
||||
'projectName': project_name,
|
||||
'projectPath': project_path,
|
||||
'description': description
|
||||
})
|
||||
self.current_activity = {
|
||||
'projectName': project_name,
|
||||
'projectPath': project_path
|
||||
}
|
||||
|
||||
logger.info(f"Started activity for project: {project_name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to start activity: {e}")
|
||||
self.current_activity = None
|
||||
return False
|
||||
|
||||
def stop_activity(self):
|
||||
"""Stop the current activity"""
|
||||
logger.debug(f"stop_activity called, current: {self.current_activity}")
|
||||
|
||||
if not self.connected or not self.sio:
|
||||
logger.warning("Not connected to activity server")
|
||||
return False
|
||||
|
||||
try:
|
||||
self.sio.emit('activity-stop')
|
||||
# Store project name for logging
|
||||
project_name = self.current_activity.get('projectName') if self.current_activity else 'None'
|
||||
|
||||
# Clear current activity immediately
|
||||
self.current_activity = None
|
||||
logger.info("Stopped current activity")
|
||||
logger.debug("Cleared current_activity")
|
||||
|
||||
# Emit to server
|
||||
self.sio.emit('activity-stop')
|
||||
|
||||
logger.info(f"Stopped activity for project: {project_name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to stop activity: {e}")
|
||||
@ -179,7 +213,9 @@ class ActivitySyncService:
|
||||
)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return data.get('activities', [])
|
||||
all_activities = data.get('activities', [])
|
||||
# Filter to only return active activities
|
||||
return [a for a in all_activities if a.get('isActive', False)]
|
||||
else:
|
||||
logger.error(f"Failed to fetch activities: {response.status_code}")
|
||||
return []
|
||||
@ -204,17 +240,18 @@ class ActivitySyncService:
|
||||
|
||||
def get_current_activity(self) -> Optional[Dict]:
|
||||
"""Get current user's activity"""
|
||||
logger.debug(f"get_current_activity called, returning: {self.current_activity}")
|
||||
return self.current_activity
|
||||
|
||||
def _fetch_initial_activities(self):
|
||||
"""Fetch initial activities after connection"""
|
||||
try:
|
||||
activities = self.get_activities()
|
||||
activities = self.get_activities() # Already filtered to only active
|
||||
if activities:
|
||||
self.activities = activities
|
||||
if self.on_activities_update:
|
||||
self.on_activities_update(activities)
|
||||
logger.info(f"Fetched {len(activities)} initial activities")
|
||||
logger.info(f"Fetched {len(activities)} active activities")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to fetch initial activities: {e}")
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren