C
C#15mo ago
Mekasu0124

✅ getting app to check repo for latest release, updating if versions don't match

I've created a help channel for this before, but at this point I'm lost. I'm wanting to set my app up so that when the user launches it, it runs a check to see if the user has the matching version to the latest github repo release. I am using Avalonia MVVM and I'm wanting to incorporate this into my Todo app first. https://github.com/mekasu0124/Todo The only problem is, I have no idea what file to do this in, or how to work with the github thing to achieve what I'm wanting to achieve. Thanks in advance
98 Replies
Mekasu0124
Mekasu0124OP15mo ago
if I can get help learning how to do it in my Todo app, then I can replicate it and put it in my other apps on my own, I believe. MainWindowView.axaml and MainWindowViewModel.cs dictates what is showing on the screen.
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db);
}

public ViewModelBase Content
{
get => _content;
private set => this.RaiseAndSetIfChanged(ref _content, value);
}

public TodoListViewModel List { get; }

public void AddItem()
{
var vm = new AddItemViewModel();
Observable.Merge(
vm.Ok,
vm.Cancel
.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
List.AddItem(model);
}
Content = List;
});

Content = vm;
}
}
}
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db);
}

public ViewModelBase Content
{
get => _content;
private set => this.RaiseAndSetIfChanged(ref _content, value);
}

public TodoListViewModel List { get; }

public void AddItem()
{
var vm = new AddItemViewModel();
Observable.Merge(
vm.Ok,
vm.Cancel
.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
List.AddItem(model);
}
Content = List;
});

Content = vm;
}
}
}
here, the MainWindowViewModel.cs obtains the list of items to display on the screen. I have a feeling that my update checker would happen in this file (likely calling an external class to do so) and either rendering the home screen like normal if the versions match, or rendering the "There is an update available, click ok to update" if the versions don't match. I just don't know how to implement that
Mekasu0124
Mekasu0124OP15mo ago
https://github.com/octokit/octokit.net any thoughts on using this?
GitHub
GitHub - octokit/octokit.net: A GitHub API client library for .NET
A GitHub API client library for .NET . Contribute to octokit/octokit.net development by creating an account on GitHub.
Azrael
Azrael15mo ago
You can use this API endpoint https://api.github.com/repos/mekasu0124/Todo/releases/latest.
Mekasu0124
Mekasu0124OP15mo ago
ok. I created a new file in my Services folder and named it Update.cs How can I use that endpoing api?
Azrael
Azrael15mo ago
I can give you example.
Mekasu0124
Mekasu0124OP15mo ago
I appreciate it 🙂
Azrael
Azrael15mo ago
private async Task CheckForUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
var json = JObject.Parse(response);
var latestVersion = json["prop here"].ToString();

if (latestVersion != CurrentAppVersion)
{
// Do something here, like notify your user or something.
}
}
}
private async Task CheckForUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
var json = JObject.Parse(response);
var latestVersion = json["prop here"].ToString();

if (latestVersion != CurrentAppVersion)
{
// Do something here, like notify your user or something.
}
}
}
private async Task DownloadUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
var json = JObject.Parse(response);

var downloadUrl = json["assets"][0]["browser_download_url"].ToString(); // ""browser_download_url": "https://github.com/mekasu0124/Todo/releases/download/v1.0.2/Todo_Linux_x64.zip""
var updateData = await client.GetByteArrayAsync(downloadUrl);
var pathToSave = // Path
await File.WriteAllBytesAsync(pathToSave, updateData);

// You need to extract and install the update here.
}
}
private async Task DownloadUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
var json = JObject.Parse(response);

var downloadUrl = json["assets"][0]["browser_download_url"].ToString(); // ""browser_download_url": "https://github.com/mekasu0124/Todo/releases/download/v1.0.2/Todo_Linux_x64.zip""
var updateData = await client.GetByteArrayAsync(downloadUrl);
var pathToSave = // Path
await File.WriteAllBytesAsync(pathToSave, updateData);

