Major Feature Update: Modern Chat System & Admin Management
Features Added: 🔥 Modern Chat System: - Real-time messaging with modern Tailwind CSS design - Post-linked discussions for adventure sharing - Chat categories (general, technical-support, adventure-planning) - Mobile-responsive interface with gradient backgrounds - JavaScript polling for live message updates 🎯 Comprehensive Admin Panel: - Chat room management with merge capabilities - Password reset system with email templates - User management with admin controls - Chat statistics and analytics dashboard - Room binding to posts and categorization �� Mobile API Integration: - RESTful API endpoints at /api/v1/chat - Session-based authentication for mobile apps - Comprehensive endpoints for rooms, messages, users - Mobile app compatibility (React Native, Flutter) 🛠️ Technical Improvements: - Enhanced database models with ChatRoom categories - Password reset token system with email verification - Template synchronization fixes for Docker deployment - Migration scripts for database schema updates - Improved error handling and validation 🎨 UI/UX Enhancements: - Modern card-based layouts matching app design - Consistent styling across chat and admin interfaces - Mobile-optimized touch interactions - Professional gradient designs and glass morphism effects 📚 Documentation: - Updated README with comprehensive API documentation - Added deployment instructions for Docker (port 8100) - Configuration guide for production environments - Mobile integration examples and endpoints This update transforms the platform into a comprehensive motorcycle adventure community with modern chat capabilities and professional admin management tools.
This commit is contained in:
@@ -5,6 +5,7 @@ Provides HTML templates and endpoints for web-based chat
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
|
||||
from flask_login import login_required, current_user
|
||||
from sqlalchemy import desc, and_
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from app.extensions import db
|
||||
from app.models import ChatRoom, ChatMessage, ChatParticipant, Post
|
||||
@@ -90,7 +91,109 @@ def create_room_form():
|
||||
desc(Post.created_at)
|
||||
).limit(20).all()
|
||||
|
||||
return render_template('chat/create_room.html', posts=recent_posts)
|
||||
# Check if a specific post was requested
|
||||
pre_selected_post = request.args.get('post_id')
|
||||
if pre_selected_post:
|
||||
try:
|
||||
pre_selected_post = int(pre_selected_post)
|
||||
except ValueError:
|
||||
pre_selected_post = None
|
||||
|
||||
return render_template('chat/create_room.html', posts=recent_posts, pre_selected_post=pre_selected_post)
|
||||
|
||||
|
||||
@chat.route('/create', methods=['POST'])
|
||||
@login_required
|
||||
def create_room():
|
||||
"""Create a new chat room"""
|
||||
room_name = request.form.get('room_name')
|
||||
description = request.form.get('description', '')
|
||||
room_type = request.form.get('room_type', 'general')
|
||||
is_private = bool(request.form.get('is_private'))
|
||||
related_post_id = request.form.get('related_post_id')
|
||||
|
||||
if not room_name:
|
||||
flash('Room name is required.', 'error')
|
||||
return redirect(url_for('chat.create_room_form'))
|
||||
|
||||
# Convert to integer if post ID is provided
|
||||
if related_post_id:
|
||||
try:
|
||||
related_post_id = int(related_post_id)
|
||||
# Verify the post exists
|
||||
related_post = Post.query.get(related_post_id)
|
||||
if not related_post:
|
||||
flash('Selected post does not exist.', 'error')
|
||||
return redirect(url_for('chat.create_room_form'))
|
||||
# If post is selected, set room type to post_discussion
|
||||
room_type = 'post_discussion'
|
||||
except ValueError:
|
||||
related_post_id = None
|
||||
else:
|
||||
related_post_id = None
|
||||
# If no post selected, ensure it's general discussion
|
||||
if room_type == 'post_discussion':
|
||||
room_type = 'general'
|
||||
|
||||
# Check if room name already exists
|
||||
existing_room = ChatRoom.query.filter_by(name=room_name).first()
|
||||
if existing_room:
|
||||
flash('A room with that name already exists.', 'error')
|
||||
return redirect(url_for('chat.create_room_form'))
|
||||
|
||||
try:
|
||||
# Create the room
|
||||
room = ChatRoom(
|
||||
name=room_name,
|
||||
description=description,
|
||||
room_type=room_type,
|
||||
is_private=is_private,
|
||||
is_active=True,
|
||||
created_by_id=current_user.id,
|
||||
related_post_id=related_post_id
|
||||
)
|
||||
db.session.add(room)
|
||||
db.session.flush() # Get the room ID
|
||||
|
||||
# Add creator as participant with admin role
|
||||
participant = ChatParticipant(
|
||||
room_id=room.id,
|
||||
user_id=current_user.id,
|
||||
role='admin',
|
||||
joined_at=datetime.utcnow()
|
||||
)
|
||||
db.session.add(participant)
|
||||
|
||||
# Add welcome message
|
||||
if related_post_id:
|
||||
welcome_content = f"Welcome to the discussion for '{related_post.title}'! This room was created by {current_user.nickname} to discuss this post."
|
||||
else:
|
||||
welcome_content = f"Welcome to {room_name}! This room was created by {current_user.nickname}."
|
||||
|
||||
welcome_message = ChatMessage(
|
||||
content=welcome_content,
|
||||
room_id=room.id,
|
||||
sender_id=current_user.id,
|
||||
is_system_message=True
|
||||
)
|
||||
db.session.add(welcome_message)
|
||||
|
||||
# Update room activity
|
||||
room.last_activity = datetime.utcnow()
|
||||
room.message_count = 1
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if related_post_id:
|
||||
flash(f'Chat room "{room_name}" created successfully and linked to the post!', 'success')
|
||||
else:
|
||||
flash(f'Chat room "{room_name}" created successfully!', 'success')
|
||||
return redirect(url_for('chat.room', room_id=room.id))
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash(f'Error creating room: {str(e)}', 'error')
|
||||
return redirect(url_for('chat.create_room_form'))
|
||||
|
||||
@chat.route('/support')
|
||||
@login_required
|
||||
@@ -123,3 +226,51 @@ def embed_post_chat(post_id):
|
||||
return render_template('chat/embed.html',
|
||||
post=post,
|
||||
discussion_room=discussion_room)
|
||||
|
||||
|
||||
@chat.route('/post-discussions')
|
||||
@login_required
|
||||
def post_discussions():
|
||||
"""View all chat rooms related to posts"""
|
||||
page = request.args.get('page', 1, type=int)
|
||||
|
||||
# Get all rooms that are linked to posts
|
||||
post_rooms = ChatRoom.query.filter(
|
||||
ChatRoom.related_post_id.isnot(None),
|
||||
ChatRoom.is_active == True
|
||||
).join(Post).order_by(ChatRoom.last_activity.desc()).paginate(
|
||||
page=page, per_page=20, error_out=False
|
||||
)
|
||||
|
||||
# Get statistics
|
||||
total_post_discussions = ChatRoom.query.filter(
|
||||
ChatRoom.related_post_id.isnot(None),
|
||||
ChatRoom.is_active == True
|
||||
).count()
|
||||
|
||||
active_discussions = ChatRoom.query.filter(
|
||||
ChatRoom.related_post_id.isnot(None),
|
||||
ChatRoom.is_active == True,
|
||||
ChatRoom.last_activity >= datetime.utcnow() - timedelta(days=7)
|
||||
).count()
|
||||
|
||||
return render_template('chat/post_discussions.html',
|
||||
rooms=post_rooms,
|
||||
total_discussions=total_post_discussions,
|
||||
active_discussions=active_discussions)
|
||||
|
||||
|
||||
@chat.route('/post/<int:post_id>/discussions')
|
||||
@login_required
|
||||
def post_specific_discussions(post_id):
|
||||
"""View all chat rooms for a specific post"""
|
||||
post = Post.query.get_or_404(post_id)
|
||||
|
||||
# Get all rooms for this specific post
|
||||
rooms = ChatRoom.query.filter(
|
||||
ChatRoom.related_post_id == post_id,
|
||||
ChatRoom.is_active == True
|
||||
).order_by(ChatRoom.last_activity.desc()).all()
|
||||
|
||||
return render_template('chat/post_specific_discussions.html',
|
||||
post=post, rooms=rooms)
|
||||
|
||||
Reference in New Issue
Block a user