diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/sharing/BlogSharingViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/sharing/BlogSharingViewModel.kt
index 9cfbc2b6f6229ad525777d52ac99b1456aca4c3d..9d9dbf201f20cd3b61bdeefc8f74bfc0eb69fb30 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/sharing/BlogSharingViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/blog/sharing/BlogSharingViewModel.kt
@@ -22,6 +22,7 @@ import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.mutableStateOf
 import mu.KotlinLogging
 import org.briarproject.bramble.api.connection.ConnectionRegistry
+import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.db.Transaction
 import org.briarproject.bramble.api.db.TransactionManager
@@ -30,12 +31,14 @@ import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent
 import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.briar.api.blog.BlogManager
 import org.briarproject.briar.api.blog.BlogSharingManager
 import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.identity.AuthorManager
+import org.briarproject.briar.api.sharing.SharingManager
 import org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHAREABLE
 import org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHARING
 import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent
@@ -69,6 +72,8 @@ class BlogSharingViewModel @Inject constructor(
     eventBus,
 ) {
 
+    override val clientId: ClientId = BlogSharingManager.CLIENT_ID
+
     companion object {
         private val LOG = KotlinLogging.logger {}
     }
@@ -138,6 +143,13 @@ class BlogSharingViewModel @Inject constructor(
         }
     }
 
