Skip to content

Commit b3b1f9d

Browse files
committed
show date separators in live chat
1 parent f4c5095 commit b3b1f9d

File tree

6 files changed

+208
-10
lines changed

6 files changed

+208
-10
lines changed

libraries/sdk/src/main/java/com/fastcomments/sdk/CommentViewHolder.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,29 @@ public CommentViewHolder(Context context, FastCommentsSDK sdk, @NonNull View ite
100100
childPaginationProgressBar = itemView.findViewById(R.id.childPaginationProgressBar);
101101
}
102102

103+
private boolean liveChatStyle = false;
104+
105+
/**
106+
* Set whether to use live chat styling (smaller avatars, hidden dates)
107+
* @param liveChatStyle True for live chat style
108+
*/
109+
public void setLiveChatStyle(boolean liveChatStyle) {
110+
this.liveChatStyle = liveChatStyle;
111+
112+
// Update avatar size
113+
ViewGroup.LayoutParams avatarParams = avatarImageView.getLayoutParams();
114+
if (liveChatStyle) {
115+
// Smaller avatar for live chat
116+
avatarParams.width = (int) (context.getResources().getDisplayMetrics().density * 28);
117+
avatarParams.height = (int) (context.getResources().getDisplayMetrics().density * 28);
118+
} else {
119+
// Regular avatar size
120+
avatarParams.width = (int) (context.getResources().getDisplayMetrics().density * 40);
121+
avatarParams.height = (int) (context.getResources().getDisplayMetrics().density * 40);
122+
}
123+
avatarImageView.setLayoutParams(avatarParams);
124+
}
125+
103126
public void setComment(final RenderableComment comment, boolean disableUnverifiedLabel, final CommentsAdapter.OnToggleRepliesListener listener) {
104127
//noinspection ConstantValue
105128
if (comment.getComment().getCommenterName() != null) {
@@ -144,8 +167,15 @@ public void setComment(final RenderableComment comment, boolean disableUnverifie
144167
// Store current comment reference first, so updateDateDisplay has the correct reference
145168
this.currentComment = comment;
146169

147-
// Format and display the date
148-
updateDateDisplay();
170+
// In live chat mode, we hide individual comment dates
171+
// Dates will be shown as separators between comments instead
172+
if (liveChatStyle) {
173+
dateTextView.setVisibility(View.GONE);
174+
} else {
175+
dateTextView.setVisibility(View.VISIBLE);
176+
// Format and display the date
177+
updateDateDisplay();
178+
}
149179

150180
ViewGroup.LayoutParams textViewLayout = contentTextView.getLayoutParams();
151181
textViewLayout.height = ViewGroup.LayoutParams.WRAP_CONTENT;

libraries/sdk/src/main/java/com/fastcomments/sdk/CommentsAdapter.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,26 @@
55
import android.view.View;
66
import android.view.ViewGroup;
77
import android.widget.Button;
8+
import android.widget.TextView;
89

910
import androidx.annotation.NonNull;
1011
import androidx.recyclerview.widget.RecyclerView;
1112

1213
import com.fastcomments.model.PublicComment;
1314

15+
import java.time.LocalDate;
16+
import java.time.OffsetDateTime;
17+
import java.time.format.DateTimeFormatter;
18+
import java.time.format.FormatStyle;
19+
import java.util.ArrayList;
1420
import java.util.List;
21+
import java.util.Locale;
1522

1623
public class CommentsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
1724

1825
private static final int VIEW_TYPE_COMMENT = 0;
1926
private static final int VIEW_TYPE_BUTTON = 1;
27+
private static final int VIEW_TYPE_DATE_SEPARATOR = 2;
2028

2129
private final Context context;
2230
private final CommentsTree commentsTree;
@@ -73,6 +81,8 @@ public int getItemViewType(int position) {
7381
RenderableNode node = commentsTree.visibleNodes.get(position);
7482
if (node instanceof RenderableComment) {
7583
return VIEW_TYPE_COMMENT;
84+
} else if (node instanceof RenderableNode.DateSeparator) {
85+
return VIEW_TYPE_DATE_SEPARATOR;
7686
} else {
7787
return VIEW_TYPE_BUTTON;
7888
}
@@ -84,6 +94,9 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
8494
if (viewType == VIEW_TYPE_COMMENT) {
8595
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_comment, parent, false);
8696
return new CommentViewHolder(context, sdk, view);
97+
} else if (viewType == VIEW_TYPE_DATE_SEPARATOR) {
98+
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.date_separator, parent, false);
99+
return new DateSeparatorViewHolder(view);
87100
} else {
88101
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_button, parent, false);
89102
return new ButtonViewHolder(view);
@@ -94,14 +107,24 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
94107
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
95108
if (holder instanceof CommentViewHolder) {
96109
bindCommentViewHolder((CommentViewHolder) holder, position);
110+
} else if (holder instanceof DateSeparatorViewHolder) {
111+
bindDateSeparatorViewHolder((DateSeparatorViewHolder) holder, position);
97112
} else if (holder instanceof ButtonViewHolder) {
98113
bindButtonViewHolder((ButtonViewHolder) holder, position);
99114
}
100115
}
101116

117+
private void bindDateSeparatorViewHolder(DateSeparatorViewHolder holder, int position) {
118+
final RenderableNode.DateSeparator separator = (RenderableNode.DateSeparator) commentsTree.visibleNodes.get(position);
119+
holder.setDate(separator);
120+
}
121+
102122
private void bindCommentViewHolder(CommentViewHolder holder, int position) {
103123
final RenderableComment comment = (RenderableComment) commentsTree.visibleNodes.get(position);
104124

125+
// Set live chat style for smaller avatars and hidden dates
126+
holder.setLiveChatStyle(commentsTree.liveChatStyle);
127+
105128
// Pass config setting for unverified label
106129
boolean disableUnverifiedLabel = Boolean.TRUE.equals(sdk.getConfig().disableUnverifiedLabel);
107130

@@ -250,4 +273,20 @@ public void setButtonClickListener(View.OnClickListener listener) {
250273
button.setOnClickListener(listener);
251274
}
252275
}
276+
277+
/**
278+
* Date separator view holder for the live chat view
279+
*/
280+
static class DateSeparatorViewHolder extends RecyclerView.ViewHolder {
281+
private final TextView dateText;
282+
283+
public DateSeparatorViewHolder(@NonNull View itemView) {
284+
super(itemView);
285+
dateText = itemView.findViewById(R.id.dateSeparatorText);
286+
}
287+
288+
public void setDate(RenderableNode.DateSeparator separator) {
289+
dateText.setText(separator.getFormattedDate());
290+
}
291+
}
253292
}

