From ee4ed2e075546b4537561eea19c323975634ae63 Mon Sep 17 00:00:00 2001
From: audric <audric@loki.network>
Date: Tue, 24 Aug 2021 13:23:23 +1000
Subject: [PATCH] fix attachment logic with ui redesign

---
 stylesheets/_modules.scss                     | 205 +-----------
 stylesheets/_quote.scss                       |  10 +-
 stylesheets/_session.scss                     |  19 +-
 stylesheets/_theme_dark.scss                  |  44 ---
 .../conversation/ConversationHeader.tsx       |  16 +-
 ts/components/conversation/Image.tsx          |  31 +-
 ts/components/conversation/ImageGrid.tsx      |  40 +--
 ts/components/conversation/Message.tsx        | 313 ++++++++----------
 ts/components/conversation/Quote.tsx          |  84 +++--
 .../message/MessageAuthorText.tsx             |  37 ++-
 .../SessionQuotedMessageComposition.tsx       |   4 -
 .../session/settings/SessionSettings.tsx      |   1 +
 ts/state/selectors/conversations.ts           |  31 ++
 13 files changed, 275 insertions(+), 560 deletions(-)

diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss
index f9b0e8394..a46734f82 100644
--- a/stylesheets/_modules.scss
+++ b/stylesheets/_modules.scss
@@ -85,80 +85,16 @@
   right: 8px;
 }
 
-.module-message__attachment-container {
-  // Entirely to ensure that images are centered if they aren't full width of bubble
-  text-align: center;
-  position: relative;
-
-  margin-inline-start: -12px;
-  margin-inline-end: -12px;
-  margin-top: -10px;
-  margin-bottom: -10px;
-
-  border-radius: $session_message-container-border-radius;
-  overflow: hidden;
-  // no background by default for the attachment container
-}
-
-.module-message--outgoing {
-  .module-message__attachment-container--with-content-below,
-  .module-message__attachment-container--with-content-above {
-    background: none;
-  }
-}
-
-.module-message--incoming {
-  .module-message__attachment-container--with-content-below,
-  .module-message__attachment-container--with-content-above {
-    background: none;
-  }
-}
-
-.module-message__attachment-container--with-content-below {
-  margin-bottom: 7px;
-  border-bottom-left-radius: 0px;
-  border-bottom-right-radius: 0px;
-}
-
-.module-message__attachment-container--with-content-above {
-  margin-top: 4px;
-  border-top-left-radius: 0px;
-  border-top-right-radius: 0px;
-}
-
 .module-message__img-attachment {
-  margin-bottom: -3px;
-
-  // redundant with attachment-container, but we get cursor flashing on move otherwise
   cursor: pointer;
 }
 
-.module-message__audio-attachment {
-  margin-top: 2px;
-}
-
-.module-message__audio-attachment--with-content-below {
-  margin-bottom: 5px;
-}
-
-.module-message__audio-attachment--with-content-above {
-  margin-top: 6px;
-}
-
 .module-message__generic-attachment {
   display: flex;
   flex-direction: row;
   align-items: center;
 }
 
-.module-message__generic-attachment--with-content-below {
-  padding-bottom: 6px;
-}
-
-.module-message__generic-attachment--with-content-above {
-  padding-top: 4px;
-}
-
 .module-message__generic-attachment__icon-container {
   position: relative;
   cursor: pointer;
@@ -269,12 +205,6 @@
   border-top-right-radius: $session_message-container-border-radius;
 }
 
-.module-message__link-preview--with-content-above {
-  margin-top: 4px;
-  border-top-left-radius: 0px;
-  border-top-right-radius: 0px;
-}
-
 .module-message__link-preview__content {
   padding: 8px;
   border-top-left-radius: $session_message-container-border-radius;
@@ -286,13 +216,6 @@
   border: 1px solid $color-black-015;
 }
 
-.module-message__link-preview__content--with-content-above {
-  border-top: none;
-  border-bottom: none;
-  border-top-left-radius: 0px;
-  border-top-right-radius: 0px;
-}
-
 .module-message__link-preview__image_container {
   margin: -2px;
   margin-inline-end: 8px;
@@ -389,70 +312,6 @@
   align-items: center;
 }
 
-// Module: Embedded Contact
-
-.module-embedded-contact {
-  // Cursor is always a pointer because this component is always wired up to the contact detail screen
-  cursor: pointer;
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-}
-
-.module-embedded-contact--with-content-above {
-  padding-top: 4px;
-}
-
-.module-embedded-contact--with-content-below {
-  padding-bottom: 4px;
-}
-
-.module-embedded-contact__spinner-container {
-  padding-inline-start: 5px;
-  padding-inline-end: 5px;
-}
-
-.module-embedded-contact__text-container {
-  flex-grow: 1;
-  margin-inline-start: 8px;
-
-  max-width: calc(100% - 58px);
-}
-
-.module-embedded-contact__contact-name {
-  font-size: 14px;
-  line-height: 18px;
-  font-weight: 300;
-  margin-top: 6px;
-  color: $color-gray-90;
-
-  max-width: 100%;
-  white-space: nowrap;
-  overflow-x: hidden;
-  text-overflow: ellipsis;
-}
-
-.module-embedded-contact__contact-name--incoming {
-  color: $color-white;
-}
-
-.module-embedded-contact__contact-method {
-  font-size: 11px;
-  line-height: 16px;
-  letter-spacing: 0.3px;
-  margin-top: 3px;
-  color: $color-gray-60;
-
-  max-width: 100%;
-  white-space: nowrap;
-  overflow-x: hidden;
-  text-overflow: ellipsis;
-}
-
-.module-embedded-contact__contact-method--incoming {
-  color: $color-white-07;
-}
-
 // Module: Contact Detail
 
 .module-contact-detail {
@@ -543,45 +402,6 @@
   font-weight: bold;
 }
 