+    override fun getSharingStatusForContact(
+        txn: Transaction,
+        groupId: GroupId,
+        contact: Contact,
+    ): SharingManager.SharingStatus =
+        blogSharingManager.getSharingStatus(txn, groupId, contact)
+
     private fun loadSharingStatus(txn: Transaction, groupId: GroupId) {
         val map = contactManager.getContacts(txn).associate { contact ->
             contact.id to blogSharingManager.getSharingStatus(txn, groupId, contact)
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt
index 773c13f4a58e66c2528653aafff1156c353df907..bca0cc4054064aa98a5372c9fb7fb2017c0b05d2 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forum/sharing/ForumSharingViewModel.kt
@@ -21,6 +21,7 @@ package org.briarproject.briar.desktop.forum.sharing
 import androidx.compose.runtime.derivedStateOf
 import mu.KotlinLogging
 import org.briarproject.bramble.api.connection.ConnectionRegistry
+import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.db.Transaction
 import org.briarproject.bramble.api.db.TransactionManager
@@ -29,11 +30,13 @@ import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent
 import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.forum.ForumSharingManager
 import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent
 import org.briarproject.briar.api.identity.AuthorManager
+import org.briarproject.briar.api.sharing.SharingManager
 import org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHAREABLE
 import org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHARING
 import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent
@@ -69,10 +72,13 @@ class ForumSharingViewModel @Inject constructor(
         private val LOG = KotlinLogging.logger {}
     }
 
+    override val clientId: ClientId = ForumSharingManager.CLIENT_ID
+
     val currentlySharedWith = derivedStateOf {
         _contactList.filter { _sharingStatus.value[it.id] == SHARING }
     }
 
+    @UiExecutor
     override fun reload() {
         _shareableSelected.value = emptySet()
         _sharingMessage.value = ""
@@ -129,6 +135,13 @@ class ForumSharingViewModel @Inject constructor(
         }
     }
 
+    override fun getSharingStatusForContact(
+        txn: Transaction,
+        groupId: GroupId,
+        contact: Contact,
+    ): SharingManager.SharingStatus =
+        forumSharingManager.getSharingStatus(txn, groupId, contact)
+
     private fun loadSharingStatus(txn: Transaction, groupId: GroupId) {
         val map = contactManager.getContacts(txn).associate { contact ->
             contact.id to forumSharingManager.getSharingStatus(txn, groupId, contact)
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/sharing/PrivateGroupSharingViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/sharing/PrivateGroupSharingViewModel.kt
index 7dea7c6da11dd80db10c609309c8b366324e4d27..d56919e140f59f07d30b8ad86779b8bee61b0737 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/sharing/PrivateGroupSharingViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/privategroup/sharing/PrivateGroupSharingViewModel.kt
@@ -41,15 +41,18 @@ import org.briarproject.bramble.api.identity.LocalAuthor
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent
 import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
 import org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.identity.AuthorManager
 import org.briarproject.briar.api.privategroup.JoinMessageHeader
 import org.briarproject.briar.api.privategroup.PrivateGroupManager
+import org.briarproject.briar.api.privategroup.event.GroupInvitationResponseReceivedEvent
 import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent
 import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory
 import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
+import org.briarproject.briar.api.sharing.SharingManager
 import org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHAREABLE
 import org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHARING
 import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent
@@ -89,6 +92,8 @@ class PrivateGroupSharingViewModel @Inject constructor(
     eventBus,
 ) {
 
+    override val clientId: ClientId = GroupInvitationManager.CLIENT_ID
+
     private val _isCreator = mutableStateOf(false)
     val isCreator = _isCreator.asState()
 
@@ -175,7 +180,6 @@ class PrivateGroupSharingViewModel @Inject constructor(
 
         val groupId = _groupId ?: return
         when {
-            // todo: is there any similar leave event we could react to?
             e is GroupMessageAddedEvent && e.groupId == groupId && e.header is JoinMessageHeader -> {
                 reloadMembers()
             }
@@ -185,6 +189,16 @@ class PrivateGroupSharingViewModel @Inject constructor(
                 reloadMembers()
             }
 
+            e is GroupInvitationResponseReceivedEvent && e.messageHeader.shareableId == groupId -> {
+                if (e.messageHeader.wasAccepted()) {
+                    _sharingStatus.value += e.contactId to SHARING
+                    val connected = connectionRegistry.isConnected(e.contactId)
+                    _sharingInfo.update { addContact(connected) }
+                } else {
+                    _sharingStatus.value += e.contactId to SHAREABLE
+                }
+            }
+
             // todo: update member list on contact alias/avatar changed (may be member)
             // todo: any way of coupling member list to contact list for members that are actually contacts?
 
@@ -212,6 +226,13 @@ class PrivateGroupSharingViewModel @Inject constructor(
         }
     }
 
+    override fun getSharingStatusForContact(
+        txn: Transaction,
+        groupId: GroupId,
+        contact: Contact,
+    ): SharingManager.SharingStatus =
+        privateGroupInvitationManager.getSharingStatus(txn, contact, groupId)
+
     private fun loadSharingStatus(
         txn: Transaction,
         groupId: GroupId,
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/sharing/ThreadedGroupSharingViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/sharing/ThreadedGroupSharingViewModel.kt
index 55779093e0ca81c50bc6514d12d18cc21f90656a..aea3e2eab2b01705506f94a8a8724265eb2a18ca 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/sharing/ThreadedGroupSharingViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/threadedgroup/sharing/ThreadedGroupSharingViewModel.kt
@@ -20,13 +20,20 @@ package org.briarproject.briar.desktop.threadedgroup.sharing
 
 import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.mutableStateOf
+import mu.KotlinLogging
 import org.briarproject.bramble.api.connection.ConnectionRegistry
+import org.briarproject.bramble.api.contact.Contact
 import org.briarproject.bramble.api.contact.ContactId
 import org.briarproject.bramble.api.contact.ContactManager
+import org.briarproject.bramble.api.contact.event.ContactAddedEvent
+import org.briarproject.bramble.api.db.Transaction
 import org.briarproject.bramble.api.db.TransactionManager
+import org.briarproject.bramble.api.event.Event
 import org.briarproject.bramble.api.event.EventBus
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
+import org.briarproject.bramble.api.sync.ClientId
 import org.briarproject.bramble.api.sync.GroupId
+import org.briarproject.bramble.api.versioning.event.ClientVersionUpdatedEvent
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.identity.AuthorManager
 import org.briarproject.briar.api.sharing.SharingConstants
@@ -58,6 +65,12 @@ abstract class ThreadedGroupSharingViewModel(
     db,
     eventBus,
 ) {
+    companion object {
+        private val LOG = KotlinLogging.logger {}
+    }
+
+    protected abstract val clientId: ClientId
+
     protected var _groupId: GroupId? = null
 
     protected val _sharingInfo = mutableStateOf(SharingInfo(0, 0))
@@ -67,8 +80,6 @@ abstract class ThreadedGroupSharingViewModel(
     protected val _shareableSelected = mutableStateOf(emptySet<ContactId>())
     protected val _sharingMessage = mutableStateOf("")
 
-    open val deleteDialogCondition = false
-
     data class ShareableContactItem(val status: SharingManager.SharingStatus, val contactItem: ContactItem)
 
     val contactList = derivedStateOf {
@@ -95,6 +106,19 @@ abstract class ThreadedGroupSharingViewModel(
         loadContacts()
     }
 
+    override fun eventOccurred(e: Event) {
+        super.eventOccurred(e)
+        when {
+            e is ContactAddedEvent -> {
+                _groupId?.let { loadSharingStatusForContact(it, e.contactId) }
+            }
+
+            e is ClientVersionUpdatedEvent && e.clientVersion.clientId == clientId -> {
+                _groupId?.let { loadSharingStatusForContact(it, e.contactId) }
+            }
+        }
+    }
+
     @UiExecutor
     fun setGroupId(groupId: GroupId) {
         if (_groupId == groupId) return
@@ -104,6 +128,21 @@ abstract class ThreadedGroupSharingViewModel(
 
     protected abstract fun reload()
 
+    private fun loadSharingStatusForContact(groupId: GroupId, contactId: ContactId) =
+        runOnDbThreadWithTransaction(true) { txn ->
+            val contact = contactManager.getContact(txn, contactId)
+            val status = getSharingStatusForContact(txn, groupId, contact)
+            txn.attach {
+                _sharingStatus.value += contactId to status
+            }
+        }
+
+    protected abstract fun getSharingStatusForContact(
+        txn: Transaction,
+        groupId: GroupId,
+        contact: Contact,
+    ): SharingManager.SharingStatus
+
     data class SharingInfo(val total: Int, val online: Int) {
         fun addContact(connected: Boolean) = copy(
             total = total + 1,