Skip to content

Commit 853b88c

Browse files
authored
Merge pull request #1435 from indentlabs/claude/review-table-of-contents-ooM1U
Finish Table of Contents feature for public universe browsing
2 parents 101eb11 + 5c5b0c7 commit 853b88c

4 files changed

Lines changed: 104 additions & 94 deletions

File tree

app/controllers/main_controller.rb

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,28 +53,38 @@ def dashboard
5353
end
5454

5555
def table_of_contents
56-
@toc_scope = @universe_scope || current_user
57-
@toc_user = @universe_scope.try(:user) || current_user
58-
59-
# Get content list - handle differently depending on whether we're scoped to a universe or a user
60-
content_list = if @toc_scope.is_a?(Universe)
61-
# For a Universe, we need to get content from the user that's in this universe
62-
user_content = @toc_user.content_list(page_scoping: { user_id: @toc_user.id })
63-
universe_id = @toc_scope.id
64-
user_content.select { |page| page['universe_id']&.to_i == universe_id || page['page_type'] == 'Universe' && page['id'] == universe_id }
65-
else
66-
# For a User, we can directly use their content_list method
67-
@toc_scope.content_list
68-
end
56+
@universe = Universe.find(params[:id])
6957

70-
# Sort the content list by name
71-
content_list = content_list.sort_by { |page| page['name'] }
58+
unless @universe.public_content?
59+
raise ActiveRecord::RecordNotFound
60+
end
7261

73-
@starred_pages = content_list.select { |page| page['favorite'] == 1 }
74-
@other_pages = content_list.select { |page| page['favorite'] == 0 }
62+
@toc_user = @universe.user
63+
@page_title = "#{@universe.name} — Table of Contents"
7564

65+
# Gather all public, non-deleted content in this universe
66+
all_pages = []
7667
@page_type_counts = Hash.new(0)
77-
content_list.each { |page| @page_type_counts[page['page_type']] += 1 }
68+
69+
Rails.application.config.content_types[:all_non_universe].each do |content_type|
70+
relation = content_type.name.downcase.pluralize.to_sym
71+
pages = @universe.send(relation)
72+
.is_public
73+
.where(deleted_at: nil, archived_at: nil)
74+
pages.each do |page|
75+
all_pages << page
76+
@page_type_counts[content_type.name] += 1
77+
end
78+
end
79+
80+
all_pages.sort_by!(&:name)
81+
@starred_pages = all_pages.select { |p| p.try(:favorite) }
82+
@other_pages = all_pages.reject { |p| p.try(:favorite) }
83+
84+
# Statistics
85+
@total_pages = all_pages.size
86+
@total_words = all_pages.sum { |p| p.try(:cached_word_count).to_i }
87+
@content_type_count = @page_type_counts.keys.size
7888
end
7989

8090
def infostack
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Controller } from "stimulus"
2+
3+
export default class extends Controller {
4+
static targets = ["page", "allFilter"]
5+
6+
filter(event) {
7+
const selectedType = event.currentTarget.dataset.pageType
8+
9+
this.pageTargets.forEach(el => {
10+
el.style.display = el.dataset.pageType === selectedType ? "" : "none"
11+
})
12+
}
13+
14+
showAll() {
15+
this.pageTargets.forEach(el => {
16+
el.style.display = ""
17+
})
18+
}
19+
}

app/views/main/table_of_contents.html.erb

Lines changed: 55 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,19 @@
44
<main class="mt-16 mx-auto max-w-7xl px-4 sm:mt-24">
55
<div class="text-center">
66
<h1 class="text-4xl tracking-tight font-extrabold text-gray-900 sm:text-5xl md:text-6xl">
7-
<span class="<%= Universe.text_color %>"><%= @toc_scope.name %></span>
7+
<span class="<%= Universe.text_color %>"><%= @universe.name %></span>
88
</h1>
99
<h2 class="text-gray-400 tracking-wider">
1010
a universe by
1111
<%= link_to @toc_user.display_name, @toc_user, class: User.text_color %>
1212
</h2>
13-
<p class="mt-3 max-w-md mx-auto text-base text-gray-500 sm:text-lg md:mt-5 md:text-xl md:max-w-3xl">
14-
scope description
15-
</p>
13+
<% if @universe.description.present? %>
14+
<p class="mt-3 max-w-md mx-auto text-base text-gray-500 sm:text-lg md:mt-5 md:text-xl md:max-w-3xl">
15+
<%= @universe.description %>
16+
</p>
17+
<% end %>
1618
</div>
1719
</main>
18-
19-
<div class="mt-8">
20-
<div class="max-w-7xl mx-auto px-4 sm:px-6">
21-
<nav class="relative flex items-center justify-between sm:h-10 md:justify-center" aria-label="Global">
22-
<div class="hidden md:flex md:space-x-10">
23-
<a href="#" class="font-medium text-gray-500 hover:text-gray-900 hover:bg-white rounded-lg hover:border-gray-200 border-transparent border px-4">Worldbuilding</a>
24-
<a href="#" class="font-medium text-gray-500 hover:text-gray-900 hover:bg-white rounded-lg hover:border-gray-200 border-transparent border px-4">Documents</a>
25-
</div>
26-
</nav>
27-
</div>
28-
</div>
2920