-// Module: Reset Session Notification
-
-.module-reset-session-notification {
-  margin-top: 14px;
-  font-size: 14px;
-  line-height: 20px;
-  letter-spacing: 0.3px;
-  color: $color-gray-60;
-  text-align: center;
-}
-
-.module-verification-notification__button {
-  margin-top: 5px;
-  display: inline-block;
-  cursor: pointer;
-  font-size: 13px;
-  font-weight: 300;
-  line-height: 18px;
-  padding: 12px;
-  color: $color-loki-green;
-  background-color: $color-light-02;
-  border-radius: 4px;
-}
-
-// Module: Verification Notification
-
-.module-verification-notification {
-  margin-top: 14px;
-  font-size: 14px;
-  line-height: 20px;
-  letter-spacing: 0.3px;
-  color: $color-gray-60;
-  text-align: center;
-}
-
-.module-verification-notification__contact {
-  font-weight: 300;
-}
-
 // Module: Timer Notification
 
 .module-timer-notification {
@@ -751,6 +571,7 @@
   align-items: center;
 
   -webkit-user-select: text;
+  cursor: pointer;
 
   .module-contact-name__profile-name {
     width: 100%;
@@ -1226,22 +1047,6 @@
   border-radius: 4px;
 }
 
-.module-image--curved-top-left {
-  border-top-left-radius: $session_message-container-border-radius;
-}
-.module-image--curved-top-right {
-  border-top-right-radius: $session_message-container-border-radius;
-}
-.module-image--curved-bottom-left {
-  border-bottom-left-radius: $session_message-container-border-radius;
-}
-.module-image--curved-bottom-right {
-  border-bottom-right-radius: $session_message-container-border-radius;
-}
-.module-image--small-curved-top-left {
-  border-top-left-radius: 10px;
-}
-
 .module-image__border-overlay {
   position: absolute;
   top: 0;
@@ -1249,7 +1054,6 @@
   z-index: 1;
   left: 0;
   right: 0;
-  box-shadow: inset 0px 0px 0px 1px $color-black-015;
 }
 
 .module-image__border-overlay--dark {
@@ -1265,10 +1069,7 @@
 
 .module-image__image {
   object-fit: cover;
-  // redundant with attachment-container, but we get cursor flashing on move otherwise
   cursor: pointer;
-
-  margin-bottom: -3px;
 }
 
 .module-image__bottom-overlay {
@@ -1347,10 +1148,6 @@
   margin: -1px;
 }
 
-.module-image-grid--one-image {
-  margin-bottom: -5px;
-}
-
 .module-image-grid__column {
   display: inline-flex;
   flex-direction: column;
diff --git a/stylesheets/_quote.scss b/stylesheets/_quote.scss
index 6e4a1db1d..deb769f76 100644
--- a/stylesheets/_quote.scss
+++ b/stylesheets/_quote.scss
@@ -131,15 +131,9 @@
 }
 
 .module-quote-container {
-  margin-inline-start: -6px;
-  margin-inline-end: -6px;
-  margin-top: -4px;
   margin-bottom: 5px;
-  padding-left: 5px;
-}
-
-.module-quote-container--with-content-above {
-  margin-top: 3px;
+  margin-top: 10px;
+  padding-left: 10px;
 }
 
 .module-quote--no-click {
diff --git a/stylesheets/_session.scss b/stylesheets/_session.scss
index 3c326ca54..b68506614 100644
--- a/stylesheets/_session.scss
+++ b/stylesheets/_session.scss
@@ -342,10 +342,6 @@ textarea {
 .module-message__container {
   position: relative;
   display: inline-block;
-  padding-inline-end: 10px;
-  padding-inline-start: 10px;
-  padding-top: 10px;
-  padding-bottom: 10px;
   overflow: hidden;
   min-width: 30px;
   // To limit messages with things forcing them wider, like long attachment names
@@ -355,12 +351,17 @@ textarea {
 label {
   user-select: none;
 }
+.module-message__attachment-container {
+  // Entirely to ensure that images are centered if they aren't full width of bubble
+  text-align: center;
+  position: relative;
+
+  border-radius: $session_message-container-border-radius;
+  overflow: hidden;
+  // no background by default for the attachment container
+}
 
-.module-message__attachment-container,
-.module-image--curved-bottom-right,
-.module-image--curved-top-left,
-.module-image--curved-top-right,
-.module-image--curved-bottom-left {
+.module-message__attachment-container {
   border-top-left-radius: $session_message-container-border-radius;
   border-top-right-radius: $session_message-container-border-radius;
   border-bottom-left-radius: $session_message-container-border-radius;
diff --git a/stylesheets/_theme_dark.scss b/stylesheets/_theme_dark.scss
index 63f0d71e7..cb9341bb9 100644
--- a/stylesheets/_theme_dark.scss
+++ b/stylesheets/_theme_dark.scss
@@ -207,11 +207,6 @@
     border: 1px solid $color-gray-60;
   }
 
-  .module-message__link-preview__content--with-content-above {
-    border-top: none;
-    border-bottom: none;
-  }
-
   .module-message__link-preview__title {
     color: $color-gray-05;
   }
@@ -220,24 +215,6 @@
     color: $color-gray-25;
   }
 
-  // Module: Embedded Contact
-
-  .module-embedded-contact__contact-name {
-    color: $color-dark-05;
-  }
-
-  .module-embedded-contact__contact-name--incoming {
-    color: $color-white;
-  }
-
-  .module-embedded-contact__contact-method {
-    color: $color-white-07;
-  }
-
-  .module-embedded-contact__contact-method--incoming {
-    color: $color-white-07;
-  }
-
   // Module: Contact Detail
 
   .module-contact-detail__send-message {
@@ -259,23 +236,6 @@
     color: $color-dark-30;
   }
 
-  // Module: Reset Session Notification
-
-  .module-reset-session-notification {
-    color: $color-dark-30;
-  }
-
-  .module-verification-notification__button {
-    color: $color-loki-green;
-    background-color: $color-gray-75;
-  }
-
-  // Module: Verification Notification
-
-  .module-verification-notification {
-    color: $color-dark-30;
-  }
-
   // Module: Timer Notification
 
   .module-timer-notification {
@@ -404,10 +364,6 @@
     background: none;
   }
 
-  .module-image__border-overlay {
-    box-shadow: inset 0px 0px 0px 1px $color-white-015;
-  }
-
   .module-image__loading-placeholder {
     background-color: $color-white-015;
   }
diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx
index 09b207052..ff41d5922 100644
--- a/ts/components/conversation/ConversationHeader.tsx
+++ b/ts/components/conversation/ConversationHeader.tsx
@@ -18,6 +18,7 @@ import {
   getSelectedMessageIds,
   isMessageDetailView,
   isMessageSelectionMode,
+  isRightPanelShowing,
 } from '../../state/selectors/conversations';
 import { useDispatch, useSelector } from 'react-redux';
 import { useMembersAvatars } from '../../hooks/useMembersAvatar';
@@ -25,6 +26,7 @@ import { useMembersAvatars } from '../../hooks/useMembersAvatar';
 import { deleteMessagesById } from '../../interactions/conversationInteractions';
 import {
   closeMessageDetailsView,
+  closeRightPanel,
   NotificationForConvoOption,
   openRightPanel,
   resetSelectedMessageIds,
@@ -210,6 +212,8 @@ export type ConversationHeaderTitleProps = {
 const ConversationHeaderTitle = () => {
   const headerTitleProps = useSelector(getConversationHeaderTitleProps);
   const notificationSetting = useSelector(getCurrentNotificationSettingText);
+  const isRightPanelOn = useSelector(isRightPanelShowing);
+  const dispatch = useDispatch();
   if (!headerTitleProps) {
     return null;
   }
@@ -257,7 +261,17 @@ const ConversationHeaderTitle = () => {
   const title = profileName || name || phoneNumber;
 
   return (
-    <div className="module-conversation-header__title">
+    <div
+      className="module-conversation-header__title"
+      onClick={() => {
+        if (isRightPanelOn) {
+          dispatch(closeRightPanel());
+        } else {
+          dispatch(openRightPanel());
+        }
+      }}
+      role="button"
+    >
       <span className="module-contact-name__profile-name">{title}</span>
       <StyledSubtitleContainer>
         <ConversationHeaderSubtitle text={fullTextSubtitle} />
diff --git a/ts/components/conversation/Image.tsx b/ts/components/conversation/Image.tsx
index e1c59b736..cdba596bf 100644
--- a/ts/components/conversation/Image.tsx
+++ b/ts/components/conversation/Image.tsx
@@ -17,12 +17,6 @@ type Props = {
 
   bottomOverlay?: boolean;
   closeButton?: boolean;
-  curveBottomLeft?: boolean;
-  curveBottomRight?: boolean;
-  curveTopLeft?: boolean;
-  curveTopRight?: boolean;
-
-  smallCurveTopLeft?: boolean;
 
   darkOverlay?: boolean;
   playIconOverlay?: boolean;
@@ -40,10 +34,6 @@ export const Image = (props: Props) => {
     attachment,
     bottomOverlay,
     closeButton,
-    curveBottomLeft,
-    curveBottomRight,
-    curveTopLeft,
-    curveTopRight,
     darkOverlay,
     height,
     onClick,
@@ -51,7 +41,6 @@ export const Image = (props: Props) => {
     onError,
     overlayText,
     playIconOverlay,
-    smallCurveTopLeft,
     softCorners,
     url,
     width,
@@ -83,11 +72,6 @@ export const Image = (props: Props) => {
       className={classNames(
         'module-image',
         canClick ? 'module-image__with-click-handler' : null,
-        curveBottomLeft ? 'module-image--curved-bottom-left' : null,
-        curveBottomRight ? 'module-image--curved-bottom-right' : null,
-        curveTopLeft ? 'module-image--curved-top-left' : null,
-        curveTopRight ? 'module-image--curved-top-right' : null,
-        smallCurveTopLeft ? 'module-image--small-curved-top-left' : null,
         softCorners ? 'module-image--soft-corners' : null
       )}
     >
@@ -125,11 +109,6 @@ export const Image = (props: Props) => {
       <div
         className={classNames(
           'module-image__border-overlay',
-          curveTopLeft ? 'module-image--curved-top-left' : null,
-          curveTopRight ? 'module-image--curved-top-right' : null,
-          curveBottomLeft ? 'module-image--curved-bottom-left' : null,
-          curveBottomRight ? 'module-image--curved-bottom-right' : null,
-          smallCurveTopLeft ? 'module-image--small-curved-top-left' : null,
           softCorners ? 'module-image--soft-corners' : null,
           darkOverlay ? 'module-image__border-overlay--dark' : null
         )}
@@ -146,15 +125,7 @@ export const Image = (props: Props) => {
           className="module-image__close-button"
         />
       ) : null}
-      {bottomOverlay ? (
-        <div
-          className={classNames(
-            'module-image__bottom-overlay',
-            curveBottomLeft ? 'module-image--curved-bottom-left' : null,
-            curveBottomRight ? 'module-image--curved-bottom-right' : null
-          )}
-        />
-      ) : null}
+      {bottomOverlay ? <div className={classNames('module-image__bottom-overlay')} /> : null}
       {!(pending || loading) && playIconOverlay ? (
         <div className="module-image__play-overlay__circle">
           <div className="module-image__play-overlay__icon" />
diff --git a/ts/components/conversation/ImageGrid.tsx b/ts/components/conversation/ImageGrid.tsx
index 3731506ee..51017533e 100644
--- a/ts/components/conversation/ImageGrid.tsx
+++ b/ts/components/conversation/ImageGrid.tsx
@@ -16,8 +16,6 @@ import { Image } from './Image';
 
 type Props = {
   attachments: Array<AttachmentTypeWithPath>;
-  withContentAbove?: boolean;
-  withContentBelow?: boolean;
   bottomOverlay?: boolean;
 
   onError: () => void;
@@ -26,23 +24,9 @@ type Props = {
 
 export const ImageGrid = (props: Props) => {
   // tslint:disable-next-line max-func-body-length */
-  const {
-    attachments,
-    bottomOverlay,
-    onError,
-    onClickAttachment,
-    withContentAbove,
-    withContentBelow,
-  } = props;
+  const { attachments, bottomOverlay, onError, onClickAttachment } = props;
 
-  const curveTopLeft = !Boolean(withContentAbove);
-  const curveTopRight = curveTopLeft;
-
-  const curveBottom = !Boolean(withContentBelow);
-  const curveBottomLeft = curveBottom;
-  const curveBottomRight = curveBottom;
-
-  const withBottomOverlay = Boolean(bottomOverlay && curveBottom);
+  const withBottomOverlay = Boolean(bottomOverlay);
 
   if (!attachments || !attachments.length) {
     return null;
@@ -56,10 +40,6 @@ export const ImageGrid = (props: Props) => {
         <Image
           alt={getAlt(attachments[0])}
           bottomOverlay={withBottomOverlay}
-          curveTopLeft={curveTopLeft}
-          curveTopRight={curveTopRight}
-          curveBottomLeft={curveBottomLeft}
-          curveBottomRight={curveBottomRight}
           attachment={attachments[0]}
           playIconOverlay={isVideoAttachment(attachments[0])}
           height={height}
@@ -79,8 +59,6 @@ export const ImageGrid = (props: Props) => {
           alt={getAlt(attachments[0])}
           attachment={attachments[0]}
           bottomOverlay={withBottomOverlay}
-          curveTopLeft={curveTopLeft}
-          curveBottomLeft={curveBottomLeft}
           playIconOverlay={isVideoAttachment(attachments[0])}
           height={149}
           width={149}
@@ -91,8 +69,6 @@ export const ImageGrid = (props: Props) => {
         <Image
           alt={getAlt(attachments[1])}
           bottomOverlay={withBottomOverlay}
-          curveTopRight={curveTopRight}
-          curveBottomRight={curveBottomRight}
           playIconOverlay={isVideoAttachment(attachments[1])}
           height={149}
           width={149}
@@ -111,8 +87,6 @@ export const ImageGrid = (props: Props) => {
         <Image
           alt={getAlt(attachments[0])}
           bottomOverlay={withBottomOverlay}
-          curveTopLeft={curveTopLeft}
-          curveBottomLeft={curveBottomLeft}
           attachment={attachments[0]}
           playIconOverlay={isVideoAttachment(attachments[0])}
           height={200}
@@ -124,7 +98,6 @@ export const ImageGrid = (props: Props) => {
         <div className="module-image-grid__column">
           <Image
             alt={getAlt(attachments[1])}
-            curveTopRight={curveTopRight}
             height={99}
             width={99}
             attachment={attachments[1]}
@@ -136,7 +109,6 @@ export const ImageGrid = (props: Props) => {
           <Image
             alt={getAlt(attachments[2])}
             bottomOverlay={withBottomOverlay}
-            curveBottomRight={curveBottomRight}
             height={99}
             width={99}
             attachment={attachments[2]}
@@ -157,7 +129,6 @@ export const ImageGrid = (props: Props) => {
           <div className="module-image-grid__row">
             <Image
               alt={getAlt(attachments[0])}
-              curveTopLeft={curveTopLeft}
               attachment={attachments[0]}
               playIconOverlay={isVideoAttachment(attachments[0])}
               height={149}
@@ -168,7 +139,6 @@ export const ImageGrid = (props: Props) => {
             />
             <Image
               alt={getAlt(attachments[1])}
-              curveTopRight={curveTopRight}
               playIconOverlay={isVideoAttachment(attachments[1])}
               height={149}
               width={149}
@@ -182,7 +152,6 @@ export const ImageGrid = (props: Props) => {
             <Image
               alt={getAlt(attachments[2])}
               bottomOverlay={withBottomOverlay}
-              curveBottomLeft={curveBottomLeft}
               playIconOverlay={isVideoAttachment(attachments[2])}
               height={149}
               width={149}
@@ -194,7 +163,6 @@ export const ImageGrid = (props: Props) => {
             <Image
               alt={getAlt(attachments[3])}
               bottomOverlay={withBottomOverlay}
-              curveBottomRight={curveBottomRight}
               playIconOverlay={isVideoAttachment(attachments[3])}
               height={149}
               width={149}
@@ -218,7 +186,6 @@ export const ImageGrid = (props: Props) => {
         <div className="module-image-grid__row">
           <Image
             alt={getAlt(attachments[0])}
-            curveTopLeft={curveTopLeft}
             attachment={attachments[0]}
             playIconOverlay={isVideoAttachment(attachments[0])}
             height={149}
@@ -229,7 +196,6 @@ export const ImageGrid = (props: Props) => {
           />
           <Image
             alt={getAlt(attachments[1])}
-            curveTopRight={curveTopRight}
             playIconOverlay={isVideoAttachment(attachments[1])}
             height={149}
             width={149}
@@ -243,7 +209,6 @@ export const ImageGrid = (props: Props) => {
           <Image
             alt={getAlt(attachments[2])}
             bottomOverlay={withBottomOverlay}
-            curveBottomLeft={curveBottomLeft}
             playIconOverlay={isVideoAttachment(attachments[2])}
             height={99}
             width={99}
@@ -266,7 +231,6 @@ export const ImageGrid = (props: Props) => {
           <Image
             alt={getAlt(attachments[4])}
             bottomOverlay={withBottomOverlay}
-            curveBottomRight={curveBottomRight}
             playIconOverlay={isVideoAttachment(attachments[4])}
             height={99}
             width={99}
diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx
index 38cf8ff0f..44478dc05 100644
--- a/ts/components/conversation/Message.tsx
+++ b/ts/components/conversation/Message.tsx
@@ -211,26 +211,13 @@ class MessageInner extends React.PureComponent<Props, State> {
 
   // tslint:disable-next-line max-func-body-length cyclomatic-complexity
   public renderAttachment() {
-    const {
-      id,
-      attachments,
-      text,
-      conversationType,
-      direction,
-      quote,
-      isTrustedForAttachmentDownload,
-    } = this.props;
+    const { id, attachments, direction, isTrustedForAttachmentDownload } = this.props;
     const { imageBroken } = this.state;
 
     if (!attachments || !attachments[0]) {
       return null;
     }
     const firstAttachment = attachments[0];
-
-    // For attachments which aren't full-frame
-    const withContentBelow = Boolean(text);
-    const withContentAbove =
-      Boolean(quote) || (conversationType === 'group' && direction === 'incoming');
     const displayImage = canDisplayImage(attachments);
 
     if (!isTrustedForAttachmentDownload) {
@@ -244,17 +231,9 @@ class MessageInner extends React.PureComponent<Props, State> {
         (isVideo(attachments) && hasVideoScreenshot(attachments)))
     ) {
       return (
-        <div
-          className={classNames(
-            'module-message__attachment-container',
-            withContentAbove ? 'module-message__attachment-container--with-content-above' : null,
-            withContentBelow ? 'module-message__attachment-container--with-content-below' : null
-          )}
-        >
+        <div className={classNames('module-message__attachment-container')}>
           <ImageGrid
             attachments={attachments}
-            withContentAbove={withContentAbove}
-            withContentBelow={withContentBelow}
             onError={this.handleImageError}
             onClickAttachment={this.onClickOnImageGrid}
           />
@@ -281,13 +260,7 @@ class MessageInner extends React.PureComponent<Props, State> {
       const isDangerous = isFileDangerous(fileName || '');
 
       return (
-        <div
-          className={classNames(
-            'module-message__generic-attachment',
-            withContentBelow ? 'module-message__generic-attachment--with-content-below' : null,
-            withContentAbove ? 'module-message__generic-attachment--with-content-above' : null
-          )}
-        >
+        <div className={classNames('module-message__generic-attachment')}>
           {pending ? (
             <div className="module-message__generic-attachment__spinner-container">
               <Spinner size="small" direction={direction} />
@@ -337,7 +310,7 @@ class MessageInner extends React.PureComponent<Props, State> {
 
   // tslint:disable-next-line cyclomatic-complexity
   public renderPreview() {
-    const { attachments, conversationType, direction, previews, quote } = this.props;
+    const { attachments, previews } = this.props;
 
     // Attachments take precedence over Link Previews
     if (attachments && attachments.length) {
@@ -353,41 +326,19 @@ class MessageInner extends React.PureComponent<Props, State> {
       return null;
     }
 
-    const withContentAbove =
-      Boolean(quote) || (conversationType === 'group' && direction === 'incoming');
-
     const previewHasImage = first.image && isImageAttachment(first.image);
     const width = first.image && first.image.width;
     const isFullSizeImage = width && width >= MINIMUM_LINK_PREVIEW_IMAGE_WIDTH;
 
     return (
-      <div
-        role="button"
-        className={classNames(
-          'module-message__link-preview',
-          withContentAbove ? 'module-message__link-preview--with-content-above' : null
-        )}
-      >
+      <div role="button" className={classNames('module-message__link-preview')}>
         {first.image && previewHasImage && isFullSizeImage ? (
-          <ImageGrid
-            attachments={[first.image]}
-            withContentAbove={withContentAbove}
-            withContentBelow={true}
-            onError={this.handleImageError}
-          />
+          <ImageGrid attachments={[first.image]} onError={this.handleImageError} />
         ) : null}
-        <div
-          className={classNames(
-            'module-message__link-preview__content',
-            withContentAbove || isFullSizeImage
-              ? 'module-message__link-preview__content--with-content-above'
-              : null
-          )}
-        >
+        <div className={classNames('module-message__link-preview__content')}>
           {first.image && previewHasImage && !isFullSizeImage ? (
             <div className="module-message__link-preview__image_container">
               <Image
-                smallCurveTopLeft={!withContentAbove}
                 softCorners={true}
                 alt={window.i18n('previewThumbnail', [first.domain])}
                 height={72}
@@ -423,13 +374,11 @@ class MessageInner extends React.PureComponent<Props, State> {
   }
 
   public renderQuote() {
-    const { conversationType, direction, quote, isPublic, convoId } = this.props;
+    const { direction, quote } = this.props;
 
     if (!quote || !quote.authorPhoneNumber || !quote.messageId) {
       return null;
     }
-    const withContentAbove = conversationType === 'group' && direction === 'incoming';
-
     const shortenedPubkey = PubKey.shorten(quote.authorPhoneNumber);
 
     const displayedPubkey = quote.authorProfileName ? shortenedPubkey : quote.authorPhoneNumber;
@@ -440,15 +389,11 @@ class MessageInner extends React.PureComponent<Props, State> {
         text={quote.text}
         attachment={quote.attachment}
         isIncoming={direction === 'incoming'}
-        conversationType={conversationType}
-        convoId={convoId}
-        isPublic={isPublic}
         authorPhoneNumber={displayedPubkey}
         authorProfileName={quote.authorProfileName}
         authorName={quote.authorName}
         referencedMessageNotFound={quote.referencedMessageNotFound}
         isFromMe={quote.isFromMe}
-        withContentAbove={withContentAbove}
       />
     );
   }
@@ -538,6 +483,18 @@ class MessageInner extends React.PureComponent<Props, State> {
     return <OutgoingMessageStatus status={status} />;
   }
 
+  public renderExpireTimer(isCorrectSide: boolean) {
+    const { expirationLength, expirationTimestamp } = this.props;
+
+    if (!(isCorrectSide && expirationLength && expirationTimestamp)) {
+      return null;
+    }
+
+    return (
+      <ExpireTimer expirationLength={expirationLength} expirationTimestamp={expirationTimestamp} />
+    );
+  }
+
   public getWidth(): number | undefined {
     const { attachments, previews } = this.props;
 
@@ -600,8 +557,7 @@ class MessageInner extends React.PureComponent<Props, State> {
     return false;
   }
 
-  // tslint:disable-next-line: cyclomatic-complexity
-  // tslint:disable-next-line: max-func-body-length
+  // tslint:disable-next-line: cyclomatic-complexity cyclomatic-complexity
   public render() {
     const {
       direction,
@@ -610,142 +566,122 @@ class MessageInner extends React.PureComponent<Props, State> {
       selectedMessages,
       receivedAt,
       isUnread,
-      text,
-      timestamp,
-      serverTimestamp,
-      expirationLength,
-      expirationTimestamp,
-      firstMessageOfSeries,
-      lastMessageOfSeries,
     } = this.props;
-    const { expired, expiring } = this.state;
+    const { expired } = this.state;
 
     if (expired) {
       return null;
     }
 
     const selected = selectedMessages.includes(messageId) || false;
-
-    const width = this.getWidth();
-    const isShowingImage = this.isShowingImage();
-
-    const divClasses = ['session-message-wrapper'];
-
-    if (selected) {
-      divClasses.push('message-selected');
-    }
-
-    if (conversationType === 'group') {
-      divClasses.push('public-chat-message-wrapper');
-    }
-
-    if (this.props.quotedMessageToAnimate === messageId) {
-      divClasses.push('flash-green-once');
-    }
-
+    const isGroup = conversationType === 'group';
+    const isQuotedMessageToAnimate = this.props.quotedMessageToAnimate === messageId;
     const isIncoming = direction === 'incoming';
 
-    if (isIncoming) {
-      divClasses.push('session-message-wrapper-incoming');
-    } else {
-      divClasses.push('session-message-wrapper-outgoing');
-    }
-
-    const hasText = Boolean(text);
-
-    const bgShouldBeTransparent = isShowingImage && !hasText;
-    const toolTipTitle = moment(serverTimestamp || timestamp).format('llll');
-
     return (
       <ReadableMessage
         messageId={messageId}
-        className={classNames(divClasses)}
+        className={classNames(
+          'session-message-wrapper',
+          selected && 'message-selected',
+          isGroup && 'public-chat-message-wrapper',
+          isQuotedMessageToAnimate && 'flash-green-once',
+          isIncoming ? 'session-message-wrapper-incoming' : 'session-message-wrapper-outgoing'
+        )}
         onContextMenu={this.handleContextMenu}
         receivedAt={receivedAt}
         isUnread={isUnread}
         key={`readable-message-${messageId}`}
       >
         {this.renderAvatar()}
-        {!isIncoming && expirationLength && expirationTimestamp ? (
-          <ExpireTimer
-            expirationLength={expirationLength}
-            expirationTimestamp={expirationTimestamp}
-          />
-        ) : null}
-        <div
-          className={classNames(
-            'module-message',
-            `module-message--${direction}`,
-            expiring ? 'module-message--expired' : null
-          )}
-          role="button"
-          onClick={this.onClickOnMessageOuterContainer}
-        >
-          {this.renderStatus(isIncoming)}
-          <Flex container={true} flexDirection="column">
-            <MessageAuthorText
-              authorName={this.props.authorName}
-              authorPhoneNumber={this.props.authorPhoneNumber}
-              authorProfileName={this.props.authorProfileName}
-              conversationType={this.props.conversationType}
-              direction={this.props.direction}
-              firstMessageOfSeries={this.props.firstMessageOfSeries}
-              isPublic={this.props.isPublic}
-            />
+        {this.renderExpireTimer(!isIncoming)}
+        {this.renderMessageContentWithStatuses()}
+        {this.renderExpireTimer(isIncoming)}
+      </ReadableMessage>
+    );
+  }
 
-            <div
-              className={classNames(
-                'module-message__container',
-                `module-message__container--${direction}`,
-                bgShouldBeTransparent
-                  ? `module-message__container--${direction}--transparent`
-                  : `module-message__container--${direction}--opaque`,
-                firstMessageOfSeries
-                  ? `module-message__container--${direction}--first-of-series`
-                  : '',
-                lastMessageOfSeries ? `module-message__container--${direction}--last-of-series` : ''
-              )}
-              style={{
-                width: isShowingImage ? width : undefined,
-              }}
-              role="button"
-              onClick={this.onClickOnMessageInnerContainer}
-              title={toolTipTitle}
-            >
-              {this.renderQuote()}
-              {this.renderAttachment()}
-              {this.renderPreview()}
-              {this.renderText()}
-            </div>
-          </Flex>
-          {this.renderStatus(!isIncoming)}
+  private renderMessageContentWithStatuses() {
+    const { expiring } = this.state;
+    const { direction } = this.props;
+    const isIncoming = direction === 'incoming';
 
-          <MessageContextMenu
+    return (
+      <div
+        className={classNames(
+          'module-message',
+          `module-message--${direction}`,
+          expiring ? 'module-message--expired' : null
+        )}
+        role="button"
+        onClick={this.onClickOnMessageOuterContainer}
+      >
+        {this.renderStatus(isIncoming)}
+        <Flex container={true} flexDirection="column">
+          <MessageAuthorText
+            authorName={this.props.authorName}
             authorPhoneNumber={this.props.authorPhoneNumber}
-            convoId={this.props.convoId}
-            contextMenuId={this.ctxMenuID}
+            authorProfileName={this.props.authorProfileName}
             direction={this.props.direction}
-            isBlocked={this.props.isBlocked}
-            isDeletable={this.props.isDeletable}
-            messageId={this.props.id}
-            text={this.props.text}
-            timestamp={this.props.timestamp}
-            serverTimestamp={this.props.serverTimestamp}
-            attachments={this.props.attachments}
-            isAdmin={this.props.isSenderAdmin}
-            isOpenGroupV2={this.props.isOpenGroupV2}
-            isPublic={this.props.isPublic}
-            status={this.props.status}
-            weAreAdmin={this.props.weAreAdmin}
-          />
-        </div>
-        {isIncoming && expirationLength && expirationTimestamp ? (
-          <ExpireTimer
-            expirationLength={expirationLength}
-            expirationTimestamp={expirationTimestamp}
+            firstMessageOfSeries={this.props.firstMessageOfSeries}
           />
+
+          {this.renderMessageContent()}
+        </Flex>
+        {this.renderStatus(!isIncoming)}
+
+        {this.renderContextMenu()}
+      </div>
+    );
+  }
+
+  private renderMessageContent() {
+    const {
+      direction,
+      text,
+      timestamp,
+      serverTimestamp,
+      firstMessageOfSeries,
+      lastMessageOfSeries,
+    } = this.props;
+
+    const width = this.getWidth();
+    const isShowingImage = this.isShowingImage();
+    const hasText = Boolean(text);
+    const hasQuote = !_.isEmpty(this.props.quote);
+    const hasContentAfterAttachmentAndQuote =
+      !_.isEmpty(this.props.previews) || !_.isEmpty(this.props.text);
+
+    const bgShouldBeTransparent = isShowingImage && !hasText && !hasQuote;
+    const toolTipTitle = moment(serverTimestamp || timestamp).format('llll');
+
+    return (
+      <div
+        className={classNames(
+          'module-message__container',
+          `module-message__container--${direction}`,
+          bgShouldBeTransparent
+            ? `module-message__container--${direction}--transparent`
+            : `module-message__container--${direction}--opaque`,
+          firstMessageOfSeries ? `module-message__container--${direction}--first-of-series` : '',
+          lastMessageOfSeries ? `module-message__container--${direction}--last-of-series` : ''
+        )}
+        style={{
+          width: isShowingImage ? width : undefined,
+        }}
+        role="button"
+        onClick={this.onClickOnMessageInnerContainer}
+        title={toolTipTitle}
+      >
+        {this.renderQuote()}
+        {this.renderAttachment()}
+        {hasContentAfterAttachmentAndQuote ? (
+          <Flex padding="5px 5px 10px 5px">
+            {this.renderPreview()}
+            {this.renderText()}
+          </Flex>
         ) : null}
-      </ReadableMessage>
+      </div>
     );
   }
 
@@ -765,6 +701,29 @@ class MessageInner extends React.PureComponent<Props, State> {
     }
   }
 
+  private renderContextMenu() {
+    return (
+      <MessageContextMenu
+        authorPhoneNumber={this.props.authorPhoneNumber}
+        convoId={this.props.convoId}
+        contextMenuId={this.ctxMenuID}
+        direction={this.props.direction}
+        isBlocked={this.props.isBlocked}
+        isDeletable={this.props.isDeletable}
+        messageId={this.props.id}
+        text={this.props.text}
+        timestamp={this.props.timestamp}
+        serverTimestamp={this.props.serverTimestamp}
+        attachments={this.props.attachments}
+        isAdmin={this.props.isSenderAdmin}
+        isOpenGroupV2={this.props.isOpenGroupV2}
+        isPublic={this.props.isPublic}
+        status={this.props.status}
+        weAreAdmin={this.props.weAreAdmin}
+      />
+    );
+  }
+
   private onQuoteClick(e: any) {
     const { quote, multiSelectMode, id } = this.props;
     e.preventDefault();
diff --git a/ts/components/conversation/Quote.tsx b/ts/components/conversation/Quote.tsx
index ca43f2dee..d6d3ce1c2 100644
--- a/ts/components/conversation/Quote.tsx
+++ b/ts/components/conversation/Quote.tsx
@@ -1,6 +1,6 @@
 // tslint:disable:react-this-binding-issue
 
-import React, { useCallback } from 'react';
+import React, { useCallback, useState } from 'react';
 import classNames from 'classnames';
 
 import * as MIME from '../../../ts/types/MIME';
@@ -9,9 +9,15 @@ import * as GoogleChrome from '../../../ts/util/GoogleChrome';
 import { MessageBody } from './MessageBody';
 import { ContactName } from './ContactName';
 import { PubKey } from '../../session/types';
-import { ConversationTypeEnum } from '../../models/conversation';
 
 import { useEncryptedFileFetch } from '../../hooks/useEncryptedFileFetch';
+import { useSelector } from 'react-redux';
+import {
+  getSelectedConversationKey,
+  isGroupConversation,
+  isPublicGroupConversation,
+} from '../../state/selectors/conversations';
+import { noop } from 'underscore';
 
 export type QuotePropsWithoutListener = {
   attachment?: QuotedAttachmentType;
@@ -20,10 +26,6 @@ export type QuotePropsWithoutListener = {
   authorName?: string;
   isFromMe: boolean;
   isIncoming: boolean;
-  conversationType: ConversationTypeEnum;
-  convoId: string;
-  isPublic?: boolean;
-  withContentAbove: boolean;
   text: string | null;
   referencedMessageNotFound: boolean;
 };
@@ -107,7 +109,12 @@ export const QuoteIcon = (props: any) => {
   );
 };
 
-export const QuoteImage = (props: any) => {
+export const QuoteImage = (props: {
+  handleImageErrorBound: () => void;
+  url: string;
+  contentType: string;
+  icon?: string;
+}) => {
   const { url, icon, contentType, handleImageErrorBound } = props;
 
   const { loading, urlToLoad } = useEncryptedFileFetch(url, contentType);
@@ -144,7 +151,9 @@ export const QuoteImage = (props: any) => {
   );
 };
 
-export const QuoteGenericFile = (props: any) => {
+export const QuoteGenericFile = (
+  props: Pick<QuotePropsWithoutListener, 'attachment' | 'isIncoming'>
+) => {
   const { attachment, isIncoming } = props;
 
   if (!attachment) {
@@ -176,7 +185,12 @@ export const QuoteGenericFile = (props: any) => {
   );
 };
 
-export const QuoteIconContainer = (props: any) => {
+export const QuoteIconContainer = (
+  props: Pick<QuotePropsWithoutListener, 'attachment'> & {
+    handleImageErrorBound: () => void;
+    imageBroken: boolean;
+  }
+) => {
   const { attachment, imageBroken, handleImageErrorBound } = props;
 
   if (!attachment) {
@@ -188,7 +202,12 @@ export const QuoteIconContainer = (props: any) => {
 
   if (GoogleChrome.isVideoTypeSupported(contentType)) {
     return objectUrl && !imageBroken ? (
-      <QuoteImage url={objectUrl} icon={'play'} />
+      <QuoteImage
+        url={objectUrl}
+        contentType={MIME.IMAGE_JPEG}
+        icon="play"
+        handleImageErrorBound={noop}
+      />
     ) : (
       <QuoteIcon icon="movie" />
     );
@@ -210,9 +229,12 @@ export const QuoteIconContainer = (props: any) => {
   return null;
 };
 
-export const QuoteText = (props: any) => {
-  const { text, attachment, isIncoming, conversationType, convoId } = props;
-  const isGroup = conversationType === ConversationTypeEnum.GROUP;
+export const QuoteText = (
+  props: Pick<QuotePropsWithoutListener, 'text' | 'attachment' | 'isIncoming'>
+) => {
+  const { text, attachment, isIncoming } = props;
+  const isGroup = useSelector(isGroupConversation);
+  const convoId = useSelector(getSelectedConversationKey);
 
   if (text) {
     return (
@@ -285,7 +307,9 @@ const QuoteAuthor = (props: QuoteAuthorProps) => {
   );
 };
 
-export const QuoteReferenceWarning = (props: any) => {
+export const QuoteReferenceWarning = (
+  props: Pick<QuotePropsWithoutListener, 'isIncoming' | 'referencedMessageNotFound'>
+) => {
   const { isIncoming, referencedMessageNotFound } = props;
 
   if (!referencedMessageNotFound) {
@@ -318,21 +342,21 @@ export const QuoteReferenceWarning = (props: any) => {
 };
 
 export const Quote = (props: QuotePropsWithListener) => {
-  const handleImageErrorBound = null;
+  const [imageBroken, setImageBroken] = useState(false);
+  const handleImageErrorBound = () => {
+    setImageBroken(true);
+  };
 
-  const { isIncoming, onClick, referencedMessageNotFound, withContentAbove } = props;
+  const isPublic = useSelector(isPublicGroupConversation);
 
   if (!validateQuote(props)) {
     return null;
   }
 
+  const { isIncoming, referencedMessageNotFound, attachment, text, onClick } = props;
+
   return (
-    <div
-      className={classNames(
-        'module-quote-container',
-        withContentAbove ? 'module-quote-container--with-content-above' : null
-      )}
-    >
+    <div className={classNames('module-quote-container')}>
       <div
         onClick={onClick}
         role="button"
@@ -340,7 +364,6 @@ export const Quote = (props: QuotePropsWithListener) => {
           'module-quote',
           isIncoming ? 'module-quote--incoming' : 'module-quote--outgoing',
           !onClick ? 'module-quote--no-click' : null,
-          withContentAbove ? 'module-quote--with-content-above' : null,
           referencedMessageNotFound ? 'module-quote--with-reference-warning' : null
         )}
       >
@@ -351,14 +374,21 @@ export const Quote = (props: QuotePropsWithListener) => {
             authorProfileName={props.authorProfileName}
             isFromMe={props.isFromMe}
             isIncoming={props.isIncoming}
-            showPubkeyForAuthor={props.isPublic}
+            showPubkeyForAuthor={isPublic}
           />
           <QuoteGenericFile {...props} />
-          <QuoteText {...props} />
+          <QuoteText isIncoming={isIncoming} text={text} attachment={attachment} />
         </div>
-        <QuoteIconContainer {...props} handleImageErrorBound={handleImageErrorBound} />
+        <QuoteIconContainer
+          attachment={attachment}
+          handleImageErrorBound={handleImageErrorBound}
+          imageBroken={imageBroken}
+        />
       </div>
-      <QuoteReferenceWarning {...props} />
+      <QuoteReferenceWarning
+        isIncoming={isIncoming}
+        referencedMessageNotFound={referencedMessageNotFound}
+      />
     </div>
   );
 };
diff --git a/ts/components/conversation/message/MessageAuthorText.tsx b/ts/components/conversation/message/MessageAuthorText.tsx
index 758c4a970..46b331ca0 100644
--- a/ts/components/conversation/message/MessageAuthorText.tsx
+++ b/ts/components/conversation/message/MessageAuthorText.tsx
@@ -1,7 +1,11 @@
 import React from 'react';
-import { ConversationTypeEnum } from '../../../models/conversation';
+import { useSelector } from 'react-redux';
 import { MessageModelType } from '../../../models/messageType';
 import { PubKey } from '../../../session/types/PubKey';
+import {
+  isGroupConversation,
+  isPublicGroupConversation,
+} from '../../../state/selectors/conversations';
 import { Flex } from '../../basic/Flex';
 import { ContactName } from '../ContactName';
 
@@ -9,9 +13,7 @@ export type MessageAuthorProps = {
   authorName: string | null;
   authorProfileName: string | null;
   authorPhoneNumber: string;
-  conversationType: ConversationTypeEnum;
   direction: MessageModelType;
-  isPublic: boolean;
   firstMessageOfSeries: boolean;
 };
 
@@ -20,15 +22,16 @@ export const MessageAuthorText = (props: MessageAuthorProps) => {
     authorName,
     authorPhoneNumber,
     authorProfileName,
-    conversationType,
     direction,
-    isPublic,
     firstMessageOfSeries,
   } = props;
 
+  const isPublic = useSelector(isPublicGroupConversation);
+  const isGroup = useSelector(isGroupConversation);
+
   const title = authorName ? authorName : authorPhoneNumber;
 
-  if (direction !== 'incoming' || conversationType !== 'group' || !title || !firstMessageOfSeries) {
+  if (direction !== 'incoming' || !isGroup || !title || !firstMessageOfSeries) {
     return null;
   }
 
@@ -37,17 +40,15 @@ export const MessageAuthorText = (props: MessageAuthorProps) => {
   const displayedPubkey = authorProfileName ? shortenedPubkey : authorPhoneNumber;
 
   return (
-    <div className="module-message__author">
-      <Flex container={true}>
-        <ContactName
-          phoneNumber={displayedPubkey}
-          name={authorName}
-          profileName={authorProfileName}
-          module="module-message__author"
-          boldProfileName={true}
-          shouldShowPubkey={Boolean(isPublic)}
-        />
-      </Flex>
-    </div>
+    <Flex container={true}>
+      <ContactName
+        phoneNumber={displayedPubkey}
+        name={authorName}
+        profileName={authorProfileName}
+        module="module-message__author"
+        boldProfileName={true}
+        shouldShowPubkey={Boolean(isPublic)}
+      />
+    </Flex>
   );
 };
diff --git a/ts/components/session/conversation/SessionQuotedMessageComposition.tsx b/ts/components/session/conversation/SessionQuotedMessageComposition.tsx
index eea2b1100..2b8b93354 100644
--- a/ts/components/session/conversation/SessionQuotedMessageComposition.tsx
+++ b/ts/components/session/conversation/SessionQuotedMessageComposition.tsx
@@ -93,10 +93,6 @@ export const SessionQuotedMessageComposition = () => {
               attachment={firstImageAttachment}
               height={100}
               width={100}
-              curveTopLeft={true}
-              curveTopRight={true}
-              curveBottomLeft={true}
-              curveBottomRight={true}
               url={firstImageAttachment.thumbnail.objectUrl}
             />
           )}
diff --git a/ts/components/session/settings/SessionSettings.tsx b/ts/components/session/settings/SessionSettings.tsx
index 59459f019..4fde93db9 100644
--- a/ts/components/session/settings/SessionSettings.tsx
+++ b/ts/components/session/settings/SessionSettings.tsx
@@ -363,6 +363,7 @@ class SettingsViewInner extends React.Component<SettingsViewProps, State> {
                 title: window.i18n('linkPreviewsTitle'),
                 message: window.i18n('linkPreviewsConfirmMessage'),
                 okTheme: SessionButtonColor.Danger,
+                // onClickOk:
               })
             );
           }
diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts
index 8d423b90a..cb3fe5ac2 100644
--- a/ts/state/selectors/conversations.ts
+++ b/ts/state/selectors/conversations.ts
@@ -48,6 +48,37 @@ export const getSelectedConversation = createSelector(
   }
 );
 
+/**
+ * Returns true if the current conversation selected is a group conversation.
+ * Returns false if the current conversation selected is not a group conversation, or none are selected
+ */
+export const isGroupConversation = createSelector(
+  getSelectedConversation,
+  (state: ReduxConversationType | undefined): boolean => {
+    return state?.type === 'group' || false;
+  }
+);
+
+/**
+ * Returns true if the current conversation selected is a closed group and false otherwise.
+ */
+export const isClosedGroupConversation = createSelector(
+  getSelectedConversation,
+  (state: ReduxConversationType | undefined): boolean => {
+    return (state?.type === 'group' && !state.isPublic) || false;
+  }
+);
+
+/**
+ * Returns true if the current conversation selected is a public group and false otherwise.
+ */
+export const isPublicGroupConversation = createSelector(
+  getSelectedConversation,
+  (state: ReduxConversationType | undefined): boolean => {
+    return (state?.type === 'group' && state.isPublic) || false;
+  }
+);
+
 export const getOurPrimaryConversation = createSelector(
   getConversations,
   (state: ConversationsStateType): ReduxConversationType =>