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