diff --git a/js/views/create_group_dialog_view.js b/js/views/create_group_dialog_view.js
index 8e7a841e2..df542b021 100644
--- a/js/views/create_group_dialog_view.js
+++ b/js/views/create_group_dialog_view.js
@@ -62,12 +62,14 @@
       return this;
     },
     onSubmit(groupName, avatar) {
-      window.MediumGroups.initiateGroupUpdate(
-        this.groupId,
-        groupName,
-        this.members,
-        avatar
-      );
+      if(groupName !== this.groupName || avatar !== this.avatarPath) {
+        window.MediumGroups.initiateGroupUpdate(
+          this.groupId,
+          groupName,
+          this.members,
+          avatar
+        );
+      }
     },
     close() {
       this.remove();
diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts
index c5f9463b5..eb3fed81b 100644
--- a/ts/receiver/contentMessage.ts
+++ b/ts/receiver/contentMessage.ts
@@ -4,13 +4,12 @@ import { getEnvelopeId } from './common';
 
 import { removeFromCache, updateCache } from './cache';
 import { SignalService } from '../protobuf';
-import { toNumber } from 'lodash';
+import * as Lodash from 'lodash';
 import * as libsession from '../session';
 import { handleSessionRequestMessage } from './sessionHandling';
 import { handlePairingAuthorisationMessage } from './multidevice';
 import {
   MediumGroupRequestKeysMessage,
-  ReceiptMessage,
 } from '../session/messages/outgoing';
 import { MultiDeviceProtocol, SessionProtocol } from '../session/protocols';
 import { PubKey } from '../session/types';
@@ -20,6 +19,7 @@ import { onError } from './errors';
 import ByteBuffer from 'bytebuffer';
 import { BlockedNumberController } from '../util/blockedNumberController';
 import { decryptWithSenderKey } from '../session/medium_group/ratchet';
+import { StringUtils } from '../session/utils';
 
 export async function handleContentMessage(envelope: EnvelopePlus) {
   const plaintext = await decrypt(envelope, envelope.content);
@@ -188,12 +188,6 @@ async function decryptUnidentifiedSender(
     envelope.type = SignalService.Envelope.Type.FALLBACK_MESSAGE;
   }
 
-  const blocked = await isBlocked(sender.getName());
-  if (blocked) {
-    window.log.info('Dropping blocked message after sealed sender decryption');
-    return null;
-  }
-
   // Here we take this sender information and attach it back to the envelope
   //   to make the rest of the app work properly.
 
@@ -336,6 +330,50 @@ async function decrypt(
   }
 }
 
+function shouldDropBlockedUserMessage(content: SignalService.Content): boolean {
+  // Even if the user is blocked, we should allow the message if:
+  //   - it is a group message AND
+  //   - the group exists already on the db (to not join a closed group created by a blocked user) AND
+  //   - the group is not blocked AND
+  //   - the message is only control (no body/attachments/quote/groupInvitation/contact/preview)
+
+  if (!content?.dataMessage?.group?.id) {
+    return true;
+  }
+  const groupId = StringUtils.decode(content.dataMessage.group.id, 'utf8');
+
+  const groupConvo = window.ConversationController.get(groupId);
+  if (!groupConvo) {
+    return true;
+  }
+
+  if (groupConvo.isBlocked()) {
+    return true;
+  }
+
+  // first check that dataMessage is the only field set in the Content
+  let msgWithoutDataMessage = Lodash.pickBy(
+    content,
+    (_, key) => key !== 'dataMessage' && key !== 'toJSON'
+  );
+  msgWithoutDataMessage = Lodash.pickBy(msgWithoutDataMessage, Lodash.identity);
+
+  const isMessageDataMessageOnly = Lodash.isEmpty(msgWithoutDataMessage);
+  if (!isMessageDataMessageOnly) {
+    return true;
+  }
+  const data = content.dataMessage;
+  const isControlDataMessageOnly =
+    !data.body &&
+    !data.contact?.length &&
+    !data.preview?.length &&
+    !data.attachments?.length &&
+    !data.groupInvitation &&
+    !data.quote;
+
+  return !isControlDataMessageOnly;
+}
+
 export async function innerHandleContentMessage(
   envelope: EnvelopePlus,
   plaintext: ArrayBuffer
@@ -344,6 +382,17 @@ export async function innerHandleContentMessage(
 
   const content = SignalService.Content.decode(new Uint8Array(plaintext));
 
+  const blocked = await isBlocked(envelope.source);
+  if (blocked) {
+    // We want to allow a blocked user message if that's a control message for a known group and the group is not blocked
+    if (shouldDropBlockedUserMessage(content)) {
+      window.log.info('Dropping blocked user message');
+      return;
+    } else {
+      window.log.info('Allowing group-control message only from blocked user');
+    }
+  }
+
   const { FALLBACK_MESSAGE } = SignalService.Envelope.Type;
 
   await ConversationController.getOrCreateAndWait(envelope.source, 'private');
@@ -451,14 +500,14 @@ async function handleReceiptMessage(
   const results = [];
   if (type === SignalService.ReceiptMessage.Type.DELIVERY) {
     for (const ts of timestamp) {
-      const promise = onDeliveryReceipt(envelope.source, toNumber(ts));
+      const promise = onDeliveryReceipt(envelope.source, Lodash.toNumber(ts));
       results.push(promise);
     }
   } else if (type === SignalService.ReceiptMessage.Type.READ) {
     for (const ts of timestamp) {
       const promise = onReadReceipt(
-        toNumber(envelope.timestamp),
-        toNumber(ts),
+        Lodash.toNumber(envelope.timestamp),
+        Lodash.toNumber(ts),
         envelope.source
       );
       results.push(promise);
@@ -494,8 +543,8 @@ async function handleTypingMessage(
   await removeFromCache(envelope);
 
   if (envelope.timestamp && timestamp) {
-    const envelopeTimestamp = toNumber(envelope.timestamp);
-    const typingTimestamp = toNumber(timestamp);
+    const envelopeTimestamp = Lodash.toNumber(envelope.timestamp);
+    const typingTimestamp = Lodash.toNumber(timestamp);
 
     if (typingTimestamp !== envelopeTimestamp) {
       window.log.warn(