mirror of
https://github.com/indentlabs/notebook.git
synced 2025-10-26 11:19:22 +00:00
new stream design
This commit is contained in:
parent
0a027681c7
commit
df088d195a
78
STREAM_REDESIGN_SUMMARY.md
Normal file
78
STREAM_REDESIGN_SUMMARY.md
Normal file
@ -0,0 +1,78 @@
|
||||
# Stream Redesign Summary
|
||||
|
||||
## 🎨 Professional, Minimalist, and Fun Redesign Complete!
|
||||
|
||||
The social activity stream has been completely redesigned with a modern, professional aesthetic that maintains fun and engaging elements.
|
||||
|
||||
### ✅ Key Improvements Made
|
||||
|
||||
#### **Visual Design**
|
||||
- **Glass morphism effects** - Frosted glass header with backdrop blur
|
||||
- **Gradient backgrounds** - Subtle gradients from indigo to purple throughout
|
||||
- **Professional color palette** - Indigo, purple, and pink accents with clean grays
|
||||
- **Enhanced typography** - Better font weights, sizing, and spacing hierarchy
|
||||
- **Rounded corners** - Modern 2xl border radius for cards and components
|
||||
|
||||
#### **Interactive Elements**
|
||||
- **Smooth animations** - Hover effects, scale transforms, and smooth transitions
|
||||
- **Gradient buttons** - Eye-catching CTAs with shadow effects and hover states
|
||||
- **Interactive cards** - Subtle glow effects on hover, professional shadows
|
||||
- **Status indicators** - Online status dots, content type badges
|
||||
- **Micro-interactions** - Button hover states, form focus effects
|
||||
|
||||
#### **Layout & UX**
|
||||
- **Sticky glass header** - Professional navigation that stays visible
|
||||
- **Card-based design** - Clean, organized content in beautiful cards
|
||||
- **Better spacing** - Generous whitespace and consistent padding
|
||||
- **Visual hierarchy** - Clear content organization with dividers and sections
|
||||
- **Mobile responsive** - Works beautifully across all device sizes
|
||||
|
||||
#### **Technical Fixes**
|
||||
- **MaterializeCSS conflicts resolved** - No more double dropdowns
|
||||
- **Form styling** - Tailwind-styled selects work properly
|
||||
- **JavaScript protection** - Prevents MaterializeCSS initialization on Tailwind components
|
||||
|
||||
### 🚀 New Features
|
||||
|
||||
#### **Enhanced Share Creation**
|
||||
- Beautiful gradient-bordered creation card
|
||||
- Better placeholder text and instructions
|
||||
- Visual feedback for public sharing
|
||||
- Professional form styling
|
||||
|
||||
#### **Modern Feed Items**
|
||||
- Glass morphism card design
|
||||
- Content type badges with brand colors
|
||||
- Interactive hover effects
|
||||
- Better comment threading
|
||||
- Professional interaction buttons (Like, Comment, Share)
|
||||
|
||||
#### **Improved Navigation**
|
||||
- Icon-enhanced navigation buttons
|
||||
- Search with keyboard shortcut indicators
|
||||
- Professional toggle states
|
||||
- Smooth transitions between views
|
||||
|
||||
#### **Professional Empty States**
|
||||
- Beautiful empty state illustrations
|
||||
- Encouraging call-to-action buttons
|
||||
- Context-appropriate messaging
|
||||
|
||||
### 🎯 Design Principles Applied
|
||||
|
||||
1. **Professional** - Clean lines, consistent spacing, professional typography
|
||||
2. **Minimalist** - Removed visual clutter, focused on content
|
||||
3. **Fun** - Gradients, animations, playful hover effects
|
||||
4. **Modern** - Glass morphism, subtle shadows, rounded corners
|
||||
5. **Accessible** - Good contrast, clear hierarchy, readable fonts
|
||||
|
||||
### 💫 Visual Effects Used
|
||||
|
||||
- **Backdrop blur filters** for glass effects
|
||||
- **CSS gradients** for backgrounds and buttons
|
||||
- **Box shadows** with color tinting
|
||||
- **Transform animations** for hover states
|
||||
- **Opacity transitions** for smooth interactions
|
||||
- **Border radius** for modern appearance
|
||||
|
||||
The stream now feels like a premium, professional social platform while maintaining the creative and fun nature that makes Notebook.ai special!
|
||||
@ -1,4 +1,5 @@
|
||||
class ContentPageSharesController < ApplicationController
|
||||
layout 'tailwind', only: [:show]
|
||||
before_action :authenticate_user!, except: [:show]
|
||||
before_action :set_content_page_share, only: [
|
||||
:show, :edit, :update, :destroy,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
class StreamController < ApplicationController
|
||||
layout 'tailwind', only: [:index]
|
||||
layout 'tailwind', only: [:index, :global]
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_stream_navbar_actions, only: [:index, :global]
|
||||
before_action :set_stream_navbar_color, only: [:index, :global]
|
||||
before_action :set_sidenav_expansion
|
||||
before_action :cache_linkable_content_for_each_content_type, only: [:index]
|
||||
before_action :cache_linkable_content_for_each_content_type, only: [:index, :global]
|
||||
|
||||
def index
|
||||
@page_title = "What's happening"
|
||||
@ -18,7 +18,17 @@ class StreamController < ApplicationController
|
||||
.order('created_at DESC')
|
||||
.includes([:content_page, :secondary_content_page])
|
||||
.includes({ share_comments: [:user], user: [:avatar_attachment] })
|
||||
.limit(25)
|
||||
|
||||
# Apply search filter if present
|
||||
if params[:search].present?
|
||||
search_term = "%#{params[:search]}%"
|
||||
@feed = @feed.joins(:user).where(
|
||||
"content_page_shares.message ILIKE ? OR users.name ILIKE ? OR users.email ILIKE ?",
|
||||
search_term, search_term, search_term
|
||||
)
|
||||
end
|
||||
|
||||
@feed = @feed.limit(25)
|
||||
end
|
||||
|
||||
def community
|
||||
@ -35,7 +45,17 @@ class StreamController < ApplicationController
|
||||
.order('created_at DESC')
|
||||
.includes([:content_page, :secondary_content_page])
|
||||
.includes({ share_comments: [:user], user: [:avatar_attachment] })
|
||||
.limit(25)
|
||||
|
||||
# Apply search filter if present
|
||||
if params[:search].present?
|
||||
search_term = "%#{params[:search]}%"
|
||||
@feed = @feed.joins(:user).where(
|
||||
"content_page_shares.message ILIKE ? OR users.name ILIKE ? OR users.email ILIKE ?",
|
||||
search_term, search_term, search_term
|
||||
)
|
||||
end
|
||||
|
||||
@feed = @feed.limit(25)
|
||||
end
|
||||
|
||||
def set_stream_navbar_color
|
||||
|
||||
117
app/services/README_stream_events.md
Normal file
117
app/services/README_stream_events.md
Normal file
@ -0,0 +1,117 @@
|
||||
# Stream Events Integration
|
||||
|
||||
This document explains how to easily create stream events from anywhere in the application using the `StreamEventService`.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### 1. Manual Page Sharing
|
||||
|
||||
```ruby
|
||||
# When a user manually shares a page
|
||||
StreamEventService.create_share_event(
|
||||
user: current_user,
|
||||
content_page: @character,
|
||||
message: "Check out my new character!"
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Collection Publishing
|
||||
|
||||
```ruby
|
||||
# When a page gets published to a collection
|
||||
StreamEventService.create_collection_published_event(
|
||||
user: @page.user,
|
||||
content_page: @page,
|
||||
collection: @collection
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Document Publishing
|
||||
|
||||
```ruby
|
||||
# When a user publishes a document
|
||||
StreamEventService.create_document_published_event(
|
||||
user: current_user,
|
||||
document: @document
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Generic Activity Events
|
||||
|
||||
```ruby
|
||||
# Generic method for various activity types
|
||||
StreamEventService.create_activity_event(
|
||||
user: current_user,
|
||||
activity_type: :published_to_collection,
|
||||
target: {
|
||||
content_page: @page,
|
||||
collection: @collection
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## Integration Points
|
||||
|
||||
### In Controllers
|
||||
|
||||
Add stream events to existing controllers:
|
||||
|
||||
```ruby
|
||||
# In page_collections_controller.rb
|
||||
def add_page_to_collection
|
||||
# ... existing logic ...
|
||||
|
||||
if @submission.approved?
|
||||
StreamEventService.create_collection_published_event(
|
||||
user: @submission.content_page.user,
|
||||
content_page: @submission.content_page,
|
||||
collection: @collection
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### In Jobs/Background Tasks
|
||||
|
||||
```ruby
|
||||
class PublishToCollectionJob < ApplicationJob
|
||||
def perform(page_id, collection_id)
|
||||
# ... existing logic ...
|
||||
|
||||
StreamEventService.create_activity_event(
|
||||
user: page.user,
|
||||
activity_type: :published_to_collection,
|
||||
target: { content_page: page, collection: collection }
|
||||
)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### In Models (after_save callbacks)
|
||||
|
||||
```ruby
|
||||
class Document < ApplicationRecord
|
||||
after_update :create_stream_event_if_published
|
||||
|
||||
private
|
||||
|
||||
def create_stream_event_if_published
|
||||
if saved_change_to_privacy? && privacy == 'public'
|
||||
StreamEventService.create_document_published_event(
|
||||
user: self.user,
|
||||
document: self
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Extending the System
|
||||
|
||||
To add new event types:
|
||||
|
||||
1. Add a new method to `StreamEventService`
|
||||
2. Add a case to `create_activity_event` method
|
||||
3. Optionally create new partial templates in `app/views/stream/` for custom event rendering
|
||||
|
||||
The system is designed to be lightweight and extensible while maintaining consistency with the existing notification system.
|
||||
66
app/services/stream_event_service.rb
Normal file
66
app/services/stream_event_service.rb
Normal file
@ -0,0 +1,66 @@
|
||||
class StreamEventService
|
||||
def self.create_share_event(user:, content_page:, message: nil)
|
||||
return unless user && content_page
|
||||
|
||||
# Make the content page public when sharing
|
||||
content_page.update(privacy: 'public') if content_page.respond_to?(:privacy)
|
||||
|
||||
ContentPageShare.create!(
|
||||
user: user,
|
||||
content_page: content_page,
|
||||
message: message,
|
||||
shared_at: DateTime.current
|
||||
)
|
||||
end
|
||||
|
||||
def self.create_collection_published_event(user:, content_page:, collection:)
|
||||
return unless user && content_page && collection
|
||||
|
||||
message = "#{content_page.name} was featured in the collection #{collection.name}!"
|
||||
|
||||
create_share_event(
|
||||
user: user,
|
||||
content_page: content_page,
|
||||
message: message
|
||||
)
|
||||
end
|
||||
|
||||
def self.create_forum_thread_event(user:, thread_title:, thread_url:)
|
||||
# For forum threads, we'll need to create a different type of stream event
|
||||
# This would require extending the ContentPageShare model or creating a new model
|
||||
# For now, this is a placeholder for future implementation
|
||||
Rails.logger.info "StreamEventService: Would create forum thread event for #{user.display_name}: #{thread_title}"
|
||||
end
|
||||
|
||||
def self.create_document_published_event(user:, document:)
|
||||
return unless user && document
|
||||
|
||||
create_share_event(
|
||||
user: user,
|
||||
content_page: document,
|
||||
message: "Just published this document!"
|
||||
)
|
||||
end
|
||||
|
||||
# Helper method to create notification-style stream events
|
||||
def self.create_activity_event(user:, activity_type:, target:, message: nil)
|
||||
case activity_type
|
||||
when :published_to_collection
|
||||
create_collection_published_event(
|
||||
user: user,
|
||||
content_page: target[:content_page],
|
||||
collection: target[:collection]
|
||||
)
|
||||
when :shared_document
|
||||
create_document_published_event(user: user, document: target)
|
||||
when :forum_thread
|
||||
create_forum_thread_event(
|
||||
user: user,
|
||||
thread_title: target[:title],
|
||||
thread_url: target[:url]
|
||||
)
|
||||
else
|
||||
Rails.logger.warn "StreamEventService: Unknown activity type: #{activity_type}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,150 +1,198 @@
|
||||
<div class="stream-item row">
|
||||
<div class="col s12 m12 l4">
|
||||
<div class="grey-text uppercase">
|
||||
<br />
|
||||
Shared page
|
||||
</div>
|
||||
<% if @share.content_page %>
|
||||
<div class="hoverable card">
|
||||
<div class="card-image">
|
||||
<%= link_to @share.content_page do %>
|
||||
<%= image_tag @share.content_page.first_public_image %>
|
||||
<span class="card-title bordered-text">
|
||||
<i class="material-icons <%= @share.content_page.class.text_color %> left small"><%= @share.content_page.class.icon %></i>
|
||||
<%= @share.content_page.name %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="hoverable card">
|
||||
The shared page has been removed.
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="grey-text uppercase">
|
||||
<br />
|
||||
Created by
|
||||
</div>
|
||||
<%= link_to @share.user do %>
|
||||
<div class="hoverable card <%= User.color %>">
|
||||
<!-- todo vertical card stack user avatar -->
|
||||
<div class="card-content">
|
||||
<div class="card-title white-text">
|
||||
<i class="material-icons left"><%= User.icon %></i>
|
||||
<%= @share.user.display_name %>
|
||||
<div class="min-h-screen bg-gray-100 py-8">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="lg:grid lg:grid-cols-12 lg:gap-8">
|
||||
<!-- Sidebar -->
|
||||
<aside class="lg:col-span-4">
|
||||
<!-- Shared Content Card -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 mb-6">
|
||||
<div class="p-4 border-b border-gray-200">
|
||||
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide">Shared Page</h3>
|
||||
</div>
|
||||
<%= render partial: 'thredded/users/badge', locals: { user: @share.user } %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<br />
|
||||
<div>
|
||||
<ul>
|
||||
<li class="uppercase grey-text">
|
||||
Share Actions
|
||||
</li>
|
||||
<li>
|
||||
<% if @share.followed_by?(current_user) %>
|
||||
<%= link_to 'Unfollow this share', unfollow_user_content_page_share_path(user_id: @share.user.id, id: @share.id) %>
|
||||
<% if @share.content_page %>
|
||||
<div class="relative">
|
||||
<%= link_to @share.content_page, class: "block hover:shadow-lg transition-shadow" do %>
|
||||
<% if @share.content_page.respond_to?(:first_public_image) && @share.content_page.first_public_image.present? %>
|
||||
<div class="aspect-w-16 aspect-h-9">
|
||||
<%= image_tag @share.content_page.first_public_image, class: "w-full h-48 object-cover rounded-t-lg" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="p-4" style="border-left: 4px solid <%= @share.content_page.class.hex_color %>">
|
||||
<div class="flex items-center">
|
||||
<i class="material-icons text-sm mr-2" style="color: <%= @share.content_page.class.hex_color %>"><%= @share.content_page.class.icon %></i>
|
||||
<h4 class="text-lg font-semibold text-gray-900"><%= @share.content_page.name %></h4>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= link_to 'Follow this share', follow_user_content_page_share_path(user_id: @share.user.id, id: @share.id) %>
|
||||
<% end %>
|
||||
</li>
|
||||
<% if @share.user == current_user %>
|
||||
<li>
|
||||
<%=
|
||||
link_to user_content_page_share_path(user_id: @share.user.id, id: @share.id),
|
||||
method: :DELETE,
|
||||
data: { confirm: "Are you sure? This will delete this share and any comments that have been posted to it!" } do
|
||||
%>
|
||||
Delete this share
|
||||
<% end %>
|
||||
</li>
|
||||
<% else %>
|
||||
<li>
|
||||
<%= link_to report_user_content_page_share_path(user_id: @share.user.id, id: @share.id) do %>
|
||||
Report this share
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
<li>
|
||||
<%= link_to 'Back to your stream', main_app.stream_path %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li class="grey-text uppercase">
|
||||
Reshare to...
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to [
|
||||
'http://twitter.com/share?',
|
||||
'url=' + CGI.escape(user_content_page_share_url(user_id: @share.user.id, id: @share.id)),
|
||||
'&text=' + CGI.escape(@share.message)
|
||||
].join, class: 'blue-text', target: '_blank' do %>
|
||||
<i class="material-icons tiny left" style="margin-top: 4px;">share</i>
|
||||
Twitter
|
||||
<% end %>
|
||||
</li>
|
||||
<li>
|
||||
<%=
|
||||
link_to "https://www.facebook.com/sharer/sharer.php?app_id=1523926344336934&u=#{CGI.escape(user_content_page_share_url(user_id: @share.user.id, id: @share.id))}&display=popup&ref=plugin&src=share_button",
|
||||
class: 'blue-text',
|
||||
onclick: "return !window.open(this.href, 'Facebook', 'width=640,height=580')" do
|
||||
%>
|
||||
<i class="material-icons tiny left" style="margin-top: 4px">share</i>
|
||||
Facebook
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12 m12 l7">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p>
|
||||
<%= link_to @share.user, class: "#{User.text_color}" do %>
|
||||
<%= image_tag @share.user.image_url(size=20), class: 'left circle avatar' %>
|
||||
<%= @share.user.name %>
|
||||
<% end %>
|
||||
said:
|
||||
<span class="grey-text right">
|
||||
<%= time_ago_in_words @share.shared_at %> ago
|
||||
</span>
|
||||
</p>
|
||||
<% if @share.message.present? %>
|
||||
<blockquote class="original-comment black-text">
|
||||
<%=
|
||||
simple_format ContentFormatterService.show(
|
||||
text: @share.message,
|
||||
viewing_user: current_user
|
||||
)
|
||||
%>
|
||||
</blockquote>
|
||||
<% end %>
|
||||
|
||||
<%= render partial: 'share_comments/form', locals: { share: @share } %>
|
||||
<br /><br />
|
||||
|
||||
<div class="uppercase grey-text center">
|
||||
<%= pluralize @share.share_comments.count, 'comment' %>
|
||||
</div>
|
||||
<div class="row">
|
||||
<% @share.share_comments.each do |comment| %>
|
||||
<%=
|
||||
render partial: 'share_comments/show',
|
||||
locals: { comment: comment, share: @share }
|
||||
%>
|
||||
<div class="p-4 text-center text-gray-500">
|
||||
<i class="material-icons text-4xl text-gray-300 mb-2">help_outline</i>
|
||||
<p>The shared page has been removed.</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if @share.share_comments.count > 10 %>
|
||||
<%= render partial: 'share_comments/form', locals: { share: @share } %>
|
||||
<% end %>
|
||||
<br /><br />
|
||||
</div>
|
||||
<!-- User Card -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 mb-6">
|
||||
<div class="p-4 border-b border-gray-200">
|
||||
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide">Shared By</h3>
|
||||
</div>
|
||||
<%= link_to @share.user, class: "block p-4 hover:bg-gray-50 transition-colors" do %>
|
||||
<div class="flex items-center">
|
||||
<%= image_tag @share.user.image_url(size: 48), class: "h-12 w-12 rounded-full mr-4" %>
|
||||
<div>
|
||||
<h4 class="text-lg font-semibold text-gray-900"><%= @share.user.display_name %></h4>
|
||||
<%= render partial: 'thredded/users/badge', locals: { user: @share.user } %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 mb-6">
|
||||
<div class="p-4 border-b border-gray-200">
|
||||
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide">Actions</h3>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<% if @share.followed_by?(current_user) %>
|
||||
<%= link_to unfollow_user_content_page_share_path(user_id: @share.user.id, id: @share.id),
|
||||
class: "block w-full text-left px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-md" do %>
|
||||
<i class="material-icons text-sm mr-2">notifications_off</i>
|
||||
Unfollow this share
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to follow_user_content_page_share_path(user_id: @share.user.id, id: @share.id),
|
||||
class: "block w-full text-left px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-md" do %>
|
||||
<i class="material-icons text-sm mr-2">notifications</i>
|
||||
Follow this share
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @share.user == current_user %>
|
||||
<%= link_to user_content_page_share_path(user_id: @share.user.id, id: @share.id),
|
||||
method: :DELETE,
|
||||
data: { confirm: "Are you sure? This will delete this share and any comments that have been posted to it!" },
|
||||
class: "block w-full text-left px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-md" do %>
|
||||
<i class="material-icons text-sm mr-2">delete</i>
|
||||
Delete this share
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to report_user_content_page_share_path(user_id: @share.user.id, id: @share.id),
|
||||
class: "block w-full text-left px-3 py-2 text-sm text-red-600 hover:bg-red-50 rounded-md" do %>
|
||||
<i class="material-icons text-sm mr-2">report</i>
|
||||
Report this share
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to main_app.stream_path,
|
||||
class: "block w-full text-left px-3 py-2 text-sm text-gray-700 hover:bg-gray-100 rounded-md" do %>
|
||||
<i class="material-icons text-sm mr-2">arrow_back</i>
|
||||
Back to stream
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Share to Social -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div class="p-4 border-b border-gray-200">
|
||||
<h3 class="text-sm font-medium text-gray-500 uppercase tracking-wide">Share</h3>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<%= link_to [
|
||||
'http://twitter.com/share?',
|
||||
'url=' + CGI.escape(user_content_page_share_url(user_id: @share.user.id, id: @share.id)),
|
||||
'&text=' + CGI.escape(@share.message.to_s)
|
||||
].join, target: '_blank',
|
||||
class: "flex items-center px-3 py-2 text-sm text-blue-600 hover:bg-blue-50 rounded-md" do %>
|
||||
<i class="material-icons text-sm mr-2">share</i>
|
||||
Twitter
|
||||
<% end %>
|
||||
|
||||
<%= link_to "https://www.facebook.com/sharer/sharer.php?app_id=1523926344336934&u=#{CGI.escape(user_content_page_share_url(user_id: @share.user.id, id: @share.id))}&display=popup&ref=plugin&src=share_button",
|
||||
onclick: "return !window.open(this.href, 'Facebook', 'width=640,height=580')",
|
||||
class: "flex items-center px-3 py-2 text-sm text-blue-600 hover:bg-blue-50 rounded-md" do %>
|
||||
<i class="material-icons text-sm mr-2">share</i>
|
||||
Facebook
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="lg:col-span-8">
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div class="p-6 border-b border-gray-200">
|
||||
<div class="flex items-start space-x-3">
|
||||
<%= image_tag @share.user.image_url(size: 40), class: "h-10 w-10 rounded-full" %>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<%= link_to @share.user, class: "font-medium text-gray-900 hover:text-gray-700" do %>
|
||||
<%= @share.user.display_name %>
|
||||
<% end %>
|
||||
<span class="text-gray-500">shared this</span>
|
||||
<span class="text-gray-400">•</span>
|
||||
<span class="text-sm text-gray-500"><%= time_ago_in_words @share.shared_at %> ago</span>
|
||||
</div>
|
||||
<% if @share.message.present? %>
|
||||
<div class="mt-3 text-gray-700">
|
||||
<%= simple_format ContentFormatterService.show(text: @share.message, viewing_user: current_user) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Comments Section -->
|
||||
<div class="p-6">
|
||||
<!-- Comment Form -->
|
||||
<div class="mb-6">
|
||||
<%= form_for ShareComment.new, local: true, class: "flex space-x-3" do |f| %>
|
||||
<%= f.hidden_field :content_page_share_id, value: @share.id %>
|
||||
<div class="flex-shrink-0">
|
||||
<%= image_tag current_user.image_url(size: 40), class: "h-10 w-10 rounded-full" %>
|
||||
</div>
|
||||
<div class="flex-1 space-y-3">
|
||||
<%= f.text_area :message,
|
||||
placeholder: "Add a comment...",
|
||||
rows: 3,
|
||||
class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" %>
|
||||
<div class="flex justify-end">
|
||||
<%= f.submit "Post Comment", class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Comments List -->
|
||||
<div class="border-t border-gray-200 pt-6">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-4">
|
||||
<%= pluralize @share.share_comments.count, 'comment' %>
|
||||
</h3>
|
||||
|
||||
<% if @share.share_comments.any? %>
|
||||
<div class="space-y-6">
|
||||
<% @share.share_comments.each do |comment| %>
|
||||
<div class="flex space-x-3">
|
||||
<%= image_tag comment.user.image_url(size: 32), class: "h-8 w-8 rounded-full flex-shrink-0" %>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="font-medium text-gray-900"><%= comment.user.display_name %></span>
|
||||
<span class="text-sm text-gray-500"><%= time_ago_in_words comment.created_at %> ago</span>
|
||||
</div>
|
||||
<div class="mt-1 text-gray-700">
|
||||
<%= simple_format ContentFormatterService.show(text: comment.message, viewing_user: current_user) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<p class="text-gray-500 text-center py-8">No comments yet. Be the first to comment!</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
172
app/views/stream/_feed_item.html.erb
Normal file
172
app/views/stream/_feed_item.html.erb
Normal file
@ -0,0 +1,172 @@
|
||||
<div class="group relative">
|
||||
<!-- Main card with clean material design -->
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 hover:shadow-md transition-shadow duration-200">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="relative">
|
||||
<%= image_tag share.user.image_url(size: 48), class: "h-12 w-12 rounded-full shadow-sm" %>
|
||||
<% if share.content_page %>
|
||||
<div class="absolute -bottom-1 -right-1 h-6 w-6 rounded-full shadow-sm flex items-center justify-center" style="background-color: <%= share.content_page.class.hex_color %>;">
|
||||
<i class="material-icons text-white text-sm"><%= share.content_page.class.icon %></i>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center space-x-2">
|
||||
<%= link_to share.user, class: "font-semibold text-gray-900 hover:text-indigo-600 transition-colors" do %>
|
||||
<%= share.user.display_name %>
|
||||
<% end %>
|
||||
<% if share.content_page %>
|
||||
<span class="text-gray-500">shared a</span>
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium" style="background-color: <%= share.content_page.class.hex_color %>20; color: <%= share.content_page.class.hex_color %>;">
|
||||
<%= share.content_page.class.name.downcase %>
|
||||
</span>
|
||||
<% else %>
|
||||
<span class="text-red-500">shared a deleted page</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 mt-0.5">
|
||||
<%= time_ago_in_words share.created_at %> ago
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action menu -->
|
||||
<div class="opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<button class="p-1.5 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-50 transition-colors">
|
||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Message -->
|
||||
<% if share.message.present? %>
|
||||
<div class="mb-4">
|
||||
<div class="prose prose-sm max-w-none text-gray-700 leading-relaxed">
|
||||
<%= simple_format ContentFormatterService.show(text: share.message, viewing_user: current_user) %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Content Preview -->
|
||||
<% if share.content_page %>
|
||||
<%= link_to [share.user, share], class: 'block group/content' do %>
|
||||
<div class="relative overflow-hidden rounded-lg border border-gray-200 bg-gray-50 hover:shadow-sm hover:border-gray-300 transition-all duration-200">
|
||||
<div class="flex">
|
||||
<% if share.content_page.respond_to?(:first_public_image) && share.content_page.first_public_image.present? %>
|
||||
<div class="flex-shrink-0 w-32 h-24">
|
||||
<%= image_tag share.content_page.first_public_image, class: 'w-full h-full object-cover' %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="flex-1 p-4" style="border-left: 3px solid <%= share.content_page.class.hex_color %>;">
|
||||
<h3 class="font-semibold text-gray-900 group-hover/content:text-indigo-600 transition-colors flex items-center">
|
||||
<i class="material-icons text-lg mr-2" style="color: <%= share.content_page.class.hex_color %>"><%= share.content_page.class.icon %></i>
|
||||
<%= share.content_page.name %>
|
||||
</h3>
|
||||
<% if share.content_page.respond_to?(:description) && share.content_page.description.present? %>
|
||||
<p class="text-sm text-gray-600 mt-2 line-clamp-2">
|
||||
<%= truncate(strip_tags(share.content_page.description), length: 120) %>
|
||||
</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<!-- Interaction Bar -->
|
||||
<div class="flex items-center justify-between mt-4 pt-4 border-t border-gray-200/50">
|
||||
<div class="flex items-center space-x-6">
|
||||
<!-- Comments -->
|
||||
<button class="flex items-center space-x-2 text-gray-500 hover:text-indigo-600 transition-colors group/btn">
|
||||
<div class="p-1.5 rounded-lg group-hover/btn:bg-indigo-50 transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="text-sm font-medium">
|
||||
<% if share.share_comments.any? %>
|
||||
<%= pluralize share.share_comments.count, 'comment' %>
|
||||
<% else %>
|
||||
Comment
|
||||
<% end %>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Like (placeholder) -->
|
||||
<button class="flex items-center space-x-2 text-gray-500 hover:text-red-600 transition-colors group/btn">
|
||||
<div class="p-1.5 rounded-lg group-hover/btn:bg-red-50 transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="text-sm font-medium">Like</span>
|
||||
</button>
|
||||
|
||||
<!-- Share (placeholder) -->
|
||||
<button class="flex items-center space-x-2 text-gray-500 hover:text-blue-600 transition-colors group/btn">
|
||||
<div class="p-1.5 rounded-lg group-hover/btn:bg-blue-50 transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="text-sm font-medium">Share</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Comments Section (if any) -->
|
||||
<% if share.share_comments.any? %>
|
||||
<div class="mt-4 pt-4 border-t border-gray-200/50">
|
||||
<div class="space-y-4">
|
||||
<% share.share_comments.limit(2).each do |comment| %>
|
||||
<div class="flex space-x-3">
|
||||
<%= image_tag comment.user.image_url(size: 32), class: "h-8 w-8 rounded-full flex-shrink-0" %>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="bg-gray-50 rounded-lg px-3 py-2">
|
||||
<div class="flex items-center space-x-2 mb-1">
|
||||
<span class="font-medium text-sm text-gray-900"><%= comment.user.display_name %></span>
|
||||
<span class="text-xs text-gray-500"><%= time_ago_in_words comment.created_at %> ago</span>
|
||||
</div>
|
||||
<div class="text-sm text-gray-700">
|
||||
<%= simple_format ContentFormatterService.show(text: comment.message, viewing_user: current_user) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if share.share_comments.count > 2 %>
|
||||
<div class="text-center">
|
||||
<%= link_to [share.user, share], class: "text-sm text-indigo-600 hover:text-indigo-500 font-medium" do %>
|
||||
View all <%= share.share_comments.count %> comments
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Quick comment form -->
|
||||
<div class="mt-4 pt-4 border-t border-gray-200/50">
|
||||
<%= form_for ShareComment.new, local: true, class: "flex space-x-3" do |f| %>
|
||||
<%= f.hidden_field :content_page_share_id, value: share.id %>
|
||||
<div class="flex-shrink-0">
|
||||
<%= image_tag current_user.image_url(size: 32), class: "h-8 w-8 rounded-full" %>
|
||||
</div>
|
||||
<div class="flex-1 flex space-x-3">
|
||||
<%= f.text_area :message,
|
||||
placeholder: "Add a thoughtful comment...",
|
||||
rows: 1,
|
||||
class: "flex-1 px-3 py-2 bg-gray-50 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 focus:bg-white transition-colors resize-none" %>
|
||||
<%= f.submit "Post", class: "px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,28 +1,196 @@
|
||||
<br />
|
||||
<div class="min-h-screen bg-gray-50" data-in-app="true">
|
||||
<!-- Clean Header -->
|
||||
<header class="sticky top-0 z-30 bg-white border-b border-gray-200 shadow-sm">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between h-16">
|
||||
<!-- Left: Navigation Toggle -->
|
||||
<div class="flex items-center space-x-1">
|
||||
<%= link_to stream_path, class: "inline-flex items-center px-4 py-2 text-sm font-medium rounded-md transition-colors #{'bg-blue-50 text-blue-700 border border-blue-200' if request.path == stream_path} #{'text-gray-600 hover:text-gray-900 hover:bg-gray-50' unless request.path == stream_path}" do %>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
|
||||
</svg>
|
||||
Following
|
||||
<% end %>
|
||||
<%= link_to stream_world_path, class: "inline-flex items-center px-4 py-2 text-sm font-medium rounded-md transition-colors #{'bg-blue-50 text-blue-700 border border-blue-200' if request.path == stream_world_path} #{'text-gray-600 hover:text-gray-900 hover:bg-gray-50' unless request.path == stream_world_path}" do %>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Everyone
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<%= render partial: 'content_page_shares/form' %>
|
||||
<!-- Center: Search -->
|
||||
<div class="flex-1 max-w-md mx-8">
|
||||
<%= form_with url: request.path, method: :get, local: true do |f| %>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg class="h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<%= f.text_field :search,
|
||||
value: params[:search],
|
||||
placeholder: "Search posts...",
|
||||
class: "block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md text-sm placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Right: Empty for balance -->
|
||||
<div class="w-24"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<div class="py-6 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
|
||||
<!-- Collapsible Share CTA -->
|
||||
<div class="mb-6">
|
||||
<div id="share-cta-collapsed" class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 cursor-pointer hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-3">
|
||||
<%= image_tag current_user.image_url(size: 40), class: "h-10 w-10 rounded-full" %>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900">Want to share a page with the world?</p>
|
||||
<p class="text-xs text-gray-500">Share your work and get feedback from the community</p>
|
||||
</div>
|
||||
</div>
|
||||
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="share-form-expanded" class="hidden bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
||||
<%= form_for [current_user, ContentPageShare.new(user: current_user)], local: true, class: "tailwind-styled-form" do |f| %>
|
||||
<div class="p-6 border-b border-gray-100">
|
||||
<div class="flex items-start space-x-4">
|
||||
<%= image_tag current_user.image_url(size: 48), class: "h-12 w-12 rounded-full" %>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-1">Share a page</h3>
|
||||
<p class="text-sm text-gray-600">Choose one of your pages to share with the community</p>
|
||||
</div>
|
||||
<button type="button" id="close-share-form" class="text-gray-400 hover:text-gray-600">
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<%= f.select :content_page,
|
||||
options_for_select([['Select a page to share...', '']] +
|
||||
@current_user_content.flat_map do |content_type, content_list|
|
||||
content_list.sort_by(&:name).map { |content| ["#{content.name} (#{content_type})", "#{content_type}-#{content.id}"] }
|
||||
end),
|
||||
{},
|
||||
{ class: "block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500" } %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= f.text_area :message,
|
||||
placeholder: "Add a message about your page (optional)",
|
||||
rows: 3,
|
||||
class: "block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 resize-none" %>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between pt-2">
|
||||
<div class="flex items-center text-sm text-gray-500">
|
||||
<svg class="w-4 h-4 mr-1.5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Page will be made public
|
||||
</div>
|
||||
<%= f.submit "Share", class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Feed Header -->
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="h-8 w-1 bg-blue-600 rounded-full"></div>
|
||||
<h2 class="text-xl font-bold text-gray-900">
|
||||
<%= request.path == stream_path ? "Following" : "Everyone" %>
|
||||
</h2>
|
||||
<span class="px-3 py-1 text-xs font-medium bg-gray-100 text-gray-600 rounded-full">
|
||||
<%= @feed.length %> posts
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Feed Content -->
|
||||
<% if @feed.empty? %>
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-12 text-center">
|
||||
<div class="mb-6">
|
||||
<div class="mx-auto h-16 w-16 rounded-full bg-gray-100 flex items-center justify-center">
|
||||
<svg class="h-8 w-8 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">No posts here yet!</h3>
|
||||
<p class="text-gray-600 mb-6 max-w-md mx-auto leading-relaxed">
|
||||
<%= request.path == stream_path ?
|
||||
"Follow other worldbuilders to see their amazing creations appear here." :
|
||||
"Be the first to share something amazing with the community!" %>
|
||||
</p>
|
||||
<%= link_to "Discover Creators", users_path, class: "inline-flex items-center px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors" %>
|
||||
</div>
|
||||
<% else %>
|
||||
<div class="space-y-6">
|
||||
<% @feed.each_with_index do |share, index| %>
|
||||
<%= render partial: 'stream/feed_item', locals: { share: share, is_last: index == @feed.length - 1 } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Load More -->
|
||||
<div class="mt-12 text-center">
|
||||
<button class="inline-flex items-center px-6 py-3 bg-white border border-gray-200 text-gray-700 font-medium rounded-lg shadow-sm hover:shadow-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
Load more posts
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'stream/share', collection: @feed %>
|
||||
|
||||
<% if @feed.empty? %>
|
||||
<div class="row">
|
||||
<div class='col s12'>
|
||||
<div class="hoverable card">
|
||||
<div class="card-content">
|
||||
<p class="card-title">
|
||||
There are no posts here yet!
|
||||
</p>
|
||||
<p>
|
||||
You can follow other worldbuilders from their profiles.
|
||||
Whenever a user you follow shares one of their public pages,
|
||||
it will appear here for you to comment on!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<script>
|
||||
// Disable MaterializeCSS initialization for Tailwind-styled forms
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Prevent MaterializeCSS from initializing on Tailwind select elements
|
||||
if (window.M && window.M.FormSelect) {
|
||||
document.querySelectorAll('.tailwind-styled-form select').forEach(function(select) {
|
||||
select.classList.add('no-autoinit');
|
||||
});
|
||||
}
|
||||
|
||||
// Handle collapsible share form
|
||||
const shareCtaCollapsed = document.getElementById('share-cta-collapsed');
|
||||
const shareFormExpanded = document.getElementById('share-form-expanded');
|
||||
const closeShareForm = document.getElementById('close-share-form');
|
||||
|
||||
if (shareCtaCollapsed) {
|
||||
shareCtaCollapsed.addEventListener('click', function() {
|
||||
shareCtaCollapsed.classList.add('hidden');
|
||||
shareFormExpanded.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
if (closeShareForm) {
|
||||
closeShareForm.addEventListener('click', function() {
|
||||
shareFormExpanded.classList.add('hidden');
|
||||
shareCtaCollapsed.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@ -1,336 +1,202 @@
|
||||
<div class="min-h-screen bg-gray-100">
|
||||
<header class="bg-white shadow-sm lg:static lg:overflow-y-visible">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="relative flex justify-between xl:grid xl:grid-cols-12 lg:gap-8">
|
||||
<div class="flex md:absolute md:left-0 md:inset-y-0 lg:static xl:col-span-2">
|
||||
<div class="flex-shrink-0 flex items-center">
|
||||
<span class="relative z-0 inline-flex shadow-sm rounded-md">
|
||||
<button type="button" class="relative inline-flex items-center px-4 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-notebook-blue focus:border-notebook-blue">Following</button>
|
||||
<button type="button" class="-ml-px relative inline-flex items-center px-4 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-notebook-blue focus:border-notebook-blue">Everyone</button>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<div class="min-h-screen bg-gray-50" data-in-app="true">
|
||||
<!-- Clean Header -->
|
||||
<header class="sticky top-0 z-30 bg-white border-b border-gray-200 shadow-sm">
|
||||
<div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between h-16">
|
||||
<!-- Left: Navigation -->
|
||||
<div class="flex items-center space-x-1">
|
||||
<%= link_to stream_path, class: "inline-flex items-center px-4 py-2 text-sm font-medium rounded-md transition-colors #{'bg-blue-50 text-blue-700 border border-blue-200' if request.path == stream_path} #{'text-gray-600 hover:text-gray-900 hover:bg-gray-50' unless request.path == stream_path}" do %>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
|
||||
</svg>
|
||||
Following
|
||||
<% end %>
|
||||
<%= link_to stream_world_path, class: "inline-flex items-center px-4 py-2 text-sm font-medium rounded-md transition-colors #{'bg-blue-50 text-blue-700 border border-blue-200' if request.path == stream_world_path} #{'text-gray-600 hover:text-gray-900 hover:bg-gray-50' unless request.path == stream_world_path}" do %>
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Everyone
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1 md:px-8 lg:px-0 xl:col-span-6">
|
||||
<div class="flex items-center px-6 py-4 md:max-w-3xl md:mx-auto lg:max-w-none lg:mx-0 xl:px-0">
|
||||
<div class="w-full">
|
||||
<label for="search" class="sr-only">Share something...</label>
|
||||
<div class="relative">
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 pl-3 flex items-center">
|
||||
<!-- Heroicon name: solid/search -->
|
||||
<svg class="h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<input id="search" name="search" class="block w-full bg-white border border-gray-300 rounded-md py-2 pl-10 pr-3 text-sm placeholder-gray-500 focus:outline-none focus:text-gray-900 focus:placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm" placeholder="Search shared posts..." type="search">
|
||||
|
||||
<!-- Center: Search -->
|
||||
<div class="flex-1 max-w-md mx-8">
|
||||
<%= form_with url: request.path, method: :get, local: true do |f| %>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<svg class="h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<%= f.text_field :search,
|
||||
value: params[:search],
|
||||
placeholder: "Search posts...",
|
||||
class: "block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md text-sm placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex md:absolute md:left-0 md:inset-y-0 lg:static xl:col-span-2">
|
||||
<div class="flex-shrink-0 flex items-center">
|
||||
rhs
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Right: Empty for balance -->
|
||||
<div class="w-24"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="max-w-3xl mx-auto sm:px-6 lg:max-w-7xl lg:px-8 lg:grid lg:grid-cols-12 lg:gap-8">
|
||||
<!--
|
||||
<div class="hidden lg:block lg:col-span-3 xl:col-span-2">
|
||||
<nav aria-label="Sidebar" class="divide-y divide-gray-300">
|
||||
filters / highlights (each page, documents, timelines)
|
||||
</nav>
|
||||
<!-- Main Content Area -->
|
||||
<div class="py-6 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
|
||||
<!-- Collapsible Share CTA -->
|
||||
<div class="mb-6">
|
||||
<div id="share-cta-collapsed" class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 cursor-pointer hover:shadow-md transition-shadow">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-3">
|
||||
<%= image_tag current_user.image_url(size: 40), class: "h-10 w-10 rounded-full" %>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900">Want to share a page with the world?</p>
|
||||
<p class="text-xs text-gray-500">Share your work and get feedback from the community</p>
|
||||
</div>
|
||||
</div>
|
||||
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="share-form-expanded" class="hidden bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
|
||||
<%= form_for [current_user, ContentPageShare.new(user: current_user)], local: true, class: "tailwind-styled-form" do |f| %>
|
||||
<div class="p-6 border-b border-gray-100">
|
||||
<div class="flex items-start space-x-4">
|
||||
<%= image_tag current_user.image_url(size: 48), class: "h-12 w-12 rounded-full" %>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-1">Share a page</h3>
|
||||
<p class="text-sm text-gray-600">Choose one of your pages to share with the community</p>
|
||||
</div>
|
||||
<button type="button" id="close-share-form" class="text-gray-400 hover:text-gray-600">
|
||||
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<%= f.select :content_page,
|
||||
options_for_select([['Select a page to share...', '']] +
|
||||
@current_user_content.flat_map do |content_type, content_list|
|
||||
content_list.sort_by(&:name).map { |content| ["#{content.name} (#{content_type})", "#{content_type}-#{content.id}"] }
|
||||
end),
|
||||
{},
|
||||
{ class: "block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500" } %>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= f.text_area :message,
|
||||
placeholder: "Add a message about your page (optional)",
|
||||
rows: 3,
|
||||
class: "block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 resize-none" %>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between pt-2">
|
||||
<div class="flex items-center text-sm text-gray-500">
|
||||
<svg class="w-4 h-4 mr-1.5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Page will be made public
|
||||
</div>
|
||||
<%= f.submit "Share", class: "inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<main class="lg:col-span-12 xl:col-span-8">
|
||||
<div class="bg-notebook-blue text-white my-8 p-2 rounded">
|
||||
share input form TODO
|
||||
<!-- Feed Header -->
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="h-8 w-1 bg-blue-600 rounded-full"></div>
|
||||
<h2 class="text-xl font-bold text-gray-900">
|
||||
<%= request.path == stream_path ? "Following" : "Everyone" %>
|
||||
</h2>
|
||||
<span class="px-3 py-1 text-xs font-medium bg-gray-100 text-gray-600 rounded-full">
|
||||
<%= @feed.length %> posts
|
||||
</span>
|
||||
</div>
|
||||
<h2 class="text-gray-400 font-bold text-sm my-4">Shares from people you follow</h2>
|
||||
<div class="flow-root">
|
||||
<ul role="list" class="-mb-8">
|
||||
<li>
|
||||
<div class="relative pb-8">
|
||||
<span class="absolute top-5 left-5 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
|
||||
<div class="relative flex items-start space-x-3">
|
||||
<div class="relative">
|
||||
<img class="h-10 w-10 rounded-full bg-gray-400 flex items-center justify-center ring-8 ring-white" src="https://images.unsplash.com/photo-1520785643438-5bf77931f493?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80" alt="">
|
||||
|
||||
<span class="absolute -bottom-2 -right-1.5 bg-white rounded-tl px-0.5">
|
||||
<i class="material-icons <%= Creature.text_color %> text-base"><%= Creature.icon %></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div>
|
||||
<div class="text-sm text-gray-500">
|
||||
<a href="#" class="font-medium text-gray-900">Andrew Brown</a>
|
||||
<span class="mt-0.5">shared a <strong>Creature</strong></span>
|
||||
<span class="px-1">·</span>
|
||||
<span class="">6h ago</span>
|
||||
</div>
|
||||
<p>
|
||||
message with share
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-2 text-sm text-gray-700">
|
||||
<%= link_to '#', class: '' do %>
|
||||
<div class="grid grid-cols-3 bg-white rounded hover:shadow-lg">
|
||||
<div class="col-span-1">
|
||||
<%= image_tag Character.last.random_image_including_private, class: 'w-full h-full rounded-l' %>
|
||||
</div>
|
||||
<div class="col-span-2 pl-4 pt-4 border-l-4 border-brown-500">
|
||||
<h3 class="text-lg mb-4 font-bold">
|
||||
<i class="material-icons <%= Creature.text_color %> float-left mr-2"><%= Creature.icon %></i>
|
||||
Page name
|
||||
</h3>
|
||||
Details or description here
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="relative pb-8">
|
||||
<span class="absolute top-5 left-5 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
|
||||
<div class="relative flex items-start space-x-3">
|
||||
<div>
|
||||
<div class="relative px-1">
|
||||
<div class="relative">
|
||||
<img class="h-10 w-10 rounded-full bg-gray-400 flex items-center justify-center ring-8 ring-white" src="https://images.unsplash.com/photo-1520785643438-5bf77931f493?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80" alt="">
|
||||
|
||||
<span class="absolute -bottom-2 -right-1.5 bg-white rounded-tl px-0.5">
|
||||
<i class="material-icons <%= User.text_color %> text-base"><%= User.icon %></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1 py-1.5">
|
||||
<div class="text-sm text-gray-500">
|
||||
<a href="#" class="font-medium text-gray-900">Dr. Evil</a>
|
||||
followed
|
||||
<a href="#" class="font-medium text-gray-900">Bob "Bob" Bobson</a>
|
||||
<span class="px-1">·</span>
|
||||
<span class="text-sm text-gray-500">2d ago</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="relative pb-8">
|
||||
<span class="absolute top-5 left-5 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
|
||||
<div class="relative flex items-start space-x-3">
|
||||
<div>
|
||||
<div class="relative px-1">
|
||||
<div class="relative">
|
||||
<img class="h-10 w-10 rounded-full bg-gray-400 flex items-center justify-center ring-8 ring-white" src="https://images.unsplash.com/photo-1520785643438-5bf77931f493?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80" alt="">
|
||||
|
||||
<span class="absolute -bottom-2 -right-1.5 bg-white rounded-tl px-0.5">
|
||||
<i class="material-icons text-blue-500 text-base">forum</i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1 py-0">
|
||||
<div class="text-sm leading-8 text-gray-500">
|
||||
<span class="mr-0.5">
|
||||
<a href="#" class="font-medium text-gray-900">Dr. Evil</a>
|
||||
started a forum discussion
|
||||
</span>
|
||||
<span class="px-1">·</span>
|
||||
<span class="text-sm text-gray-500">2d ago</span>
|
||||
<div class="ml-1">
|
||||
<a href="#" class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-1 text-sm bg-blue-500 hover:bg-blue-400 hover:shadow-lg">
|
||||
<i class="material-icons float-left text-white">forum</i>
|
||||
<span class="ml-3.5 font-medium text-white">What is the name of that guy who did the thing?</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="relative pb-8">
|
||||
<div class="relative flex items-start space-x-3">
|
||||
<div class="relative">
|
||||
<img class="h-10 w-10 rounded-full bg-gray-400 flex items-center justify-center ring-8 ring-white" src="https://images.unsplash.com/photo-1531427186611-ecfd6d936c79?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80" alt="">
|
||||
|
||||
<span class="absolute -bottom-2 -right-1.5 bg-white rounded-tl px-0.5">
|
||||
<i class="material-icons <%= Document.text_color %> text-base"><%= Document.icon %></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="text-sm">
|
||||
<a href="#" class="font-medium text-gray-900">Jason Meyers</a>
|
||||
<span class="text-gray-500">shared a <strong>Document</strong></span>
|
||||
<span class="px-1">·</span>
|
||||
<span class="text-sm text-gray-500">5d ago</span>
|
||||
</div>
|
||||
<div class="mt-2 text-sm text-gray-700">
|
||||
<%= link_to '#', class: '' do %>
|
||||
<div class="grid grid-cols-3 bg-white rounded hover:shadow-lg">
|
||||
<div class="col-span-1">
|
||||
<%= image_tag Character.last.random_image_including_private, class: 'w-full h-full rounded-l' %>
|
||||
</div>
|
||||
<div class="col-span-2 pl-4 pt-4 border-l-4 border-teal-500">
|
||||
<h3 class="text-lg mb-4 font-bold">
|
||||
<i class="material-icons <%= Document.text_color %> float-left mr-2"><%= Document.icon %></i>
|
||||
Document name
|
||||
</h3>
|
||||
Details or description here
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Feed Content -->
|
||||
<% if @feed.empty? %>
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-12 text-center">
|
||||
<div class="mb-6">
|
||||
<div class="mx-auto h-16 w-16 rounded-full bg-gray-100 flex items-center justify-center">
|
||||
<svg class="h-8 w-8 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-900 mb-3">No posts here yet!</h3>
|
||||
<p class="text-gray-600 mb-6 max-w-md mx-auto leading-relaxed">
|
||||
<%= request.path == stream_path ?
|
||||
"Follow other worldbuilders to see their amazing creations appear here." :
|
||||
"Be the first to share something amazing with the community!" %>
|
||||
</p>
|
||||
<%= link_to "Discover Creators", users_path, class: "inline-flex items-center px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors" %>
|
||||
</div>
|
||||
</main>
|
||||
<aside class="hidden xl:block xl:col-span-4">
|
||||
<h2 class="text-gray-400 font-bold text-sm my-4">Pages requesting feedback</h2>
|
||||
<div>
|
||||
<ul role="list" class="divide-y divide-gray-200">
|
||||
<li class="pt-0 pb-2">
|
||||
<div class="flex space-x-3">
|
||||
<img class="h-12 w-12 rounded-lg" src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" alt="">
|
||||
<div class="flex-1 space-y-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-medium">
|
||||
<i class="material-icons <%= Character.text_color %> float-left text-sm mr-1"><%= Character.icon %></i>
|
||||
Lindsay Walton
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500">1h</p>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500">for <strong>general feedback</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="py-2">
|
||||
<div class="flex space-x-3">
|
||||
<img class="h-12 w-12 rounded-lg" src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" alt="">
|
||||
<div class="flex-1 space-y-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-medium">
|
||||
<i class="material-icons <%= Character.text_color %> float-left text-sm mr-1"><%= Character.icon %></i>
|
||||
Lindsay Walton
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500">1h</p>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500">for <strong>believability</strong>, <strong>science check</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="py-2">
|
||||
<div class="flex space-x-3">
|
||||
<img class="h-12 w-12 rounded-lg" src="https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" alt="">
|
||||
<div class="flex-1 space-y-1">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-medium">
|
||||
<i class="material-icons <%= Character.text_color %> float-left text-sm mr-1"><%= Character.icon %></i>
|
||||
Lindsay Walton
|
||||
</h3>
|
||||
<p class="text-sm text-gray-500">2h</p>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500">for <strong>sensitivity</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- More items... -->
|
||||
</ul>
|
||||
<% else %>
|
||||
<div class="space-y-6">
|
||||
<% @feed.each_with_index do |share, index| %>
|
||||
<%= render partial: 'stream/feed_item', locals: { share: share, is_last: index == @feed.length - 1 } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<h2 class="text-gray-400 font-bold text-sm mt-4 mb-2">Discussions happening now</h2>
|
||||
<div class="space-y-1">
|
||||
<div class="ml-1">
|
||||
<a href="#" class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-1 text-sm bg-blue-500 hover:bg-blue-400 hover:shadow-lg">
|
||||
<i class="material-icons float-left text-white">forum</i>
|
||||
<span class="ml-3.5 font-medium text-white">What is the name of that guy who did the thing?</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<a href="#" class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-1 text-sm bg-blue-500 hover:bg-blue-400 hover:shadow-lg">
|
||||
<i class="material-icons float-left text-white">forum</i>
|
||||
<span class="ml-3.5 font-medium text-white">What is your favorite crayon to write on rocks with?</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<a href="#" class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-1 text-sm bg-blue-500 hover:bg-blue-400 hover:shadow-lg">
|
||||
<i class="material-icons float-left text-white">forum</i>
|
||||
<span class="ml-3.5 font-medium text-white">How 2 write good</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="ml-1">
|
||||
<a href="#" class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-1 text-sm bg-blue-500 hover:bg-blue-400 hover:shadow-lg">
|
||||
<i class="material-icons float-left text-white">forum</i>
|
||||
<span class="ml-3.5 font-medium text-white">DAE hate the new design?</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Load More -->
|
||||
<div class="mt-12 text-center">
|
||||
<button class="inline-flex items-center px-6 py-3 bg-white border border-gray-200 text-gray-700 font-medium rounded-lg shadow-sm hover:shadow-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all duration-200">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
Load more posts
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h2 class="text-gray-400 font-bold text-sm mt-4 mb-2">Newest discussion</h2>
|
||||
<div class="space-y-1">
|
||||
<div class="ml-1">
|
||||
<a href="#" class="relative inline-flex items-center rounded-full border border-gray-300 px-3 py-1 text-sm bg-blue-500 hover:bg-blue-400 hover:shadow-lg">
|
||||
<i class="material-icons float-left text-white">forum</i>
|
||||
<span class="ml-3.5 font-medium text-white">how do i get in contact with cashapp support</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Disable MaterializeCSS initialization for Tailwind-styled forms
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Prevent MaterializeCSS from initializing on Tailwind select elements
|
||||
if (window.M && window.M.FormSelect) {
|
||||
document.querySelectorAll('.tailwind-styled-form select').forEach(function(select) {
|
||||
select.classList.add('no-autoinit');
|
||||
});
|
||||
}
|
||||
|
||||
// Handle collapsible share form
|
||||
const shareCtaCollapsed = document.getElementById('share-cta-collapsed');
|
||||
const shareFormExpanded = document.getElementById('share-form-expanded');
|
||||
const closeShareForm = document.getElementById('close-share-form');
|
||||
|
||||
if (shareCtaCollapsed) {
|
||||
shareCtaCollapsed.addEventListener('click', function() {
|
||||
shareCtaCollapsed.classList.add('hidden');
|
||||
shareFormExpanded.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
if (closeShareForm) {
|
||||
closeShareForm.addEventListener('click', function() {
|
||||
shareFormExpanded.classList.add('hidden');
|
||||
shareCtaCollapsed.classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<%= render partial: 'notice_dismissal/messages/12' %>
|
||||
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<%= render partial: 'content_page_shares/form' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render partial: 'stream/share', collection: @feed %>
|
||||
|
||||
<% if @feed.empty? %>
|
||||
<div class="row">
|
||||
<div class='col s12'>
|
||||
<div class="hoverable card">
|
||||
<div class="card-content">
|
||||
<p class="card-title">
|
||||
There are no posts here yet!
|
||||
</p>
|
||||
<p>
|
||||
You can follow other worldbuilders from their profiles.
|
||||
Whenever a user you follow shares one of their public pages,
|
||||
it will appear here for you to comment on!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render partial: 'javascripts/content_linking' %>
|
||||
Loading…
Reference in New Issue
Block a user