3021
</div>
3122
</div>
@@ -43,16 +34,11 @@
4334
<div class="mt-12 lg:mt-0 lg:col-span-2">
4435
<dl>
4536
<% @starred_pages.each do |page| %>
46-
<%= link_to polymorphic_path(page['page_type'].downcase, id: page['id']), class: 'block px-4 py-3 hover:bg-white rounded-lg hover:border-gray-200 border-transparent border' do %>
47-
<dt class="text-lg leading-6 font-medium <%= content_class_from_name(page['page_type']).text_color %>">
48-
<i class="material-icons float-left mr-2"><%= content_class_from_name(page['page_type']).icon %></i>
49-
<%= page['name'] %>
37+
<%= link_to polymorphic_path(page.page_type.downcase, id: page.id), class: 'block px-4 py-3 hover:bg-white rounded-lg hover:border-gray-200 border-transparent border' do %>
38+
<dt class="text-lg leading-6 font-medium <%= content_class_from_name(page.page_type).text_color %>">
39+
<i class="material-icons float-left mr-2"><%= content_class_from_name(page.page_type).icon %></i>
40+
<%= page.name %>
5041
</dt>
51-
<!--
52-
<dd class="mt-2 text-base text-gray-500">
53-
Description
54-
</dd>
55-
-->
5642
<% end %>
5743
<% end %>
5844
</dl>
@@ -62,47 +48,57 @@
6248
</div>
6349
<% end %>
6450

65-
<div class="">
51+
<div data-controller="toc-filter">
6652
<div class="max-w-7xl mx-auto py-16 px-4 sm:px-6 lg:py-20 lg:px-8">
6753
<div class="lg:grid lg:grid-cols-3 lg:gap-8">
6854
<div class="lg:pr-32">
6955
<h2 class="text-3xl font-extrabold text-gray-900">All pages</h2>
7056
<p class="mt-8 text-lg text-gray-500">
71-
Filter by page type...
57+
Filter by page type
7258
</p>
7359

7460
<ul role="list" class="border border-gray-200 rounded-md divide-y divide-gray-200 mt-2 bg-white">
61+
<li class="pl-3 pr-4 py-3 flex items-center justify-between text-sm rounded-full hover:bg-notebook-blue hover:text-white group cursor-pointer"
62+
data-action="click->toc-filter#showAll"
63+
data-toc-filter-target="allFilter">
64+
<div class="w-0 flex-1 flex items-center">
65+
<i class="material-icons float-left bg-white p-1 h-8 w-8 rounded-full text-gray-400">select_all</i>
66+
<span class="ml-2 flex-1 w-0 truncate font-medium">All types</span>
67+
</div>
68+
<div class="ml-4 flex-shrink-0">
69+
<span class="bg-gray-100 rounded px-1 text-xs group-hover:bg-notebook-blue">
70+
<%= @total_pages %>
71+
</span>
72+
</div>
73+
</li>
7574
<% @page_type_counts.each do |page_type, count| %>
76-
<%= link_to '#' do %>
77-
<li class="pl-3 pr-4 py-3 flex items-center justify-between text-sm rounded-full hover:bg-notebook-blue hover:text-white group">
78-
<div class="w-0 flex-1 flex items-center">
79-
<i class="material-icons float-left <%= content_class_from_name(page_type).text_color %> bg-white p-1 h-8 w-8 rounded-full"><%= content_class_from_name(page_type).icon %></i>
80-
<span class="ml-2 flex-1 w-0 truncate"><%= page_type.pluralize %></span>
81-
</div>
82-
<div class="ml-4 flex-shrink-0">
83-
<span class="bg-gray-100 rounded px-1 text-xs group-hover:bg-notebook-blue">
84-
<%= count %>
85-
</span>
86-
</div>
87-
</li>
88-
<% end %>
75+
<li class="pl-3 pr-4 py-3 flex items-center justify-between text-sm rounded-full hover:bg-notebook-blue hover:text-white group cursor-pointer"
76+
data-action="click->toc-filter#filter"
77+
data-page-type="<%= page_type %>">
78+
<div class="w-0 flex-1 flex items-center">
79+
<i class="material-icons float-left <%= content_class_from_name(page_type).text_color %> bg-white p-1 h-8 w-8 rounded-full"><%= content_class_from_name(page_type).icon %></i>
80+
<span class="ml-2 flex-1 w-0 truncate"><%= page_type.pluralize %></span>
81+
</div>
82+
<div class="ml-4 flex-shrink-0">
83+
<span class="bg-gray-100 rounded px-1 text-xs group-hover:bg-notebook-blue">
84+
<%= count %>
85+
</span>
86+
</div>
87+
</li>
8988
<% end %>
9089
</ul>
9190