// You need to extract and install the update here.
}
}
That's all the syntax I know in my head from HttpClient. You can make the response string constant.
Angius
Angius15mo ago
It'd be better to reuse a static instance of HttpClient and use .GetAsJsonAsync<T>() maybe, but yeah, this looks fine
Mekasu0124
Mekasu0124OP15mo ago
I definitely appreciate it. My goal now is to take the examples you gave and make them working code. Once they're working code, I'll be building an UpdateAvailableScreeView.axaml file that I would like to have display when an update is available instead of the home screen
Angius
Angius15mo ago
Question is, where to call it in the Avalonia app so that it runs on startup
Azrael
Azrael15mo ago
I wrote it in Discord :(
Angius
Angius15mo ago
And shows a notification that the app is out of date
Azrael
Azrael15mo ago
if (latestVersion != CurrentAppVersion)
{
// Do something here, like notify your user or something.
}
if (latestVersion != CurrentAppVersion)
{
// Do something here, like notify your user or something.
}
Angius
Angius15mo ago
Well, yes, sure
Azrael
Azrael15mo ago
Good question, isn't there a base startup class?
Mekasu0124
Mekasu0124OP15mo ago
I can use MainWindowViewModel.cs to run the update checker, however, I have a problem
Mekasu0124
Mekasu0124OP15mo ago
how am I to define CurrentAppVersion? and what goes in ["prop here"]?
Angius
Angius15mo ago
Also, just noticed that Newtonsoft.Json dependency 💀 You'd define the version... somewhere And prop here would be how you get the version from the response you get from the API So you'll need to either hit up the docs or call the endpoint with something like Insomnia, or even open it in the browser, to see where the version number is sitting
Mekasu0124
Mekasu0124OP15mo ago
public partial class Update : ViewModelBase
{
public bool _isUpdateAvailable;
public bool _isUpdating;

public string _githubAddress = "https://api.github.com/repos/mekasu0124/Todo/releases/latest";

private async Task CheckForUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync(_githubAddress);
var json = JObject.Parse(response);
var latestVersion = json["prop here"].ToString();

if (latestVersion != CurrentAppVersion)
{
_isUpdateAvailable = true;
}
}
}

private async Task DownloadUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync(_githubAddress);
var json = JObject.Parse(response);

IsUpdating = true;

// this will need to automatically know the url without my input based off the latest version and the users OS
// windows os are built for both 32-bit (Todo_Windows_x32.zip) and 64-bit (Todo_Windows_x64.zip)
var downloadUrl = json["assets"][0]["browser_download_url"].ToString();
var updateData = await client.GetByteArrayAsync(downloadUrl);

// this will need to detect where the user physically has the program
var pathToSave = "";
await File.WriteAllBytesAsync(pathToSave, updateData);

// extract and install update here

IsUpdating = false;
}
}

public bool IsUpdateAvailable
{
get => _isUpdateAvailable;
set => this.RaiseAndSetIfChanged(ref _isUpdateAvailable, value);
}

public bool IsUpdating
{
get => _isUpdating;
set => this.RaiseAndSetIfChanged(ref _isUpdating, value);
}
}
public partial class Update : ViewModelBase
{
public bool _isUpdateAvailable;
public bool _isUpdating;

public string _githubAddress = "https://api.github.com/repos/mekasu0124/Todo/releases/latest";

private async Task CheckForUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync(_githubAddress);
var json = JObject.Parse(response);
var latestVersion = json["prop here"].ToString();

if (latestVersion != CurrentAppVersion)
{
_isUpdateAvailable = true;
}
}
}

private async Task DownloadUpdatesAsync()
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync(_githubAddress);
var json = JObject.Parse(response);

IsUpdating = true;

// this will need to automatically know the url without my input based off the latest version and the users OS
// windows os are built for both 32-bit (Todo_Windows_x32.zip) and 64-bit (Todo_Windows_x64.zip)
var downloadUrl = json["assets"][0]["browser_download_url"].ToString();
var updateData = await client.GetByteArrayAsync(downloadUrl);

// this will need to detect where the user physically has the program
var pathToSave = "";
await File.WriteAllBytesAsync(pathToSave, updateData);

// extract and install update here

IsUpdating = false;
}
}

public bool IsUpdateAvailable
{
get => _isUpdateAvailable;
set => this.RaiseAndSetIfChanged(ref _isUpdateAvailable, value);
}

