auto_update_ui.rs

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