9291
</div>
9392
<div class="mt-12 lg:mt-0 lg:col-span-2">
9493
<dl>
9594
<% @other_pages.each do |page| %>
96-
<%= link_to polymorphic_path(page['page_type'].downcase, id: page['id']), class: 'block px-4 py-3 hover:bg-white rounded-lg hover:border-gray-200 border-transparent border' do %>
97-
<dt class="text-lg leading-6 font-medium <%= content_class_from_name(page['page_type']).text_color %>">
98-
<i class="material-icons float-left mr-2"><%= content_class_from_name(page['page_type']).icon %></i>
99-
<%= page['name'] %>
95+
<%= link_to polymorphic_path(page.page_type.downcase, id: page.id),
96+
class: 'block px-4 py-3 hover:bg-white rounded-lg hover:border-gray-200 border-transparent border',
97+
data: { toc_filter_target: 'page', page_type: page.page_type } do %>
98+
<dt class="text-lg leading-6 font-medium <%= content_class_from_name(page.page_type).text_color %>">
99+
<i class="material-icons float-left mr-2"><%= content_class_from_name(page.page_type).icon %></i>
100+
<%= page.name %>
100101
</dt>
101-
<!--
102-
<dd class="mt-2 text-base text-gray-500">
103-
Description
104-
</dd>
105-
-->
106102
<% end %>
107103
<% end %>
108104
</dl>
@@ -111,38 +107,15 @@
111107
</div>
112108
</div>
113109

114-
<div class="bg-white">
115-
<div class="max-w-7xl mx-auto py-24 px-4 sm:px-6 lg:py-32 lg:px-8 lg:flex lg:items-center">
116-
<div class="lg:w-0 lg:flex-1">
117-
<h2 class="text-3xl font-extrabold text-gray-900 sm:text-4xl">Follow this universe</h2>
118-
<p class="mt-3 max-w-3xl text-lg text-gray-500">
119-
Stay connected and be notified as this world grows!
120-
</p>
121-
</div>
122-
<div class="mt-8 lg:mt-0 lg:ml-8">
123-
<form class="sm:flex">
124-
<label for="email-address" class="sr-only">Email address</label>
125-
<input id="email-address" name="email-address" type="email" autocomplete="email" required class="w-full px-5 py-3 border border-gray-300 shadow-sm placeholder-gray-400 focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs rounded-md" placeholder="Enter your email">
126-
<div class="mt-3 rounded-md shadow sm:mt-0 sm:ml-3 sm:flex-shrink-0">
127-
<button type="submit" class="w-full flex items-center justify-center py-3 px-5 border border-transparent text-base font-medium rounded-md text-white bg-notebook-blue hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Notify me</button>
128-
</div>
129-
</form>
130-
<p class="mt-3 text-sm text-gray-500">
131-
Your email address won't be shared with <%= @toc_user.display_name %>.
132-
</p>
133-
</div>
134-
</div>
135-
</div>
136-
137-
<section class="container mx-auto">
110+
<section class="container mx-auto pb-16">
138111
<div>
139112
<dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3 text-center">
140113
<div class="px-4 py-5 bg-white shadow rounded-lg overflow-hidden sm:p-6">
141114
<dt class="text-sm font-medium text-gray-500 truncate">Total Pages</dt>
142115
<dd
143116
data-controller="animated-number"
144117
data-animated-number-start-value="0"
145-
data-animated-number-end-value="5192"
118+
data-animated-number-end-value="<%= @total_pages %>"
146119
data-animated-number-duration-value="700"
147120
class="mt-1 text-3xl font-semibold text-gray-900"
148121
></dd>
@@ -153,15 +126,21 @@
153126
<dd
154127
data-controller="animated-number"
155128
data-animated-number-start-value="0"
156-
data-animated-number-end-value="85192"
129+
data-animated-number-end-value="<%= @total_words %>"
157130
data-animated-number-duration-value="800"
158131
class="mt-1 text-3xl font-semibold text-gray-900"
159132
></dd>
160133
</div>
161134

162135
<div class="px-4 py-5 bg-white shadow rounded-lg overflow-hidden sm:p-6">
163-
<dt class="text-sm font-medium text-gray-500 truncate">Something</dt>
164-
<dd class="mt-1 text-3xl font-semibold text-gray-900">24.57%</dd>
136+
<dt class="text-sm font-medium text-gray-500 truncate">Page Types</dt>
137+
<dd
138+
data-controller="animated-number"
139+
data-animated-number-start-value="0"
140+
data-animated-number-end-value="<%= @content_type_count %>"
141+
data-animated-number-duration-value="500"
142+
class="mt-1 text-3xl font-semibold text-gray-900"
143+
></dd>
165144
</div>
166145
</dl>
167146
</div>

config/routes.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@
286286
post 'universes/:universe_id/contributors', to: 'contributors#create', as: :universe_contributors
287287
get '/unsubscribe/emails/:code', to: 'emails#one_click_unsubscribe'
288288

289+
get '/universes/:id/contents', to: 'main#table_of_contents', as: :universe_contents
290+
289291
get '/paper', to: redirect('https://www.notebook-paper.com')
290292

291293
# Help Center - Public routes

0 commit comments

Comments
 (0)