sojorn/go-backend/internal/models/post.go
Patrick Britton 3c4680bdd7 Initial commit: Complete threaded conversation system with inline replies
**Major Features Added:**
- **Inline Reply System**: Replace compose screen with inline reply boxes
- **Thread Navigation**: Parent/child navigation with jump functionality
- **Chain Flow UI**: Reply counts, expand/collapse animations, visual hierarchy
- **Enhanced Animations**: Smooth transitions, hover effects, micro-interactions

 **Frontend Changes:**
- **ThreadedCommentWidget**: Complete rewrite with animations and navigation
- **ThreadNode Model**: Added parent references and descendant counting
- **ThreadedConversationScreen**: Integrated navigation handlers
- **PostDetailScreen**: Replaced with threaded conversation view
- **ComposeScreen**: Added reply indicators and context
- **PostActions**: Fixed visibility checks for chain buttons

 **Backend Changes:**
- **API Route**: Added /posts/:id/thread endpoint
- **Post Repository**: Include allow_chain and visibility fields in feed
- **Thread Handler**: Support for fetching post chains

 **UI/UX Improvements:**
- **Reply Context**: Clear indication when replying to specific posts
- **Character Counting**: 500 character limit with live counter
- **Visual Hierarchy**: Depth-based indentation and styling
- **Smooth Animations**: SizeTransition, FadeTransition, hover states
- **Chain Navigation**: Parent/child buttons with visual feedback

 **Technical Enhancements:**
- **Animation Controllers**: Proper lifecycle management
- **State Management**: Clean separation of concerns
- **Navigation Callbacks**: Reusable navigation system
- **Error Handling**: Graceful fallbacks and user feedback

This creates a Reddit-style threaded conversation experience with smooth
animations, inline replies, and intuitive navigation between posts in a chain.
2026-01-30 07:40:19 -06:00

91 lines
3.8 KiB
Go

package models
import (
"time"
"github.com/google/uuid"
)
type Post struct {
ID uuid.UUID `json:"id" db:"id"`
AuthorID uuid.UUID `json:"author_id" db:"author_id"`
CategoryID *uuid.UUID `json:"category_id" db:"category_id"`
Body string `json:"body" db:"body"`
Status string `json:"status" db:"status"` // active, flagged, removed
ToneLabel *string `json:"tone_label" db:"tone_label"`
CISScore *float64 `json:"cis_score" db:"cis_score"`
ImageURL *string `json:"image_url" db:"image_url"`
VideoURL *string `json:"video_url" db:"video_url"`
ThumbnailURL *string `json:"thumbnail_url" db:"thumbnail_url"`
DurationMS int `json:"duration_ms" db:"duration_ms"`
BodyFormat string `json:"body_format" db:"body_format"` // plain, markdown
BackgroundID *string `json:"background_id" db:"background_id"`
Tags []string `json:"tags" db:"tags"`
IsBeacon bool `json:"is_beacon" db:"is_beacon"`
BeaconType *string `json:"beacon_type" db:"beacon_type"`
Location any `json:"location,omitempty" db:"location"` // geography(POINT)
Lat *float64 `json:"lat,omitempty"`
Long *float64 `json:"long,omitempty"`
Confidence float64 `json:"confidence_score" db:"confidence_score"`
IsActiveBeacon bool `json:"is_active_beacon" db:"is_active_beacon"`
AllowChain bool `json:"allow_chain" db:"allow_chain"`
ChainParentID *uuid.UUID `json:"chain_parent_id" db:"chain_parent_id"`
Visibility string `json:"visibility" db:"visibility"`
ExpiresAt *time.Time `json:"expires_at" db:"expires_at"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
EditedAt *time.Time `json:"edited_at,omitempty" db:"edited_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"`
// Joined fields (Scan targets)
AuthorHandle string `json:"-" db:"author_handle"`
AuthorDisplayName string `json:"-" db:"author_display_name"`
AuthorAvatarURL string `json:"-" db:"author_avatar_url"`
LikeCount int `json:"like_count" db:"like_count"`
CommentCount int `json:"comment_count" db:"comment_count"`
IsLiked bool `json:"is_liked" db:"is_liked"`
// Nested objects for JSON API
Author *AuthorProfile `json:"author,omitempty"`
IsSponsored bool `json:"is_sponsored,omitempty"`
}
type AuthorProfile struct {
ID uuid.UUID `json:"id"`
Handle string `json:"handle"`
DisplayName string `json:"display_name"`
AvatarURL string `json:"avatar_url"`
}
type PostMetrics struct {
PostID uuid.UUID `json:"post_id" db:"post_id"`
LikeCount int `json:"like_count" db:"like_count"`
SaveCount int `json:"save_count" db:"save_count"`
ViewCount int `json:"view_count" db:"view_count"`
CommentCount int `json:"comment_count" db:"comment_count"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
type Category struct {
ID uuid.UUID `json:"id" db:"id"`
Slug string `json:"slug" db:"slug"`
Name string `json:"name" db:"name"`
Description *string `json:"description" db:"description"`
IsSensitive bool `json:"is_sensitive" db:"is_sensitive"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
type Comment struct {
ID uuid.UUID `json:"id" db:"id"`
PostID string `json:"post_id" db:"post_id"`
AuthorID uuid.UUID `json:"author_id" db:"author_id"`
Body string `json:"body" db:"body"`
Status string `json:"status" db:"status"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty" db:"deleted_at"`
}
type TagResult struct {
Tag string `json:"tag"`
Count int `json:"count"`
}