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}