xmtp_mls/groups/lifecycle/
restored.rs1use super::*;
4use xmtp_db::ConnectionExt;
5use xmtp_proto::xmtp::device_sync::group_backup::GroupSave;
6
7impl<Context: XmtpSharedContext> MlsGroup<Context> {
8 pub fn restore_from_archive(context: &Context, save: &GroupSave) -> Result<bool, GroupError> {
11 let group_id = GroupId::try_from(save.id.as_slice())?;
12 state_write(context.mls_storage(), |tx| {
13 if tx.storage().db().find_group(&group_id)?.is_some() {
14 return Ok::<_, GroupError>(Continue(merge_activity(
15 &tx.storage().db(),
16 &group_id,
17 save.last_message_ns,
18 )?));
19 }
20 let conversation_type: ConversationType = save.conversation_type().try_into()?;
21 let pair = if conversation_type == ConversationType::Dm {
22 Some(crate::groups::parse_canonical_dm_id(save.dm_id.as_deref())?)
23 } else {
24 None
25 };
26 let attributes = save
27 .mutable_metadata
28 .as_ref()
29 .map(|metadata| &metadata.attributes);
30 let attribute = |name| {
31 attributes
32 .and_then(|attributes| attributes.get(name))
33 .cloned()
34 };
35 let opts = GroupMetadataOptions {
36 name: attribute("group_name"),
37 image_url_square: attribute("group_image_url_square"),
38 description: attribute("description"),
39 app_data: attribute("app_data"),
40 message_disappearing_settings: match (
41 save.message_disappear_from_ns,
42 save.message_disappear_in_ns,
43 ) {
44 (Some(from_ns), Some(in_ns)) => {
45 Some(MessageDisappearingSettings::new(from_ns, in_ns))
46 }
47 _ => None,
48 },
49 };
50 let kind = match &pair {
51 Some(pair) => InitialGroupKind::RestoredDm {
52 member_one_inbox_id: &pair.member_one_inbox_id,
53 member_two_inbox_id: &pair.member_two_inbox_id,
54 },
55 None => InitialGroupKind::Group {
56 conversation_type,
57 oneshot_message: None,
58 },
59 };
60 let policy = if pair.is_some() {
61 PolicySet::new_dm()
62 } else {
63 PolicySet::default()
64 };
65 let dictionary = initial_dictionary(
67 kind,
68 &policy
69 .to_proto()
70 .map_err(group_permissions::GroupMutablePermissionsError::from)
71 .map_err(MetadataPermissionsError::from)?,
72 &opts,
73 context.inbox_id(),
74 None,
75 &context
76 .server_configuration()
77 .configuration()
78 .application_components,
79 )
80 .map_err(app_data::migration::BootstrapSynthesisError::from)?;
81 let config = build_group_config(dictionary)?;
82 if let Some(pair) = &pair {
83 Self::insert_dm_row(
84 context,
85 tx,
86 GroupMembershipState::Restored,
87 pair,
88 &opts,
89 Some(&save.id),
90 &config,
91 false,
92 )?;
93 } else {
94 Self::insert_group_row(
95 context,
96 tx,
97 Some(&save.id),
98 GroupMembershipState::Restored,
99 conversation_type,
100 &opts,
101 &config,
102 false,
103 )?;
104 }
105 let storage = tx.storage();
106 let db = storage.db();
107 use xmtp_db::diesel::prelude::*;
108 use xmtp_db::schema::groups::dsl;
109 db.raw_query(|conn| {
110 xmtp_db::diesel::update(dsl::groups.find(&group_id))
111 .set((
112 dsl::created_at_ns.eq(save.created_at_ns),
113 dsl::added_by_inbox_id.eq(&save.added_by_inbox_id),
114 dsl::message_disappear_from_ns.eq(save.message_disappear_from_ns),
115 dsl::message_disappear_in_ns.eq(save.message_disappear_in_ns),
116 dsl::should_publish_commit_log.eq(false),
117 ))
118 .execute(conn)
119 })?;
120 merge_activity(&db, &group_id, save.last_message_ns)?;
121 Ok(Continue(true))
122 })
123 .map(TransactionOutcome::into_continued)
124 }
125}
126
127fn merge_activity(
128 db: &impl DbQuery,
129 group_id: &GroupId,
130 timestamp: Option<i64>,
131) -> Result<bool, StorageError> {
132 let Some(timestamp) = timestamp else {
133 return Ok(false);
134 };
135 use xmtp_db::diesel::prelude::*;
136 use xmtp_db::schema::groups::dsl;
137 Ok(db.raw_query(|conn| {
138 xmtp_db::diesel::update(dsl::groups.find(group_id))
139 .filter(
140 dsl::last_message_ns
141 .is_null()
142 .or(dsl::last_message_ns.lt(timestamp)),
143 )
144 .set(dsl::last_message_ns.eq(Some(timestamp)))
145 .execute(conn)
146 })? > 0)
147}