public bool IsUpdating
{
get => _isUpdating;
set => this.RaiseAndSetIfChanged(ref _isUpdating, value);
}
}
so here's where I'm at I've commented where the program will need to automatically know certain things. As far as CurrentAppVersion, the program will need to know that on it's own from the api call. so how can I do this? and to clarify, the IsUpdateAvaliable boolean is for the program to know whether or not to show the usercontrol.
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;
public MainWindowViewModel(Database db) {
{
Content = List = IsUpdateAvailable ? new ShowPopupHere() : new TodoListViewModel();
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;
public MainWindowViewModel(Database db) {
{
Content = List = IsUpdateAvailable ? new ShowPopupHere() : new TodoListViewModel();
and the variable IsUpdating is so that the usercontrol know to update the splash screen and when to close it the view that I'll be creating for this is a loading screen. It'll tell the user that an update is available and they'll click continue. When they click continue, it shows a progress bar that updates as the application is doing the update and the CurrentAppVersion i'd like to programmtically get from the program without me having to manually update it I'm just not going to worry about it anymore. I can't figure this out, and it's really starting to frustrate me. Thanks for your help so far
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Mekasu0124
Mekasu0124OP15mo ago
using Todo.ViewModels;

namespace Todo.Services;

public partial class Update : ViewModelBase
{
}
using Todo.ViewModels;

namespace Todo.Services;

public partial class Update : ViewModelBase
{
}
I got rid of all of it. I'm still really new to C# and avalonia in general. I"m great with axaml files because it's simliar to building a website and I picked that part up rather quickly. The C# part of it is really hard for me and I'm really new to it. I removed everything because half of what you said doesn't make sense to me. I'm not trying to annoy anyone or piss anyone off, but I need someone to teach me how to do this stuff. I cannot find any guides to do this with and the documentation I do find doesn't give any examples on what to do. I have tried using a regular console app to try and get information from the github as well, and I even fail at doing that, so at this point I'm just defeated. I either need someone to help teach me from step one until finished, or I'm just not going to waste my time with this language anymore. I'm spending more time trying to beg for help than actually getting it and I'm just over it
Angius
Angius15mo ago
So, going to this link: https://api.github.com/repos/mekasu0124/Todo/releases/latest shows us some data about the latest release. We want to use that We can take either tag_name as the version, or we can simply check the published_at date If either is higher than what we saved in the app during compilation, that means a new version exists
Mekasu0124
Mekasu0124OP15mo ago
ok I'm with you so far so what is my first step?
Angius
Angius15mo ago
To get the data we can use HttpClient
Mekasu0124
Mekasu0124OP15mo ago
like we did here in their example
Angius
Angius15mo ago
Almost Instead of going through all the song and dance with stringly-typed properties and what not, we can do it simpler Luckily, both the tag name and the publish date are top-level properties, so creating a class we can deserialize the data to will be super easy
Mekasu0124
Mekasu0124OP15mo ago
ok question. Since this is Update.cs file is in my /Services folder, does the public partial class Update need to be type casted to ViewModelBase or something else? bet
Angius
Angius15mo ago
No clue I'm still trying to google around how to do something on app startup, control load, whatever in Avalonia Let's leave it for now
public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string TagName { get; init; }

[JsonPropertyName("published_at")]
public DateTime PublishedAt { get; init; }
}
public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string TagName { get; init; }

[JsonPropertyName("published_at")]
public DateTime PublishedAt { get; init; }
}
We can use this class to pull out only the properties we want from the JSON
Azrael
Azrael15mo ago
How's it going?
Angius
Angius15mo ago
var data = await _httpClient.GetAsJsonAsync<ReleaseData>(url);
var data = await _httpClient.GetAsJsonAsync<ReleaseData>(url);
like so
Mekasu0124
Mekasu0124OP15mo ago
we're working through it
Azrael
Azrael15mo ago
Nice. I have school in an hour :(
Angius
Angius15mo ago
All in all, you would have some
public class GithubVersionService
{
private static HttpClient _client = new();

public async Task<bool> CheckVersion()
{
var data = await _httpClient.GetAsJsonAsync<ReleaseData>(url);
return data.PublishedAt > buildDate;
}
}
public class GithubVersionService
{
private static HttpClient _client = new();

public async Task<bool> CheckVersion()
{
var data = await _httpClient.GetAsJsonAsync<ReleaseData>(url);
return data.PublishedAt > buildDate;
}
}
With buildDate being the date the current version of the app was built at You could probably do some csproj shenanigans to make it available and automatically saved Avalonia's docs have a tutorial on loading data on startup: https://docs.avaloniaui.net/docs/next/tutorials/music-store-app/load-data-at-startup
Mekasu0124
Mekasu0124OP15mo ago
public class GithubVersionService
{
private static HttpClient _client = new();

public async Task<bool> CheckVersion()
{
var data = await _httpClient.GetAsJsonAsync<ReleaseData>(url);
return data.PublishedAt > buildDate;
}
}

public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string TagName { get; set; }

[JsonPropertyName("published_at")]
public DateTime PublishedAt { get; set; }
}
public class GithubVersionService
{
private static HttpClient _client = new();

public async Task<bool> CheckVersion()
{
var data = await _httpClient.GetAsJsonAsync<ReleaseData>(url);
return data.PublishedAt > buildDate;
}
}

public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string TagName { get; set; }

[JsonPropertyName("published_at")]
public DateTime PublishedAt { get; set; }
}
here's where I'm at
Angius
Angius15mo ago
Yeah, that's the easy part lol Honestly... I think it would be easiest to run this code on button click, at least for the time being Just create a new command, run this code inside of it, bind it to a button
Mekasu0124
Mekasu0124OP15mo ago
Angius
Angius15mo ago
Well, yeah, to be expected The URL should be this And here's a blogpost I found on how to get the build date: https://intellitect.com/blog/build-date-net-application/ That said... the release will be always created after the build So it's probably not the best way and you should rely on the tag instead
Mekasu0124
Mekasu0124OP15mo ago
ok I'm sorry. Nuget told me that I needed to update Avalonia and some other packages and I did. The project was built on 0.10.21 and now everything has updated to version 11. How do I roll it back?
Angius
Angius15mo ago
And manually storing the version, maybe in some version.txt file Why do you want to roll it back?
Mekasu0124
Mekasu0124OP15mo ago
because otherwise, I have to pause what we're doing here and rewrite the project for version 11
Angius
Angius15mo ago
Choosing a different version in the nuget package manager should work, though Worst case scenario, rollback to the last commit
Mekasu0124
Mekasu0124OP15mo ago
I'm just going to have to rewrite to version 11....this is ridiculous
ero
ero15mo ago
i don't see why anything should need to be rewritten what you could do as well is use the commit hash by the way it's a bit more involved and requires very meticulous version control (and probably publishing from CI) you would store the hash in some embedded text resource and fetch the commit hash of the latest release instead of a tag number or release date this allows you to have "dev builds" as well, which sync to the latest commit instead of the latest release
Mekasu0124
Mekasu0124OP15mo ago
because updating to version 11 has caused all these errors
ero
ero15mo ago
all these 5 errors? 😄
Mekasu0124
Mekasu0124OP15mo ago
Mekasu0124
Mekasu0124OP15mo ago
like if all of this is fixable, then I won't re-write it. I just don't know how to fix it I have an avalonia version 11 application that is built with community toolkit, and the ViewModel.cs files are all written that same way, but yet this app is erroring so how can I fix these errors? I fixed the view locator, but I do not know why these axamal.cs files are erroring. I checked my version 11 project, and they're written the exact same way I'm just going to continue with you and working on this. Maybe these errors will resolve themselves at some point so just create a version.txt file in the root of the project?
Angius
Angius15mo ago
For example, yeah
Mekasu0124
Mekasu0124OP15mo ago
or would it serve better in the Services folder? and if we're relying on just the tag name, then do I need to do this?
public async Task<bool> CheckVersion()
{
var data = await _client.GetAsJsonAsync<ReleaseData>(url); //i put the manual url in. Just didn't want to retype
- return data.PublishedAt > buildDate;
+ return data.TagName;
}

public class ReleaseData
{
[JsonPropertyName("tag_name");
public string TagName { get; set; }
- [JsonPropertyName("published_at")]
- public DateTime PublishedAt { get; set; }
}
public async Task<bool> CheckVersion()
{
var data = await _client.GetAsJsonAsync<ReleaseData>(url); //i put the manual url in. Just didn't want to retype
- return data.PublishedAt > buildDate;
+ return data.TagName;
}

public class ReleaseData
{
[JsonPropertyName("tag_name");
public string TagName { get; set; }
- [JsonPropertyName("published_at")]
- public DateTime PublishedAt { get; set; }
}
Angius
Angius15mo ago
No, you don't need the date Instead, you would read the tag name from the file and compare it with data.TagName
Mekasu0124
Mekasu0124OP15mo ago
ok what about this?
public class GithubVersionService
{
private static HttpClient _client = new();

public async Task<bool> CheckVersion()
{
var data = await _client.GetAsJsonAsync<ReleaseData>("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
string version = File.ReadAllText("/version.txt");

return true ? data.TagName == version : false;
}
}

public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string TagName { get; set; }\
}
public class GithubVersionService
{
private static HttpClient _client = new();

public async Task<bool> CheckVersion()
{
var data = await _client.GetAsJsonAsync<ReleaseData>("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
string version = File.ReadAllText("/version.txt");

return true ? data.TagName == version : false;
}
}

public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string TagName { get; set; }\
}
Angius
Angius15mo ago
Yep, looks about right. Besides that ternary
Mekasu0124
Mekasu0124OP15mo ago
ok what did I do wrong? genuine question
Angius
Angius15mo ago
condition ? iftrue : iffalse Your condition is always true
Mekasu0124
Mekasu0124OP15mo ago
return data.TagName == version ? true : false;
Angius
Angius15mo ago
Yep
Mekasu0124
Mekasu0124OP15mo ago
ok cool
Angius
Angius15mo ago
Or... return data.TagName == version; Since it returns true or false anyway
Mekasu0124
Mekasu0124OP15mo ago
ok bet. I'll switch it to that. So this GetAsJsonAsync<ReleaseData>(url); how do I instantiate that GetAsJsonAsync part?
Angius
Angius15mo ago
what It's a method You don't instantiate methods
Mekasu0124
Mekasu0124OP15mo ago
Angius
Angius15mo ago
Ah, you're probably missing a namespace See if quick fixes can just import it
Mekasu0124
Mekasu0124OP15mo ago
Angius
Angius15mo ago
You need using System.Net.Http.Json And I made a mistake, too. It's GetFromJsonAsync not GetAsJsonAsync
Mekasu0124
Mekasu0124OP15mo ago
ok bet. that's resolved now so what's our next step?
Angius
Angius15mo ago
Next step is to call it... somewhere Probably on button click That seems the easiest
Mekasu0124
Mekasu0124OP15mo ago
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="500"
x:Class="Todo.Views.NewVersionScreen">

<Grid Background="#000000">

<Label Content="New Update Available" />

<Label Content=$"A New Update Has Been Released. Version {TagName} Is Now Available. Click Ok To Begin Update" />

<Button Content="Ok" />
</Grid>
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="500"
x:Class="Todo.Views.NewVersionScreen">

<Grid Background="#000000">

<Label Content="New Update Available" />

<Label Content=$"A New Update Has Been Released. Version {TagName} Is Now Available. Click Ok To Begin Update" />

<Button Content="Ok" />
</Grid>
</UserControl>
ok so in my Views folder, I've made a new file NewVersionScreen.axaml. It's a simple screen that will show on the home screen instead of the TodoListViewModel. My first problem is the second label. How can I get access to the tag name for the string, and it doesn't like the prefixed dollar sign
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db);
}
namespace Todo.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
ViewModelBase _content;

public MainWindowViewModel(Database db)
{
Content = List = new TodoListViewModel(db);
}
here is where it loads the content on launch. So I figured we could run the check in this same function and then do a conditional render based off the boolean value that is returned from /Services/Update.cs
using ReactiveUI;

namespace Todo.ViewModels;

public partial class NewVersionScreenViewModel : ViewModelBase
{
string _tagName;

public NewVersionScreenViewModel()
{
}

public string TagName
{
get => _tagName;
set => this.RaiseAndSetIfChanged(ref _tagName, value);
}
}
using ReactiveUI;

namespace Todo.ViewModels;

public partial class NewVersionScreenViewModel : ViewModelBase
{
string _tagName;

public NewVersionScreenViewModel()
{
}

public string TagName
{
get => _tagName;
set => this.RaiseAndSetIfChanged(ref _tagName, value);
}
}
and here's where I'm at with that new screens view model
Angius
Angius15mo ago
Well, to get the version number from our method we will need to modify it, so it returns more than just that boolean You could make some record VersionData(string CurrentVersion, string LatestVersion, bool IsNewer) for example And return that
Mekasu0124
Mekasu0124OP15mo ago
Angius
Angius15mo ago
No A record is like a class Then you use it with new VersionInfo(foo, bar, baz) And return that You can make a class instead if you're more comfortable with them
Mekasu0124
Mekasu0124OP15mo ago
public class GithubVersionService
{
private static HttpClient _client = new();
public record VersionData(string CurrentVersion, string LatestVersion, bool IsNewer);

public async Task<bool> CheckVersion()
{
var data = await _client.GetFromJsonAsync<ReleaseData>("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
string version = File.ReadAllText("/version.txt");

return new VersionData(version, data.TagName, data.TagName==version);
}
}
public class GithubVersionService
{
private static HttpClient _client = new();
public record VersionData(string CurrentVersion, string LatestVersion, bool IsNewer);

public async Task<bool> CheckVersion()
{
var data = await _client.GetFromJsonAsync<ReleaseData>("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
string version = File.ReadAllText("/version.txt");

return new VersionData(version, data.TagName, data.TagName==version);
}
}
how close am I? how do I make it a class because apparently it's not liking me returning that record since it's not a boolean
Angius
Angius15mo ago
Maybe just change the return type...?
Mekasu0124
Mekasu0124OP15mo ago
public record class VersionData(string currentVersion, string latestVersion, bool isNewer)
{

}
public record class VersionData(string currentVersion, string latestVersion, bool isNewer)
{

}
like that?
Angius
Angius15mo ago
A class won't be a boolean either
Mekasu0124
Mekasu0124OP15mo ago
ok so change <bool> to <record>
Angius
Angius15mo ago
No To <VersionData>
Mekasu0124
Mekasu0124OP15mo ago
ok bet
namespace Todo.ViewModels;

public partial class NewVersionScreenViewModel : ViewModelBase
{
string _tagName;

public NewVersionScreenViewModel()
{
}

public string TagName
{
get => _tagName;
set => this.RaiseAndSetIfChanged(ref _tagName, value);
}
}
namespace Todo.ViewModels;

public partial class NewVersionScreenViewModel : ViewModelBase
{
string _tagName;

public NewVersionScreenViewModel()
{
}

public string TagName
{
get => _tagName;
set => this.RaiseAndSetIfChanged(ref _tagName, value);
}
}
ok so the view model. I have to create a new VersionData? I'll pick this back up tomorrow. I have to go to bed. I've been awake for 21 hours so far. I'll ping you tomorrow
Angius
Angius15mo ago
Sure thing
Mekasu0124
Mekasu0124OP15mo ago
ok I'm back. Sorry about that
namespace Todo.Services;

public class GithubVersionService
{
private static HttpClient _client = new();
public record VersionData(string CurrentVersion, string LatestVersion, bool IsNewer);

public async Task<VersionData> CheckVersion()
{
var data = await _client.GetFromJsonAsync<ReleaseData>("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
string version = File.ReadAllText("/version.txt");

return new VersionData(version, data.TagName, data.TagName == version);
}
}

public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string? TagName { get; set; }
}
namespace Todo.Services;

public class GithubVersionService
{
private static HttpClient _client = new();
public record VersionData(string CurrentVersion, string LatestVersion, bool IsNewer);

public async Task<VersionData> CheckVersion()
{
var data = await _client.GetFromJsonAsync<ReleaseData>("https://api.github.com/repos/mekasu0124/Todo/releases/latest");
string version = File.ReadAllText("/version.txt");

return new VersionData(version, data.TagName, data.TagName == version);
}
}

public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string? TagName { get; set; }
}
this is my /Services/Update.cs file. Am I missing anything here?
Angius
Angius15mo ago
Looks good
Mekasu0124
Mekasu0124OP15mo ago
and to understand the public record VersionData(); I can add more to that right? So if I wanted to also get the DateTime object that the release was made at, I can add in
public record VersionData(
string CurrentVersion,
string LatestVersion,
DateTime ReleaseDate,
bool IsNewer
);
public record VersionData(
string CurrentVersion,
string LatestVersion,
DateTime ReleaseDate,
bool IsNewer
);
right?
Angius
Angius15mo ago
Yep
Mekasu0124
Mekasu0124OP15mo ago
and then fix my return to say return new VersionData(version, data.TagName, data.ReleaseDate, data.TagName == version);
public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string? TagName { get; set; }
[JsonPropertyName("published_at")]
public DateTime? ReleaseDate { get; set; }
}
public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string? TagName { get; set; }
[JsonPropertyName("published_at")]
public DateTime? ReleaseDate { get; set; }
}
and update my ReleaseData to include the DateTime? ReleaseDate. Am I on the right track so far?
Angius
Angius15mo ago
Not sure why you decided to make those properties nullable, but yes
Mekasu0124
Mekasu0124OP15mo ago
do they not need to be?
Angius
Angius15mo ago
Do you expect any of that data to be null?
Mekasu0124
Mekasu0124OP15mo ago
no. ok I got you I'm still getting these errors. I've corrected the ViewLocator.cs, but I'm not understanding why I'm getting these errors ok give me about 5 maybe 10 minutes. I'm just going to very quickly rewrite the project. It won't take long, promise
Angius
Angius15mo ago
Sure
Mekasu0124
Mekasu0124OP15mo ago
ok so I've re-written, and I'm believing there is a difference here between version 0.10.21 and 11.0.2
public void AddItem()
{
var vm = new AddItemViewModel();

Observable.Merge(
vm.Ok,
vm.Cancel
.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(mode =>
{
if (model != null)
{
List.AddItem(mode);
}
Content = List;
});
Content = vm;
}
public void AddItem()
{
var vm = new AddItemViewModel();

Observable.Merge(
vm.Ok,
vm.Cancel
.Select(_ => (TodoItem)null))
.Take(1)
.Subscribe(mode =>
{
if (model != null)
{
List.AddItem(mode);
}
Content = List;
});
Content = vm;
}
it's telling me that for the .Subscribe part `Cannot convert lambda expression to type 'IObserver<TodoItem>' because it is not a delegate type. How can I fix this? nevermind. fixed it. I was missing an import ok I'm sorry for the wait. I am rewritten and ready to go. I've got the Update.cs file brought over, the version.txt brought over in the services folder. I have built a NewVersionScreen.axaml page and I have a NewVersionViewModel.cs file
using ReactiveUI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Todo.Services;

namespace Todo.ViewModels;

public partial class NewVersionViewModel : ViewModelBase
{
public string _informationText;

public NewVersionViewModel()
{
InformationText = $"A New Update {TagName} Was Released On {ReleaseDate}. The Program" +
"Needs To Be Updated. Please Click Ok To Update, or Exit To Close The Program.";
}

public string InformationText
{
get => _informationText;
set => this.RaiseAndSetIfChanged(ref _informationText, value);
}
}
using ReactiveUI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Todo.Services;

namespace Todo.ViewModels;

public partial class NewVersionViewModel : ViewModelBase
{
public string _informationText;

public NewVersionViewModel()
{
InformationText = $"A New Update {TagName} Was Released On {ReleaseDate}. The Program" +
"Needs To Be Updated. Please Click Ok To Update, or Exit To Close The Program.";
}

public string InformationText
{
get => _informationText;
set => this.RaiseAndSetIfChanged(ref _informationText, value);
}
}
so how do I get access to the information in Update.cs in my ViewModel.cs file?
Angius
Angius15mo ago
Well, you can run it on button click, for example
Mekasu0124
Mekasu0124OP15mo ago
I would want the InformationText to display above the buttons though?
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="500"
x:Class="Todo.Views.NewVersionScreen">

<Grid RowDefinitions="*,*,*,*" Background="Black">
<Label Grid.Row="0" Content="New Update Available!" />
<Label Grid.Row="1" Content="{Binding InformationText}" />
<Button Grid.Row="2" Content="Ok" />
<Button Grid.Row="3" Content="Exit" />
</Grid>
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="500"
x:Class="Todo.Views.NewVersionScreen">

<Grid RowDefinitions="*,*,*,*" Background="Black">
<Label Grid.Row="0" Content="New Update Available!" />
<Label Grid.Row="1" Content="{Binding InformationText}" />
<Button Grid.Row="2" Content="Ok" />
<Button Grid.Row="3" Content="Exit" />
</Grid>
</UserControl>
so when the user clicks the Ok button, it will show a loading screen. The loading screen will have a progress bar that gets updated as the upgrade happens.
Angius
Angius15mo ago
I'd say let's start with a "check for new updates" button Let's not get into downloading the files and stuff yet
Mekasu0124
Mekasu0124OP15mo ago
ok I have my button
Mekasu0124
Mekasu0124OP15mo ago
now what?
Angius
Angius15mo ago
Call the method to get the update datain your command Get the data And bind it to some label or something I'll be honest, I'm not too familliar with how Avalonia works
Mekasu0124
Mekasu0124OP15mo ago
well I don't know either. There isn't a youtube video that I can watch to do what I'm wanting to do, and you're the only one wiling to help me, but you're not familiar with it either so I don't know.
Angius
Angius15mo ago
You could try asking in #gui
Mekasu0124
Mekasu0124OP15mo ago
ok
Want results from more Discord servers?
Add your server