libraries/sdk/src/main/java/com/fastcomments/sdk/CommentsTree.java

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class CommentsTree {
3131
public List<RenderableComment> allComments = new ArrayList<>(0); // in any order
3232
public List<RenderableNode> visibleNodes = new ArrayList<>(0); // in view order - can include comments and buttons
3333
private CommentsAdapter adapter;
34+
public boolean liveChatStyle = false;
3435

3536
// Separate collections for easier lookup
3637
private RenderableButton newRootCommentsButton; // Only one of these at most
@@ -56,6 +57,14 @@ public CommentsAdapter getAdapter() {
5657
public Context getContext() {
5758
return adapter != null ? adapter.getContext() : null;
5859
}
60+
61+
/**
62+
* Set whether this tree should use live chat style rendering (with date separators)
63+
* @param liveChatStyle true for live chat style
64+
*/
65+
public void setLiveChatStyle(boolean liveChatStyle) {
66+
this.liveChatStyle = liveChatStyle;
67+
}
5968

6069
public void notifyItemChanged(RenderableNode node) {
6170
final int index = this.visibleNodes.indexOf(node);
@@ -72,14 +81,44 @@ public void build(List<PublicComment> comments) {
7281
return;
7382
}
7483

75-
// Process all comments and create RenderableComment objects
76-
for (PublicComment comment : comments) {
77-
final RenderableComment renderableComment = new RenderableComment(comment);
78-
addToMapAndRelated(renderableComment);
79-
allComments.add(renderableComment);
80-
visibleNodes.add(renderableComment);
81-
if (comment.getChildren() != null) {
82-
handleChildren(allComments, visibleNodes, comment.getChildren(), renderableComment.isRepliesShown);
84+
if (!liveChatStyle) {
85+
// Standard mode - process all comments and create RenderableComment objects
86+
for (PublicComment comment : comments) {
87+
final RenderableComment renderableComment = new RenderableComment(comment);
88+
addToMapAndRelated(renderableComment);
89+
allComments.add(renderableComment);
90+
visibleNodes.add(renderableComment);
91+
if (comment.getChildren() != null) {
92+
handleChildren(allComments, visibleNodes, comment.getChildren(), renderableComment.isRepliesShown);
93+
}
94+
}
95+
} else {
96+
// Live chat mode - insert date separators
97+
java.time.LocalDate currentDate = null;
98+
99+
for (PublicComment comment : comments) {
100+
final RenderableComment renderableComment = new RenderableComment(comment);
101+
addToMapAndRelated(renderableComment);
102+
allComments.add(renderableComment);
103+
104+
// Check if we need a date separator
105+
if (comment.getDate() != null) {
106+
java.time.LocalDate commentDate = comment.getDate().toLocalDate();
107+
108+
if (currentDate == null || !currentDate.equals(commentDate)) {
109+
// Add date separator for this new date
110+
currentDate = commentDate;
111+
visibleNodes.add(new RenderableNode.DateSeparator(currentDate));
112+
}
113+
}
114+
115+
visibleNodes.add(renderableComment);
116+
117+
// In live chat, we typically don't show children/replies
118+
// But process them anyway in case this changes
119+
if (comment.getChildren() != null) {
120+
handleChildren(allComments, visibleNodes, comment.getChildren(), renderableComment.isRepliesShown);
121+
}
83122
}
84123
}
85124

@@ -452,6 +491,41 @@ public void addComment(PublicComment comment, boolean displayNow, SortDirections
452491
} else {
453492
// For oldest first (like chat), add at the bottom
454493
position = visibleNodes.size();
494+
495+
// Check if we need to add a date separator in live chat mode
496+
if (liveChatStyle && comment.getDate() != null) {
497+
java.time.LocalDate commentDate = comment.getDate().toLocalDate();
498+
boolean needDateSeparator = true;
499+
500+
// Check if there are any comments already with the same date
501+
for (int i = visibleNodes.size() - 1; i >= 0; i--) {
502+
RenderableNode node = visibleNodes.get(i);
503+
if (node instanceof RenderableNode.DateSeparator) {
504+
RenderableNode.DateSeparator separator = (RenderableNode.DateSeparator) node;
505+
if (separator.getDate().equals(commentDate)) {
506+
// We already have a separator for this date
507+
needDateSeparator = false;
508+
}
509+
break; // Exit after finding the most recent date separator
510+
} else if (node instanceof RenderableComment) {
511+
RenderableComment lastComment = (RenderableComment) node;
512+
if (lastComment.getComment().getDate() != null &&
513+
lastComment.getComment().getDate().toLocalDate().equals(commentDate)) {
514+
// The previous comment is from the same date
515+
needDateSeparator = false;
516+
break;
517+
}
518+
}
519+
}
520+
521+
if (needDateSeparator) {
522+
RenderableNode.DateSeparator separator = new RenderableNode.DateSeparator(commentDate);
523+
visibleNodes.add(separator);
524+
adapter.notifyItemInserted(position);
525+
position++;
526+
}
527+
}
528+
455529
visibleNodes.add(renderableComment);
456530
}
457531
adapter.notifyItemInserted(position);

libraries/sdk/src/main/java/com/fastcomments/sdk/LiveChatView.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ private void initializeWithSDK() {
213213
adapter = new CommentsAdapter(getContext(), sdk);
214214
recyclerView.setAdapter(adapter);
215215

216+
// Enable live chat style in the comment tree
217+
sdk.commentsTree.setLiveChatStyle(true);
218+
216219
// Set up infinite scrolling (in reverse) for chat mode
217220
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
218221
@Override
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,43 @@
11
package com.fastcomments.sdk;
22

3+
import java.time.LocalDate;
4+
import java.time.format.DateTimeFormatter;
5+
import java.time.format.FormatStyle;
6+
import java.util.Locale;
37
import java.util.Map;
48

59
/**
610
* Base class for renderable nodes in the comment tree
711
*/
812
public abstract class RenderableNode {
913
public abstract int determineNestingLevel(Map<String, RenderableComment> commentMap);
14+
15+
/**
16+
* Date separator node type for grouping comments by date in live chat
17+
*/
18+
public static class DateSeparator extends RenderableNode {
19+
private final LocalDate date;
20+
21+
public DateSeparator(LocalDate date) {
22+
this.date = date;
23+
}
24+
25+
public LocalDate getDate() {
26+
return date;
27+
}
28+
29+
public String getFormattedDate() {
30+
// Format date based on user's locale
31+
DateTimeFormatter formatter = DateTimeFormatter
32+
.ofLocalizedDate(FormatStyle.MEDIUM)
33+
.withLocale(Locale.getDefault());
34+
return date.format(formatter);
35+
}
36+
37+
@Override
38+
public int determineNestingLevel(Map<String, RenderableComment> commentMap) {
39+
// Date separators are always at top level
40+
return 0;
41+
}
42+
}
1043
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:layout_width="match_parent"
4+
android:layout_height="wrap_content"
5+
android:orientation="vertical"
6+
android:padding="8dp"
7+
android:gravity="center">
8+
9+
<TextView
10+
android:id="@+id/dateSeparatorText"
11+
android:layout_width="wrap_content"
12+
android:layout_height="wrap_content"
13+
android:textSize="14sp"
14+
android:textColor="#757575"
15+
android:paddingHorizontal="16dp"
16+
android:paddingVertical="4dp"
17+
android:background="#F0F0F0" />
18+
19+
</LinearLayout>

0 commit comments

Comments
 (0)