Skip to content

Commit 9d46c59

Browse files
committed
Second round of thread index redesign
The view should be done-ish, missing a few lazy loading popups The seed script is also updated to create a sample database with all kind of interesting test data included.
1 parent ea3a257 commit 9d46c59

File tree

6 files changed

+452
-90
lines changed

6 files changed

+452
-90
lines changed

app/assets/stylesheets/colors.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,16 @@
4545
--color-bg-button-hover: var(--color-secondary2-1);
4646

4747
--color-border-contributor: var(--color-secondary2-2);
48+
--color-border-new: var(--color-secondary1-1);
49+
--color-border-aware: var(--color-secondary1-2);
50+
--color-border-reading: var(--color-secondary1-3);
51+
--color-border-read: var(--color-secondary1-4);
4852

4953
--color-bg-icon: var(--color-secondary1-2);
54+
--color-bg-icon-status: var(--color-secondary2-2);
5055
--color-bg-hoverbox: var(--color-secondary2-1);
5156
--color-bg-contributor: var(--color-secondary1-2);
5257
--color-bg-committer: var(--color-secondary1-1);
58+
--color-bg-activity-team: var(--color-secondary2-2);
59+
--color-bg-activity-user: var(--color-secondary2-1);
5360
}

app/assets/stylesheets/components/topics.css

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,42 @@
5555
color: var(--color-text-secondary);
5656
}
5757

58+
& th.activity-header {
59+
text-align: center;
60+
}
61+
62+
& th.participants-header {
63+
text-align: right;
64+
}
65+
66+
& th.topic-title {
67+
border-left: var(--color-bg-table-header) 8px solid;
68+
}
69+
5870
& td {
59-
padding: var(--spacing-4);
71+
padding: var(--spacing-2);
6072
border-bottom: 1px #000 solid;
6173
vertical-align: top;
6274
}
6375

76+
& .topic-replies {
77+
width: 100px;
78+
}
79+
& .topic-activity {
80+
width: 140px;
81+
}
6482
& .topic-participants {
65-
width: 340px;
83+
width: 250px;
84+
text-align: right;
85+
86+
.topic-icon {
87+
margin: 0 4px;
88+
vertical-align: top;
89+
}
90+
91+
.participants {
92+
display: inline-block;
93+
}
6694
}
6795
}
6896

@@ -71,7 +99,7 @@
7199
background: var(--color-bg-icon);
72100
border-radius: var(--border-radius-sm);
73101
padding: 3px;
74-
margin: 5px 0px;
102+
margin: 0px;
75103
margin-right: 10px;
76104
position: relative;
77105
}
@@ -95,6 +123,26 @@
95123
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
96124
}
97125

126+
.status-border {
127+
border-left: 8px solid transparent;
128+
}
129+
130+
.status-border.status-new {
131+
border-left-color: var(--color-border-new);
132+
}
133+
134+
.status-border.status-aware {
135+
border-left-color: var(--color-border-aware);
136+
}
137+
138+
.status-border.status-reading {
139+
border-left-color: var(--color-border-reading);
140+
}
141+
142+
.status-border.status-read {
143+
border-left-color: var(--color-border-read);
144+
}
145+
98146
.activity-core_team {
99147
background-color: var(--color-bg-committer);
100148
}
@@ -103,12 +151,42 @@
103151
background-color: var(--color-bg-committer);
104152
}
105153

154+
.activity-note {
155+
background-color: var(--color-bg-activity-team);
156+
.topic-icon-badge {
157+
background-color: var(--color-bg-activity-team);
158+
}
159+
}
160+
161+
.activity-team {
162+
background-color: var(--color-bg-activity-team);
163+
164+
.topic-icon-badge {
165+
background-color: var(--color-bg-activity-team);
166+
}
167+
}
168+
169+
.activity-team.is-mine {
170+
background-color: var(--color-bg-activity-user);
171+
}
172+
173+
.activity-team-read {
174+
background-color: var(--color-bg-activity-team);
175+
.topic-icon-badge {
176+
background-color: var(--color-bg-activity-team);
177+
}
178+
}
179+
106180
.topic-row {
107181
&:hover {
108182
background: var(--color-bg-hover);
109183
}
110184
}
111185

186+
.topic-row.topic-read {
187+
color: #444;
188+
}
189+
112190
.topic-icon-hover {
113191
display: none;
114192
position: absolute;
@@ -122,6 +200,11 @@
122200
min-width: 260px;
123201
}
124202

203+
.topic-participants .topic-icon-hover {
204+
left: auto;
205+
right: 0;
206+
}
207+
125208
.topic-icon.is-open .topic-icon-hover {
126209
display: block;
127210
}
@@ -156,17 +239,21 @@
156239
.activity-info {
157240
display: flex;
158241
flex-direction: column;
159-
gap: var(--spacing-1);
242+
gap: 0;
243+
text-align: center;
244+
font-size: var(--font-size-sm);
160245
}
161246

162247
.activity-replies {
163248
font-weight: var(--font-weight-semibold);
164249
color: var(--color-text-secondary);
250+
font-size: var(--font-size-sm);
165251
}
166252

