Skip to content

Commit

Permalink
Update GitHubClient sample (#276)
Browse files Browse the repository at this point in the history
* Update GitHubClient sample

* better layout
  • Loading branch information
edgarfgp authored Jan 12, 2025
1 parent 4693158 commit fdc1fa5
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 132 deletions.
6 changes: 6 additions & 0 deletions Fabulous.Avalonia.Samples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "TestableApp.UnitTests", "sa
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fabulous.Avalonia", "src\Fabulous.Avalonia\Fabulous.Avalonia.fsproj", "{31D47096-FBF7-4A74-A302-011B9608C32E}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fabulous.Avalonia.Labs", "extensions\Fabulous.Avalonia.Labs\Fabulous.Avalonia.Labs.fsproj", "{992E1416-7747-4E03-98F5-F608C135C635}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -134,5 +136,9 @@ Global
{31D47096-FBF7-4A74-A302-011B9608C32E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31D47096-FBF7-4A74-A302-011B9608C32E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31D47096-FBF7-4A74-A302-011B9608C32E}.Release|Any CPU.Build.0 = Release|Any CPU
{992E1416-7747-4E03-98F5-F608C135C635}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{992E1416-7747-4E03-98F5-F608C135C635}.Debug|Any CPU.Build.0 = Debug|Any CPU
{992E1416-7747-4E03-98F5-F608C135C635}.Release|Any CPU.ActiveCfg = Release|Any CPU
{992E1416-7747-4E03-98F5-F608C135C635}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
190 changes: 66 additions & 124 deletions samples/Mvu/GitHubClient/App.fs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
namespace GitHubClient

open System.Diagnostics
open System.IO
open Avalonia.Media
open Avalonia.Themes.Fluent
open Avalonia.Markup.Xaml.Styling
open Fabulous
open Fabulous.Avalonia
open System.Net
Expand All @@ -15,11 +13,11 @@ open System.Text.Json
open type Fabulous.Avalonia.View

module Models =
type RemoteData<'e, 't> =
type RemoteData<'T> =
| NotAsked
| Loading
| Content of 't
| Failure of 'e
| Content of 'T
| Failure of string

type User =
{ login: string
Expand All @@ -41,8 +39,6 @@ module URlConstants =
[<Literal>]
let githubBaseUrl = "https://api.github.com/users/"

type GitHubError = | Non200Response

module GitHubService =
let private fetchWitHeader (urlString: string) =
let client = new HttpClient()
Expand All @@ -63,47 +59,25 @@ module GitHubService =
return
match response.StatusCode with
| HttpStatusCode.OK -> Ok deserialized
| _ -> Error Non200Response
| _ -> Error "User not found!"
}

let getProfileImage (urlString: string) =
async {
let client = new HttpClient()
let! response = client.GetAsync(urlString) |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
let! data = response.Content.ReadAsByteArrayAsync() |> Async.AwaitTask

return
match response.StatusCode with
| HttpStatusCode.OK -> Ok(new MemoryStream(data))
| _ -> Error Non200Response
}


module App =
type Msg =
| UserNameChanged of string
| SearchClicked
| UserInfoLoaded of User
| UserInfoNotFound of GitHubError
| ProfileImageLoaded of Stream
| ProfileImageNotFound of GitHubError
| UserInfoNotFound of string
| LoadingProgress of float

type CmdMsg =
| GetUserInfo of name: string
| GetProfileImage of url: string

type Model =
{ UserName: string
UserInfo: RemoteData<string, User>
ProfileImage: RemoteData<string, Stream> }
UserInfo: RemoteData<User> }

let init () =
{ UserName = ""
UserInfo = RemoteData.NotAsked
ProfileImage = RemoteData.NotAsked },
[]
UserInfo = RemoteData.NotAsked },
Cmd.none

let getUserInfo userName =
task {
Expand All @@ -114,122 +88,90 @@ module App =
| Error error -> return UserInfoNotFound error
}

let getProfileImage url =
task {
let! response = GitHubService.getProfileImage url

match response with
| Ok image -> return ProfileImageLoaded image
| Error error -> return ProfileImageNotFound error
}

let mapCmdMsgToCmd cmdMsg =
match cmdMsg with
| GetUserInfo userName -> Cmd.OfTask.msg(getUserInfo userName)
| GetProfileImage url -> Cmd.OfTask.msg(getProfileImage url)

let update msg model =
match msg with
| UserNameChanged userName -> { model with UserName = userName }, []
| UserNameChanged userName -> { model with UserName = userName }, Cmd.none

| SearchClicked ->
{ model with
UserInfo = RemoteData.Loading },
[ GetUserInfo model.UserName ]
| SearchClicked -> { model with UserInfo = Loading }, Cmd.OfTask.msg(getUserInfo model.UserName)

| UserInfoLoaded user ->
{ model with
UserInfo = RemoteData.Content(user) },
[ GetProfileImage(user.avatar_url) ]
| UserInfoLoaded user -> { model with UserInfo = Content(user) }, Cmd.none

