Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/home/room_screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1811,6 +1811,11 @@ impl RoomScreen {
.update_tombstone_footer(cx, tl.kind.room_id(), Some(&successor_room_details));
tl.tombstone_info = Some(successor_room_details);
}
TimelineUpdate::RoomEncrypted => {
tl.is_encrypted = true;
self.view.room_input_bar(cx, ids!(room_input_bar))
.update_encryption_status(cx, true);
}
TimelineUpdate::LinkPreviewFetched => {}
TimelineUpdate::FileUploadStarted { upload_id, file_name, in_reply_to, abort_handle } => {
self.view.room_input_bar(cx, ids!(room_input_bar))
Expand Down Expand Up @@ -2491,7 +2496,7 @@ impl RoomScreen {
return;
}
if !self.is_loaded && self.all_rooms_loaded {
panic!("BUG: timeline {kind} is not loaded, but its RoomScreen \
error!("BUG: timeline {kind} is not loaded, but its RoomScreen \
was not waiting for its timeline to be loaded either.");
}
return;
Expand All @@ -2501,6 +2506,7 @@ impl RoomScreen {
update_sender,
request_sender,
successor_room,
is_encrypted,
} = timeline_endpoints;

// Start with the basic tombstone info, and fetch the full details
Expand All @@ -2521,6 +2527,7 @@ impl RoomScreen {
// This doesn't mean that the user can actually perform all actions;
// the power levels will be updated from the homeserver once the room is opened.
user_power: UserPowerLevels::all(),
is_encrypted,
// Room members start as None and get populated when fetched from the server
room_members: None,
// We assume timelines being viewed for the first time haven't been fully paginated.
Expand Down Expand Up @@ -2733,6 +2740,7 @@ impl RoomScreen {
saved_room_input_bar_state,
tl_state.user_power,
tl_state.tombstone_info.as_ref(),
tl_state.is_encrypted,
);
}

Expand Down Expand Up @@ -3042,6 +3050,9 @@ pub enum TimelineUpdate {
PinnedEvents(Vec<OwnedEventId>),
/// An update containing the currently logged-in user's power levels for this room.
UserPowerLevels(UserPowerLevels),
/// A notice that this room has been changed to use encryption.
/// It's only possible to go from unencrypted --> encrypted, not the other way.
RoomEncrypted,
/// An update to the currently logged-in user's own read receipt for this room.
OwnUserReadReceipt(Receipt),
/// A notice that the given room has been tombstoned (closed)
Expand Down Expand Up @@ -3193,6 +3204,9 @@ struct TimelineUiState {
/// The power levels of the currently logged-in user in this room.
user_power: UserPowerLevels,

/// Whether this room is encrypted. Once enabled it can never be disabled.
is_encrypted: bool,

/// The list of room members for this room.
room_members: Option<Arc<Vec<RoomMember>>>,

Expand Down
19 changes: 19 additions & 0 deletions src/room/room_input_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,17 @@ impl RoomInputBar {
self.view.view(cx, ids!(can_not_send_message_notice)).set_visible(cx, !can_send);
}

/// Sets the message input's placeholder to reflect this room's encryption status.
fn update_encryption_status(&mut self, cx: &mut Cx, is_encrypted: bool) {
let empty_text = if is_encrypted {
"Send an encrypted message..."
} else {
"Send an unencrypted message..."
};
self.text_input(cx, ids!(input_bar.mentionable_text_input.text_input))
.set_empty_text(cx, empty_text.to_string());
}

/// Returns true if the TSP signing checkbox is checked, false otherwise.
///
/// If TSP is not enabled, this will always return false.
Expand Down Expand Up @@ -755,6 +766,12 @@ impl RoomInputBarRef {
inner.update_tombstone_footer(cx, tombstoned_room_id, successor_room_details);
}

/// Updates the message input's placeholder based on this room's encryption status.
pub fn update_encryption_status(&self, cx: &mut Cx, is_encrypted: bool) {
let Some(mut inner) = self.borrow_mut() else { return };
inner.update_encryption_status(cx, is_encrypted);
}

/// Opens the native picker to upload a photo or video into this room.
pub fn open_photo_video_picker(
&self,
Expand Down Expand Up @@ -816,6 +833,7 @@ impl RoomInputBarRef {
saved_state: RoomInputBarState,
user_power_levels: UserPowerLevels,
tombstone_info: Option<&SuccessorRoomDetails>,
is_encrypted: bool,
) {
let Some(mut inner) = self.borrow_mut() else { return };
let RoomInputBarState {
Expand All @@ -831,6 +849,7 @@ impl RoomInputBarRef {
// This must happen before we restore the state of the `EditingPane`,
// because the call to `show_editing_pane()` might re-update the `input_bar`'s visibility.
inner.update_user_power_levels(cx, user_power_levels);
inner.update_encryption_status(cx, is_encrypted);

// 1. Restore the state of the TextInput within the MentionableTextInput.
inner.text_input(cx, ids!(input_bar.mentionable_text_input.text_input))
Expand Down
19 changes: 17 additions & 2 deletions src/sliding_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2363,6 +2363,7 @@ pub struct TimelineEndpoints {
pub update_receiver: crossbeam_channel::Receiver<TimelineUpdate>,
pub request_sender: TimelineRequestSender,
pub successor_room: Option<SuccessorRoom>,
pub is_encrypted: bool,
}

/// Info about a timeline for a joined room or a thread in a joined room.
Expand All @@ -2371,8 +2372,7 @@ struct PerTimelineDetails {
timeline: Arc<Timeline>,
/// A clone-able sender for updates to this timeline.
timeline_update_sender: crossbeam_channel::Sender<TimelineUpdate>,
/// A tuple of two separate channel endpoints that can only be taken *once* by the main UI thread.
///
/// A tuple of two separate channel endpoints that can only be taken *once* by the main UI thread:
/// 1. The single receiver that can receive updates from this timeline.
/// * When a new room is joined (or a thread is opened), an unbounded crossbeam channel will be created
/// and its sender given to a background task (the `timeline_subscriber_handler()`)
Expand Down Expand Up @@ -2585,6 +2585,7 @@ pub fn take_timeline_endpoints(kind: &TimelineKind) -> Option<TimelineEndpoints>
update_receiver,
request_sender,
successor_room: details.timeline.room().successor_room(),
is_encrypted: details.timeline.room().encryption_state().is_encrypted(),
})
}

Expand Down Expand Up @@ -2624,6 +2625,7 @@ struct RoomListServiceRoomInfo {
is_direct: bool,
is_marked_unread: bool,
is_tombstoned: bool,
is_encrypted: bool,
tags: Option<Tags>,
user_power_levels: Option<UserPowerLevels>,
latest_event_timestamp: Option<MilliSecondsSinceUnixEpoch>,
Expand Down Expand Up @@ -2655,6 +2657,7 @@ impl RoomListServiceRoomInfo {
is_direct: is_direct.unwrap_or(false),
is_marked_unread: room.is_marked_unread(),
is_tombstoned: room.is_tombstoned(),
is_encrypted: room.encryption_state().is_encrypted(),
tags: tags.ok().flatten(),
user_power_levels,
latest_event_timestamp: room.latest_event_timestamp(),
Expand Down Expand Up @@ -3473,6 +3476,18 @@ async fn update_room(
error!("BUG: could not find JoinedRoomDetails for room {new_room_id} where power levels changed.");
}
}

if !old_room.is_encrypted && new_room.is_encrypted {
if let Some(timeline_update_sender) = get_timeline_update_sender(&new_room_id) {
log!("Room {new_room_id} is now encrypted.");
match timeline_update_sender.send(TimelineUpdate::RoomEncrypted) {
Ok(_) => SignalToUI::set_ui_signal(),
Err(_) => error!("Failed to send the RoomEncrypted update to room {new_room_id}"),
}
} else {
error!("BUG: could not find JoinedRoomDetails for room {new_room_id} that became encrypted.");
}
}
}
Ok(())
}
Expand Down
Loading