auto_update_ui.rs

  1mod update_notification;
  2
  3use auto_update::AutoUpdater;
  4use editor::{Editor, MultiBuffer};
  5use gpui::{actions, prelude::*, App, Context, Entity, SharedString, Window};
  6use http_client::HttpClient;
  7use markdown_preview::markdown_preview_view::{MarkdownPreviewMode, MarkdownPreviewView};
  8use release_channel::{AppVersion, ReleaseChannel};
  9use serde::Deserialize;
 10use smol::io::AsyncReadExt;
 11use util::ResultExt as _;
 12use workspace::notifications::{show_app_notification, NotificationId};
 13use workspace::Workspace;
 14
 15use crate::update_notification::UpdateNotification;
 16
 17actions!(auto_update, [ViewReleaseNotesLocally]);
 18
 19pub fn init(cx: &mut App) {
 20    notify_if_app_was_updated(cx);
 21    cx.observe_new(|workspace: &mut Workspace, _window, _cx| {
 22        workspace.register_action(|workspace, _: &ViewReleaseNotesLocally, window, cx| {
 23            view_release_notes_locally(workspace, window, cx);
 24        });
 25    })
 26    .detach();
 27}
 28
 29#[derive(Deserialize)]
 30struct ReleaseNotesBody {
 31    title: String,
 32    release_notes: String,
 33}
 34
 35fn view_release_notes_locally(
 36    workspace: &mut Workspace,
 37    window: &mut Window,
 38    cx: &mut Context<Workspace>,
 39) {
 40    let release_channel = ReleaseChannel::global(cx);
 41
 42    let url = match release_channel {
 43        ReleaseChannel::Nightly => Some("https://github.com/zed-industries/zed/commits/nightly/"),
 44        ReleaseChannel::Dev => Some("https://github.com/zed-industries/zed/commits/main/"),
 45        _ => None,
 46    };
 47
 48    if let Some(url) = url {
 49        cx.open_url(url);
 50        return;
 51    }
 52
 53    let version = AppVersion::global(cx).to_string();
 54
 55    let client = client::Client::global(cx).http_client();
 56    let url = client.build_url(&format!(
 57        "/api/release_notes/v2/{}/{}",
 58        release_channel.dev_name(),
 59        version
 60    ));
 61
 62    let markdown = workspace
 63        .app_state()
 64        .languages
 65        .language_for_name("Markdown");
 66
 67    workspace
 68        .with_local_workspace(window, cx, move |_, window, cx| {
 69            cx.spawn_in(window, |workspace, mut cx| async move {
 70                let markdown = markdown.await.log_err();
 71                let response = client.get(&url, Default::default(), true).await;
 72                let Some(mut response) = response.log_err() else {
 73                    return;
 74                };
 75
 76                let mut body = Vec::new();
 77                response.body_mut().read_to_end(&mut body).await.ok();
 78
 79                let body: serde_json::Result<ReleaseNotesBody> =
 80                    serde_json::from_slice(body.as_slice());
 81
 82                if let Ok(body) = body {
 83                    workspace
 84                        .update_in(&mut cx, |workspace, window, cx| {
 85                            let project = workspace.project().clone();
 86                            let buffer = project.update(cx, |project, cx| {
 87                                project.create_local_buffer("", markdown, cx)
 88                            });
 89                            buffer.update(cx, |buffer, cx| {
 90                                buffer.edit([(0..0, body.release_notes)], None, cx)
 91                            });
 92                            let language_registry = project.read(cx).languages().clone();
 93
 94                            let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 95
 96                            let tab_description = SharedString::from(body.title.to_string());
 97                            let editor = cx.new(|cx| {
 98                                Editor::for_multibuffer(buffer, Some(project), true, window, cx)
 99                            });
100                            let workspace_handle = workspace.weak_handle();
101                            let markdown_preview: Entity<MarkdownPreviewView> =
102                                MarkdownPreviewView::new(
103                                    MarkdownPreviewMode::Default,
104                                    editor,
105                                    workspace_handle,
106                                    language_registry,
107                                    Some(tab_description),
108                                    window,
109                                    cx,
110                                );
111                            workspace.add_item_to_active_pane(
112                                Box::new(markdown_preview.clone()),
113                                None,
114                                true,
115                                window,
116                                cx,
117                            );
118                            cx.notify();
119                        })
120                        .log_err();
121                }
122            })
123            .detach();
124        })
125        .detach();
126}
127
128/// Shows a notification across all workspaces if an update was previously automatically installed
129/// and this notification had not yet been shown.
130pub fn notify_if_app_was_updated(cx: &mut App) {
131    let Some(updater) = AutoUpdater::get(cx) else {
132        return;
133    };
134    let version = updater.read(cx).current_version();
135    let should_show_notification = updater.read(cx).should_show_update_notification(cx);
136
137    cx.spawn(|cx| async move {
138        let should_show_notification = should_show_notification.await?;
139        if should_show_notification {
140            cx.update(|cx| {
141                show_app_notification(
142                    NotificationId::unique::<UpdateNotification>(),
143                    cx,
144                    move |cx| {
145                        let workspace_handle = cx.entity().downgrade();
146                        cx.new(|_| UpdateNotification::new(version, workspace_handle))
147                    },
148                );
149                updater.update(cx, |updater, cx| {
150                    updater
151                        .set_should_show_update_notification(false, cx)
152                        .detach_and_log_err(cx);
153                })
154            })?;
155        }
156        anyhow::Ok(())
157    })
158    .detach();
159}