| UserInfoNotFound _ ->
{ model with
UserInfo = RemoteData.Failure("User not found!") },
[]
UserInfo = Failure("User not found!") },
Cmd.none

| ProfileImageLoaded image ->
{ model with
ProfileImage = RemoteData.Content(image) },
[]
| LoadingProgress _ -> model, Cmd.none

| ProfileImageNotFound _ ->
{ model with
ProfileImage = RemoteData.Failure("Profile image not found!") },
[]
let program =
Program.statefulWithCmd init update
|> Program.withTrace(fun (format, args) -> Debug.WriteLine(format, box args))
|> Program.withExceptionHandler(fun ex ->
#if DEBUG
printfn $"Exception: %s{ex.ToString()}"
false
#else
true
#endif
)

| LoadingProgress _ -> model, []
let content () =
Component("GitHubClient") {
let! model = Context.Mvu program

let content model =
Grid() {
(VStack() {
Image("avares://GitHubClient/Assets/github-icon.png")
.size(100., 100.)
Grid() {
VStack() {
match model.UserInfo with
| NotAsked -> ()
| Loading -> ProgressBar(0., 1., 0.5, LoadingProgress)
| Content user ->
(VStack() {
AsyncImage(user.avatar_url)
.placeholderSource("avares://GitHubClient/Assets/github-icon.png")
.size(100., 100.)

TextBox(model.UserName, UserNameChanged)
Button("Search", SearchClicked)
TextBlock(user.login)

match model.UserInfo with
| NotAsked -> ()
| Loading -> ProgressBar(0., 1., 0.5, LoadingProgress)
| Content user ->
(VStack() {
match model.ProfileImage with
| NotAsked -> ()
| Loading -> ()
| Content source -> Image(source).size(24., 24.)
| Failure _ ->
Image("avares://GitHubClient/Assets/github-icon.png", Stretch.Uniform)
.size(24., 24.)
if user.name.IsSome then
TextBlock(user.name.Value)

TextBlock(user.login)
if user.location.IsSome then
TextBlock(user.location.Value)

if user.name.IsSome then
TextBlock(user.name.Value)
if user.bio.IsSome then
TextBlock(user.bio.Value)

if user.location.IsSome then
TextBlock(user.location.Value)
TextBlock($"Public repos: {user.public_repos}")

if user.bio.IsSome then
TextBlock(user.bio.Value)
TextBlock($"Public gists: {user.public_gists}")

TextBlock($"Public repos: {user.public_repos}")
TextBlock($"Followers: {user.followers}")

TextBlock($"Public gists: {user.public_gists}")
TextBlock($"Following: {user.following}")

TextBlock($"Followers: {user.followers}")
TextBlock($"Created at: {user.created_at}")

TextBlock($"Following: {user.following}")
TextBlock($"Profile: {user.html_url}")
})
.centerHorizontal()
| Failure s -> TextBlock(s)

TextBlock($"Created at: {user.created_at}")
TextBox(model.UserName, UserNameChanged)

TextBlock($"Profile: {user.html_url}")
})
.centerHorizontal()
| Failure s -> TextBlock(s)
})
.centerHorizontal()
Button("Search", SearchClicked).centerHorizontal()
}
}
|> _.centerVertical()
}

let view model =
let view () =
#if MOBILE
SingleViewApplication(content model)
SingleViewApplication(content())
#else
DesktopApplication(Window(content model))
DesktopApplication(Window(content()))
#endif
let create () =
let theme () = FluentTheme()

let program =
Program.statefulWithCmdMsg init update mapCmdMsgToCmd
|> Program.withTrace(fun (format, args) -> Debug.WriteLine(format, box args))
|> Program.withExceptionHandler(fun ex ->
#if DEBUG
printfn $"Exception: %s{ex.ToString()}"
false
#else
true
#endif
)
|> Program.withView view
let theme () =
StyleInclude(baseUri = null, Source = Uri("avares://GitHubClient/App.xaml"))

FabulousAppBuilder.Configure(theme, program)
FabulousAppBuilder.Configure(theme, view)
9 changes: 9 additions & 0 deletions samples/Mvu/GitHubClient/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Styles
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:labs="using:Avalonia.Labs.Controls">
<FluentTheme />
<labs:ControlThemes/>
<Styles.Resources>
</Styles.Resources>
</Styles>
4 changes: 4 additions & 0 deletions samples/Mvu/GitHubClient/GitHubClient.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@
<PackageReference Include="FSharp.Core" />
<PackageReference Include="Fabulous" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Include="*.xaml" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\extensions\Fabulous.Avalonia.Labs\Fabulous.Avalonia.Labs.fsproj" />
<ProjectReference Include="..\..\..\src\Fabulous.Avalonia\Fabulous.Avalonia.fsproj" />
</ItemGroup>
<Import Project="..\..\Fabulous.Avalonia.Samples.targets" />
Expand Down
8 changes: 0 additions & 8 deletions samples/Mvu/GitHubClient/Properties/launchSettings.json

This file was deleted.

0 comments on commit fdc1fa5

Please sign in to comment.