render inReplyTo message

This commit is contained in:
Daniel Gultsch 2023-03-30 17:47:00 +02:00
parent a4fe60dece
commit 1b3c7b6a42
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
5 changed files with 142 additions and 10 deletions

View file

@ -96,15 +96,34 @@ public final class MessageWithContentReactions
public String textContent() { public String textContent() {
final var content = Iterables.getFirst(this.contents, null); final var content = Iterables.getFirst(this.contents, null);
final var text = Strings.nullToEmpty(content == null ? null : content.body); return Strings.nullToEmpty(content == null ? null : content.body);
return text;
// return text.substring(0,Math.min(text.length(),20));
} }
public boolean hasPreview() { public boolean hasPreview() {
return Iterables.tryFind(this.contents, c -> c.type == PartType.FILE).isPresent(); return Iterables.tryFind(this.contents, c -> c.type == PartType.FILE).isPresent();
} }
public boolean hasInReplyTo() {
return this.inReplyTo != null;
}
public Instant inReplyToSentAt() {
return this.inReplyTo == null ? null : this.inReplyTo.sentAt;
}
public String inReplyToSender() {
return this.inReplyTo == null ? null : this.inReplyTo.fromResource;
}
public String inReplyToTextContent() {
final var inReplyTo = this.inReplyTo;
if (inReplyTo == null) {
return null;
}
final var content = Iterables.getFirst(inReplyTo.contents, null);
return Strings.nullToEmpty(content == null ? null : content.body);
}
public AddressWithName getAddressWithName() { public AddressWithName getAddressWithName() {
if (isKnownSender()) { if (isKnownSender()) {
return new AddressWithName(individualAddress(), individualName()); return new AddressWithName(individualAddress(), individualName());

View file

@ -15,6 +15,7 @@ import im.conversations.android.databinding.ItemMessageReceivedBinding;
import im.conversations.android.databinding.ItemMessageSentBinding; import im.conversations.android.databinding.ItemMessageSentBinding;
import im.conversations.android.databinding.ItemMessageSeparatorBinding; import im.conversations.android.databinding.ItemMessageSeparatorBinding;
import im.conversations.android.ui.AvatarFetcher; import im.conversations.android.ui.AvatarFetcher;
import java.util.function.Consumer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -27,6 +28,8 @@ public class MessageAdapter
private static final Logger LOGGER = LoggerFactory.getLogger(MessageAdapter.class); private static final Logger LOGGER = LoggerFactory.getLogger(MessageAdapter.class);
private Consumer<Long> onNavigateToInReplyTo;
public MessageAdapter(@NonNull DiffUtil.ItemCallback<MessageAdapterItem> diffCallback) { public MessageAdapter(@NonNull DiffUtil.ItemCallback<MessageAdapterItem> diffCallback) {
super(diffCallback); super(diffCallback);
} }
@ -90,7 +93,12 @@ public class MessageAdapter
@NonNull AbstractMessageViewHolder holder, @NonNull AbstractMessageViewHolder holder,
@NonNull final MessageWithContentReactions message) { @NonNull final MessageWithContentReactions message) {
holder.setItem(message); holder.setItem(message);
final var inReplyTo = message.inReplyTo;
if (holder instanceof MessageReceivedViewHolder messageReceivedViewHolder) { if (holder instanceof MessageReceivedViewHolder messageReceivedViewHolder) {
if (inReplyTo != null) {
messageReceivedViewHolder.binding.embeddedMessage.setOnClickListener(
view -> onNavigateToInReplyTo.accept(inReplyTo.id));
}
final var addressWithName = message.getAddressWithName(); final var addressWithName = message.getAddressWithName();
final var avatar = message.getAvatar(); final var avatar = message.getAvatar();
if (avatar != null) { if (avatar != null) {
@ -98,9 +106,18 @@ public class MessageAdapter
} else { } else {
AvatarFetcher.setDefault(messageReceivedViewHolder.binding.avatar, addressWithName); AvatarFetcher.setDefault(messageReceivedViewHolder.binding.avatar, addressWithName);
} }
} else if (holder instanceof MessageSentViewHolder messageSentViewHolder) {
if (inReplyTo != null) {
messageSentViewHolder.binding.embeddedMessage.setOnClickListener(
view -> onNavigateToInReplyTo.accept(inReplyTo.id));
}
} }
} }
public void setOnNavigateToInReplyTo(final Consumer<Long> consumer) {
this.onNavigateToInReplyTo = consumer;
}
protected abstract static class AbstractMessageViewHolder extends RecyclerView.ViewHolder { protected abstract static class AbstractMessageViewHolder extends RecyclerView.ViewHolder {
private AbstractMessageViewHolder(@NonNull View itemView) { private AbstractMessageViewHolder(@NonNull View itemView) {

View file

@ -53,6 +53,7 @@ public class ChatFragment extends Fragment {
this.binding.messages.setLayoutManager(linearLayoutManager); this.binding.messages.setLayoutManager(linearLayoutManager);
this.recyclerViewScroller = new RecyclerViewScroller(this.binding.messages); this.recyclerViewScroller = new RecyclerViewScroller(this.binding.messages);
this.messageAdapter = new MessageAdapter(new MessageComparator()); this.messageAdapter = new MessageAdapter(new MessageComparator());
this.messageAdapter.setOnNavigateToInReplyTo(this::onNavigateToInReplyTo);
this.binding.messages.setAdapter(this.messageAdapter); this.binding.messages.setAdapter(this.messageAdapter);
this.chatViewModel.getMessages().observe(getViewLifecycleOwner(), this::submitPagingData); this.chatViewModel.getMessages().observe(getViewLifecycleOwner(), this::submitPagingData);
@ -64,10 +65,6 @@ public class ChatFragment extends Fragment {
NavControllers.findNavController(requireActivity(), R.id.nav_host_fragment) NavControllers.findNavController(requireActivity(), R.id.nav_host_fragment)
.popBackStack(); .popBackStack();
}); });
this.binding.addContent.setOnClickListener(
v -> {
scrollToMessageId(1039);
});
this.binding.messageLayout.setEndIconOnClickListener( this.binding.messageLayout.setEndIconOnClickListener(
v -> { v -> {
this.scrollToPositionToEnd(); this.scrollToPositionToEnd();
@ -76,6 +73,10 @@ public class ChatFragment extends Fragment {
return this.binding.getRoot(); return this.binding.getRoot();
} }
private void onNavigateToInReplyTo(long messageId) {
this.scrollToMessageId(messageId);
}
private void submitPagingData(final Boolean isShowDateSeparators) { private void submitPagingData(final Boolean isShowDateSeparators) {
final var pagingData = this.chatViewModel.getMessages().getValue(); final var pagingData = this.chatViewModel.getMessages().getValue();
if (pagingData == null) { if (pagingData == null) {

View file

@ -34,6 +34,53 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_max="@dimen/message_bubble_max_width"> app:layout_constraintWidth_max="@dimen/message_bubble_max_width">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/embeddedMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?colorPrimaryContainer"
android:foreground="?android:attr/selectableItemBackground"
android:padding="8dp"
android:visibility="@{message.hasInReplyTo ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/embeddedMessageSender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{message.inReplyToSender}"
android:textAppearance="?textAppearanceTitleSmall"
android:textColor="?colorOnPrimaryContainer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Romeo Montague" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4sp"
android:textAppearance="?textAppearanceLabelSmall"
android:textColor="?colorOnPrimaryContainer"
app:datetime="@{message.inReplyToSentAt}"
app:layout_constraintBaseline_toBaselineOf="@+id/embeddedMessageSender"
app:layout_constraintStart_toEndOf="@+id/embeddedMessageSender"
tools:text="11:23 PM" />
<TextView
android:id="@+id/embeddedMessageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4sp"
android:text="@{message.inReplyToTextContent}"
android:textAppearance="?textAppearanceBodySmall"
android:textColor="?colorOnPrimaryContainer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/embeddedMessageSender"
tools:text="In lorem orci, faucibus a magna a, auctor feugiat lorem. Sed efficitur turpis nisi, nec ultrices arcu tempus et. Nulla consectetur sodales tortor, at cursus erat ultricies non." />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView <ImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -43,13 +90,14 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/message_preview_max_height" app:layout_constraintHeight_max="@dimen/message_preview_max_height"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@+id/embeddedMessage"
app:layout_constraintWidth_max="@dimen/message_preview_max_width" /> app:layout_constraintWidth_max="@dimen/message_preview_max_width" />
<LinearLayout <LinearLayout
android:id="@+id/textContentWrapper" android:id="@+id/textContentWrapper"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp" android:padding="8dp"
android:visibility="visible" android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View file

@ -24,6 +24,53 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_max="@dimen/message_bubble_max_width"> app:layout_constraintWidth_max="@dimen/message_bubble_max_width">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/embeddedMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?colorPrimaryContainer"
android:foreground="?android:attr/selectableItemBackground"
android:padding="8dp"
android:visibility="@{message.hasInReplyTo ? View.VISIBLE : View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/embeddedMessageSender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{message.inReplyToSender}"
android:textAppearance="?textAppearanceTitleSmall"
android:textColor="?colorOnPrimaryContainer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Romeo Montague" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4sp"
android:textAppearance="?textAppearanceLabelSmall"
android:textColor="?colorOnPrimaryContainer"
app:datetime="@{message.inReplyToSentAt}"
app:layout_constraintBaseline_toBaselineOf="@+id/embeddedMessageSender"
app:layout_constraintStart_toEndOf="@+id/embeddedMessageSender"
tools:text="11:23 PM" />
<TextView
android:id="@+id/embeddedMessageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4sp"
android:text="@{message.inReplyToTextContent}"
android:textAppearance="?textAppearanceBodySmall"
android:textColor="?colorOnPrimaryContainer"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/embeddedMessageSender"
tools:text="In lorem orci, faucibus a magna a, auctor feugiat lorem. Sed efficitur turpis nisi, nec ultrices arcu tempus et. Nulla consectetur sodales tortor, at cursus erat ultricies non." />
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView <ImageView
android:id="@+id/image" android:id="@+id/image"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -33,13 +80,14 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/message_preview_max_height" app:layout_constraintHeight_max="@dimen/message_preview_max_height"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@+id/embeddedMessage"
app:layout_constraintWidth_max="@dimen/message_preview_max_width" /> app:layout_constraintWidth_max="@dimen/message_preview_max_width" />
<LinearLayout <LinearLayout
android:id="@+id/textContentWrapper" android:id="@+id/textContentWrapper"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp" android:padding="8dp"
android:visibility="visible" android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -56,7 +104,6 @@
android:textColor="?colorOnSecondaryContainer" android:textColor="?colorOnSecondaryContainer"
tools:text="Quisque sit amet metus faucibus, egestas est eu, hendrerit mauris. Suspendisse pretium nisl purus, vitae vestibulum sapien rhoncus nec. Quisque molestie ante felis, vel dapibus ex mattis a. Morbi venenatis vestibulum neque, vel ornare sapien. Aliquam erat volutpat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. " /> tools:text="Quisque sit amet metus faucibus, egestas est eu, hendrerit mauris. Suspendisse pretium nisl purus, vitae vestibulum sapien rhoncus nec. Quisque molestie ante felis, vel dapibus ex mattis a. Morbi venenatis vestibulum neque, vel ornare sapien. Aliquam erat volutpat. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. " />
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>