167253
.activity-time {
168254
font-weight: var(--font-weight-medium);
169255
color: var(--color-text-secondary);
256+
font-size: var(--font-size-sm);
170257
}
171258

172259
.activity-author {

app/controllers/topics_controller.rb

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def index
1212
preload_topic_states if user_signed_in?
1313
preload_note_counts if user_signed_in?
1414
load_visible_tags if user_signed_in?
15+
preload_participation_flags if user_signed_in?
1516

1617
respond_to do |format|
1718
format.html
@@ -98,6 +99,8 @@ def search
9899

99100
apply_cursor_pagination(base_query)
100101

102+
preload_participation_flags if user_signed_in?
103+
101104
respond_to do |format|
102105
format.html
103106
format.turbo_stream { render :index }
@@ -257,22 +260,25 @@ def preload_topic_states
257260
last_times = Message.where(topic_id: topic_ids).group(:topic_id).maximum(:created_at)
258261
total_counts = Message.where(topic_id: topic_ids).group(:topic_id).count
259262

260-
aware_map = ThreadAwareness.where(user: current_user, topic_id: topic_ids)
261-
.pluck(:topic_id, :aware_until_message_id)
262-
.to_h
263-
read_rows = MessageReadRange.where(user: current_user, topic_id: topic_ids)
264-
.pluck(:topic_id, :range_start_message_id, :range_end_message_id)
265-
read_ranges = read_rows.each_with_object(Hash.new { |h, k| h[k] = [] }) do |(tid, s, e), acc|
266-
acc[tid] << [s, e]
267-
end
268-
global_aware_before = current_user.aware_before
263+
if user_signed_in?
264+
aware_map = ThreadAwareness.where(user: current_user, topic_id: topic_ids)
265+
.pluck(:topic_id, :aware_until_message_id)
266+
.to_h
267+
read_rows = MessageReadRange.where(user: current_user, topic_id: topic_ids)
268+
.pluck(:topic_id, :range_start_message_id, :range_end_message_id)
269+
read_ranges = read_rows.each_with_object(Hash.new { |h, k| h[k] = [] }) do |(tid, s, e), acc|
270+
acc[tid] << [s, e]
271+
end
272+
global_aware_before = current_user.aware_before
269273

270-
team_readers = preload_team_reader_states(topic_ids, last_ids)
274+
team_readers = preload_team_reader_states(topic_ids, last_ids)
275+
end
271276

272277
@topic_states = {}
273278
@topics.each do |topic|
274279
last_id = last_ids[topic.id]
275280
last_time = last_times[topic.id]
281+
if user_signed_in?
276282
aware_until = aware_map[topic.id]
277283
total = total_counts[topic.id].to_i
278284
ranges = read_ranges[topic.id] || []
@@ -282,6 +288,9 @@ def preload_topic_states
282288
end
283289
status = compute_topic_status(total:, last_time:, aware_until:, read_count:, global_aware_before:)
284290
progress = compute_progress(total:, read_count:)
291+
else
292+
status = :new
293+
end
285294
@topic_states[topic.id] = { status:, aware_until:, read_count:, last_id:, last_time:, progress:, team_readers: team_readers[topic.id] || [] }
286295
end
287296
end
@@ -306,7 +315,44 @@ def load_visible_tags
306315
.count
307316
end
308317

309-
# Legacy helpers for backward compatibility (not used after refactor)
318+
def preload_participation_flags
319+
topic_ids = @topics.map(&:id)
320+
return if topic_ids.empty?
321+
322+
my_alias_ids = Alias.where(user_id: current_user.id).pluck(:id)
323+
324+
team_ids = TeamMember.where(user_id: current_user.id).pluck(:team_id)
325+
teammate_user_ids = if team_ids.any?
326+
TeamMember.where(team_id: team_ids).pluck(:user_id).uniq
327+
else
328+
[current_user.id]
329+
end
330+
teammate_alias_ids = Alias.where(user_id: teammate_user_ids).pluck(:id)
331+
332+
rows = Message.where(topic_id: topic_ids, sender_id: teammate_alias_ids)
333+
.select(:topic_id, :sender_id)
334+
.distinct
335+
336+
alias_map = Alias.includes(:contributors).where(id: teammate_alias_ids).index_by(&:id)
337+
338+
@participation_flags = Hash.new { |h, k| h[k] = { mine: false, team: false, aliases: [] } }
339+
340+
rows.each do |row|
341+
entry = @participation_flags[row.topic_id]
342+
alias_record = alias_map[row.sender_id]
343+
next unless alias_record
344+
345+
entry[:aliases] << alias_record
346+
entry[:mine] ||= my_alias_ids.include?(row.sender_id)
347+
entry[:team] = true
348+
end
349+
350+
@participation_flags.transform_values! do |v|
351+
v[:aliases] = v[:aliases].uniq { |a| a.id }
352+
v
353+
end
354+
end
355+
310356
def compute_topic_status(total:, last_time:, aware_until:, read_count:, global_aware_before:)
311357
return :new unless aware_until || read_count.positive? || global_aware_before
312358
return :read if total.positive? && read_count >= total

0 commit comments

Comments
 (0)