├── YourWeather ├── YourWeather.Rcl.Web │ ├── _Imports.razor │ ├── YourWeather.Rcl.Web.csproj │ └── Services │ │ └── SettingService.cs ├── README.md ├── YourWeather.Rcl.Desktop │ ├── _Imports.razor │ ├── Layout │ │ └── MainLayout.cs │ ├── YourWeather.Rcl.Desktop.csproj │ ├── Services │ │ ├── SettingService.cs │ │ └── StaticWebAssets.cs │ └── TitleBar.cs ├── Darnton.Blazor.DeviceInterop │ ├── _Imports.razor │ ├── Geolocation │ │ ├── GeolocationEventArgs.cs │ │ ├── GeolocationPositionError.cs │ │ ├── GeolocationResult.cs │ │ ├── GeolocationPosition.cs │ │ ├── GeolocationPositionErrorCode.cs │ │ ├── PositionOptions.cs │ │ ├── GeolocationCoordinates.cs │ │ ├── IGeolocationService.cs │ │ └── GeolocationService.cs │ ├── JSBinder.cs │ ├── Darnton.Blazor.DeviceInterop.csproj │ └── wwwroot │ │ └── js │ │ └── geolocation.js ├── YourWeather.Maui │ ├── myapp.keystore │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── index.html │ │ └── css │ │ │ └── app.css │ ├── Resources │ │ ├── Fonts │ │ │ └── OpenSans-Regular.ttf │ │ ├── AppIcon │ │ │ ├── appicon.svg │ │ │ └── appiconfg.svg │ │ ├── Raw │ │ │ └── AboutAssets.txt │ │ └── Splash │ │ │ └── splash.svg │ ├── Services │ │ ├── WeatherService.cs │ │ ├── PlatformIntegration.cs │ │ ├── SettingService.cs │ │ ├── ThemeService │ │ │ ├── TitleBarOrStatusBar.cs │ │ │ └── ThemeService.cs │ │ └── StaticWebAssets.cs │ ├── Properties │ │ └── launchSettings.json │ ├── App.xaml.cs │ ├── Platforms │ │ ├── Android │ │ │ ├── Resources │ │ │ │ └── values │ │ │ │ │ └── colors.xml │ │ │ ├── MainApplication.cs │ │ │ ├── MainActivity.cs │ │ │ └── AndroidManifest.xml │ │ ├── iOS │ │ │ ├── AppDelegate.cs │ │ │ ├── Program.cs │ │ │ └── Info.plist │ │ ├── MacCatalyst │ │ │ ├── AppDelegate.cs │ │ │ ├── Program.cs │ │ │ └── Info.plist │ │ ├── Windows │ │ │ ├── IPermissionRequestHandler.cs │ │ │ ├── App.xaml │ │ │ ├── SilentPermissionRequestHandler.cs │ │ │ ├── app.manifest │ │ │ ├── App.xaml.cs │ │ │ └── Package.appxmanifest │ │ └── Tizen │ │ │ ├── Main.cs │ │ │ └── tizen-manifest.xml │ ├── Routes.razor │ ├── _Imports.razor │ ├── Layout │ │ └── MainLayout.cs │ ├── Extensions │ │ └── ServiceCollectionExtensions │ │ │ ├── AddMauiExceptionHandle.cs │ │ │ └── AddDependencyInjection.cs │ ├── MainPage.xaml.cs │ ├── MainPage.xaml.MaciOS.cs │ ├── MainPage.xaml │ ├── App.xaml │ ├── MauiProgram.cs │ ├── MainPage.xaml.Android.cs │ └── MainPage.xaml.Windows.cs ├── YourWeather.Wpf │ ├── Resources │ │ └── favicon.ico │ ├── IPermissionRequestHandler.cs │ ├── Routes.razor │ ├── _Imports.razor │ ├── App.xaml │ ├── AssemblyInfo.cs │ ├── SilentPermissionRequestHandler.cs │ ├── App.xaml.cs │ ├── Extensions │ │ └── ServiceCollectionExtensions │ │ │ └── AddDependencyInjection.cs │ ├── MainWindow.xaml │ ├── YourWeather.Wpf.csproj │ ├── wwwroot │ │ ├── index.html │ │ └── css │ │ │ └── app.css │ ├── appiconfg.svg │ └── MainWindow.xaml.cs ├── YourWeather.Photino │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── index.html │ │ └── css │ │ │ └── app.css │ ├── Resources │ │ └── favicon.ico │ ├── App.razor │ ├── Layout │ │ └── MainLayout.cs │ ├── _Imports.razor │ ├── Extensions │ │ └── ServiceCollectionExtensions │ │ │ └── AddDependencyInjection.cs │ ├── YourWeather.Photino.csproj │ ├── Services │ │ ├── SettingService.cs │ │ └── StaticWebAssets.cs │ ├── Program.cs │ └── TitleBar.cs ├── YourWeather.Server │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── favicon.png │ │ └── css │ │ │ └── site.css │ ├── appsettings.json │ ├── appsettings.Development.json │ ├── .config │ │ └── dotnet-tools.json │ ├── YourWeather.Server.csproj │ ├── _Imports.razor │ ├── App.razor │ ├── Pages │ │ ├── Error.cshtml.cs │ │ ├── Error.cshtml │ │ └── _Host.cshtml │ ├── Extensions │ │ └── ServiceCollectionExtensions │ │ │ └── AddDependencyInjection.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Program.cs │ └── Services │ │ └── StaticWebAssets.cs ├── YourWeather.Winform │ ├── Resources │ │ └── favicon.ico │ ├── IPermissionRequestHandler.cs │ ├── App.razor │ ├── _Imports.razor │ ├── SilentPermissionRequestHandler.cs │ ├── Program.cs │ ├── Extensions │ │ └── ServiceCollectionExtensions │ │ │ └── AddDependencyInjection.cs │ ├── YourWeather.Winform.csproj │ ├── wwwroot │ │ ├── index.html │ │ └── css │ │ │ └── app.css │ ├── MainForm.cs │ └── MainForm.Designer.cs ├── YourWeather.WebAssembly │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── favicon.png │ │ ├── icon-192.png │ │ ├── icon-512.png │ │ ├── service-worker.js │ │ ├── manifest.json │ │ └── service-worker.published.js │ ├── App.razor │ ├── _Imports.razor │ ├── Program.cs │ ├── Extensions │ │ └── ServiceCollectionExtensions │ │ │ └── AddDependencyInjection.cs │ ├── YourWeather.WebAssembly.csproj │ ├── Properties │ │ └── launchSettings.json │ └── Services │ │ └── StaticWebAssets.cs ├── YourWeather.Rcl │ ├── wwwroot │ │ ├── css │ │ │ ├── qweather-icon │ │ │ │ └── fonts │ │ │ │ │ ├── qweather-icons.ttf │ │ │ │ │ ├── qweather-icons.woff │ │ │ │ │ └── qweather-icons.woff2 │ │ │ ├── fontawesome │ │ │ │ └── v6.4.0 │ │ │ │ │ └── webfonts │ │ │ │ │ ├── fa-solid-900.ttf │ │ │ │ │ ├── fa-brands-400.ttf │ │ │ │ │ ├── fa-regular-400.ttf │ │ │ │ │ ├── fa-solid-900.woff2 │ │ │ │ │ ├── fa-brands-400.woff2 │ │ │ │ │ ├── fa-regular-400.woff2 │ │ │ │ │ ├── fa-v4compatibility.ttf │ │ │ │ │ └── fa-v4compatibility.woff2 │ │ │ ├── material │ │ │ │ ├── flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 │ │ │ │ └── icons.css │ │ │ ├── materialdesign │ │ │ │ └── v7.1.96 │ │ │ │ │ └── fonts │ │ │ │ │ ├── materialdesignicons-webfont.eot │ │ │ │ │ ├── materialdesignicons-webfont.ttf │ │ │ │ │ ├── materialdesignicons-webfont.woff │ │ │ │ │ └── materialdesignicons-webfont.woff2 │ │ │ └── app.css │ │ └── js │ │ │ ├── platformIntegration.js │ │ │ └── systemTheme.js │ ├── Services │ │ ├── PlatformIntegration │ │ │ ├── IPlatformIntegration.cs │ │ │ └── PlatformIntegration.cs │ │ ├── LocationService │ │ │ ├── ILocationService.cs │ │ │ └── LocationService.cs │ │ ├── ThemeService │ │ │ ├── IThemeService.cs │ │ │ ├── SystemThemeJSModule.cs │ │ │ └── ThemeService.cs │ │ ├── SettingService │ │ │ ├── ISettingsService.cs │ │ │ └── SettingService.cs │ │ ├── WeatherService │ │ │ └── IWeatherService.cs │ │ └── StaticWebAssets │ │ │ ├── IStaticWebAssets.cs │ │ │ └── StaticWebAssets.cs │ ├── _Imports.razor │ ├── Components │ │ ├── WeatherLivesInfoCard.razor.cs │ │ ├── WeatherLivesInfoCard.razor │ │ └── PageComponentBase.cs │ ├── Pages │ │ ├── WeatherPage.razor.css │ │ ├── AddLocationPage.razor │ │ ├── WeatherSourcePage.razor.cs │ │ ├── WeatherSourcePage.razor │ │ ├── WeatherPage.razor.cs │ │ └── LocationPage.razor.cs │ └── YourWeather.Rcl.csproj └── YourWeather.Shared │ ├── Models │ ├── Enum │ │ ├── ThemeType.cs │ │ ├── SettingType.cs │ │ └── WeatherSourceType.cs │ ├── WeatherDate │ │ ├── WeatherForecastHours.cs │ │ ├── WeatherData.cs │ │ ├── WeatherForecastDay.cs │ │ └── WeatherLives.cs │ ├── WeatherSource │ │ ├── IWeatherSource.cs │ │ ├── BaseWeatherSource.cs │ │ ├── OpenWeatherSource.cs │ │ └── SeniverseSource.cs │ ├── CodeSource.cs │ ├── Location.cs │ └── WeatherResult │ │ ├── VisualCrossingResult.cs │ │ ├── OpenWeatherResult.cs │ │ ├── SeniverseResult.cs │ │ ├── QWeatherResult.cs │ │ └── AmapResult.cs │ ├── YourWeather.Shared.csproj │ └── Extensions │ ├── DateTimeExtensions.cs │ └── WeatherExtensions.cs ├── .github └── workflows │ ├── sync-gitee-pages.yml │ └── gh-pages.yml ├── LICENSE.txt └── .gitattributes /YourWeather/YourWeather.Rcl.Web/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /YourWeather/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/README.md -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Desktop/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/myapp.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Maui/myapp.keystore -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Maui/wwwroot/favicon.ico -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/Resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Wpf/Resources/favicon.ico -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Photino/wwwroot/favicon.ico -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Server/wwwroot/favicon.ico -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Server/wwwroot/favicon.png -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/Resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Photino/Resources/favicon.ico -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/Resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Winform/Resources/favicon.ico -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.WebAssembly/wwwroot/favicon.ico -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.WebAssembly/wwwroot/favicon.png -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.WebAssembly/wwwroot/icon-192.png -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/wwwroot/icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.WebAssembly/wwwroot/icon-512.png -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Resources/Fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Maui/Resources/Fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Services/WeatherService.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Maui.Services 2 | { 3 | public class WeatherService : Rcl.Services.WeatherService 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Windows Machine": { 4 | "commandName": "MsixPackage", 5 | "nativeDebugging": false 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/qweather-icon/fonts/qweather-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/qweather-icon/fonts/qweather-icons.ttf -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/qweather-icon/fonts/qweather-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/qweather-icon/fonts/qweather-icons.woff -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/qweather-icon/fonts/qweather-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/qweather-icon/fonts/qweather-icons.woff2 -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/Enum/ThemeType.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public enum ThemeType 4 | { 5 | System, 6 | Light, 7 | Dark 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/material/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/material/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2 -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-v4compatibility.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-v4compatibility.ttf -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-v4compatibility.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/fontawesome/v6.4.0/webfonts/fa-v4compatibility.woff2 -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "DetailedErrors": true, 3 | "Logging": { 4 | "LogLevel": { 5 | "Default": "Information", 6 | "Microsoft.AspNetCore": "Warning" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.eot -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.woff -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/Enum/SettingType.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public enum SettingType 4 | { 5 | Theme, 6 | WeatherSource, 7 | Location, 8 | Locations 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yu-Core/YourWeather/HEAD/YourWeather/YourWeather.Rcl/wwwroot/css/materialdesign/v7.1.96/fonts/materialdesignicons-webfont.woff2 -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "7.0.9", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/PlatformIntegration/IPlatformIntegration.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Rcl.Services 2 | { 3 | public interface IPlatformIntegration 4 | { 5 | Task OpenBrowserUrl(string url); 6 | string GetVersion(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/YourWeather.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/Enum/WeatherSourceType.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public enum WeatherSourceType 4 | { 5 | Amap, 6 | OpenWeather, 7 | QWeather, 8 | Seniverse, 9 | VisualCrossing 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/App.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Maui 2 | { 3 | public partial class App : Application 4 | { 5 | public App() 6 | { 7 | InitializeComponent(); 8 | 9 | MainPage = new MainPage(); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Resources/AppIcon/appicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Android/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #512BD4 4 | #2B0B98 5 | #2B0B98 6 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/LocationService/ILocationService.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Shared; 2 | 3 | namespace YourWeather.Rcl.Services 4 | { 5 | public interface ILocationService 6 | { 7 | Task GetCurrentLocation(); 8 | Task> GetAllLocations(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/IPermissionRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Web.WebView2.Core; 2 | 3 | namespace YourWeather.Wpf 4 | { 5 | internal interface IPermissionRequestHandler 6 | { 7 | void OnPermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Foundation; 2 | 3 | namespace YourWeather.Maui 4 | { 5 | [Register("AppDelegate")] 6 | public class AppDelegate : MauiUIApplicationDelegate 7 | { 8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 9 | } 10 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherDate/WeatherForecastHours.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class WeatherForecastHours 4 | { 5 | public DateTime DateTime { get; set; } 6 | public string? Weather { get; set; } 7 | public string? Temp { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/MacCatalyst/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Foundation; 2 | 3 | namespace YourWeather.Maui 4 | { 5 | [Register("AppDelegate")] 6 | public class AppDelegate : MauiUIApplicationDelegate 7 | { 8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 9 | } 10 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Windows/IPermissionRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Web.WebView2.Core; 2 | 3 | namespace YourWeather.Maui.Platforms.Windows; 4 | 5 | internal interface IPermissionRequestHandler 6 | { 7 | void OnPermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args); 8 | } 9 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/wwwroot/service-worker.js: -------------------------------------------------------------------------------- 1 | // In development, always fetch from the network and do not enable offline support. 2 | // This is because caching would make development more difficult (changes would not 3 | // be reflected on the first load after each change). 4 | self.addEventListener('fetch', () => { }); 5 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/IPermissionRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Web.WebView2.Core; 2 | 3 | namespace YourWeather.Winform 4 | { 5 | public interface IPermissionRequestHandler 6 | { 7 | void OnPermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/ThemeService/IThemeService.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Shared; 2 | 3 | namespace YourWeather.Rcl.Services 4 | { 5 | public interface IThemeService 6 | { 7 | event Action? OnChanged; 8 | ThemeType RealTheme { get; } 9 | Task SetThemeAsync(ThemeType theme); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Routes.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherDate/WeatherData.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class WeatherData 4 | { 5 | public WeatherLives? Lives { get; set; } 6 | public List? ForecastHours { get; set; } 7 | public List? ForecastDays { get; set; } 8 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/js/platformIntegration.js: -------------------------------------------------------------------------------- 1 | export const openBrowserUrl = (url) => { 2 | const a = document.createElement("a"); 3 | a.href = url; 4 | a.target = "_blank"; 5 | a.style.display = "none"; 6 | document.body.appendChild(a); 7 | a.click(); 8 | setTimeout(() => { 9 | a.remove(); 10 | }, 1000); 11 | } 12 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherDate/WeatherForecastDay.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class WeatherForecastDay 4 | { 5 | public DateTime Date { get; set; } 6 | public string? Weather { get; set; } 7 | public string? MinTemp { get; set; } 8 | public string? MaxTemp { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/Routes.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Windows/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.AspNetCore.Components.Web.Virtualization 6 | @using Microsoft.JSInterop 7 | @using YourWeather.Wpf 8 | @using YourWeather.Shared 9 | @using YourWeather.Rcl.Layout -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.AspNetCore.Components.Web.Virtualization 6 | @using Microsoft.JSInterop 7 | @using YourWeather.Winform 8 | @using YourWeather.Shared 9 | @using YourWeather.Rcl.Layout -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | @using Microsoft.AspNetCore.Components.Routing 3 | @using BlazorComponent 4 | @using BlazorComponent.I18n; 5 | @using Masa.Blazor 6 | @using Masa.Blazor.Presets; 7 | @using YourWeather.Rcl.Layout 8 | @using YourWeather.Rcl.Components 9 | @using YourWeather.Rcl.Services 10 | @using YourWeather.Shared 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Extensions/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public static class DateTimeExtensions 4 | { 5 | static readonly string[] Day = new string[] { "周日", "周一", "周二", "周三", "周四", "周五", "周六" }; 6 | public static string ToWeek(this DayOfWeek dayOfWeek) 7 | { 8 | return Day[Convert.ToInt16(dayOfWeek)]; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Desktop/Layout/MainLayout.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Shared; 2 | 3 | namespace YourWeather.Rcl.Desktop.Layout 4 | { 5 | public class MainLayout : Rcl.Layout.MainLayout 6 | { 7 | protected override void ThemeChanged(ThemeType value) 8 | { 9 | base.ThemeChanged(value); 10 | 11 | TitleBar.EnableDarkMode(value == ThemeType.Dark); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/js/systemTheme.js: -------------------------------------------------------------------------------- 1 | export const registerForSystemThemeChanged = (dotnetObj, callbackMethodName) => { 2 | const media = matchMedia('(prefers-color-scheme: dark)'); 3 | if (media && dotnetObj) { 4 | media.addEventListener('change', (e) => { 5 | dotnetObj.invokeMethodAsync(callbackMethodName, e.matches); 6 | }); 7 | } 8 | 9 | return media.matches; 10 | } 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/YourWeather.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 1.4.4 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/Layout/MainLayout.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Shared; 2 | 3 | namespace YourWeather.Photino.Layout 4 | { 5 | public class MainLayout : Rcl.Layout.MainLayout 6 | { 7 | protected override void ThemeChanged(ThemeType value) 8 | { 9 | base.ThemeChanged(value); 10 | #if Windows 11 | TitleBar.EnableDarkMode(value == ThemeType.Dark); 12 | #endif 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherSource/IWeatherSource.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public interface IWeatherSource 4 | { 5 | public string? Name { get; set; } 6 | public string? Description { get; set; } 7 | public string? Key { get; set; } 8 | public WeatherData? WeatherData { get; set; } 9 | public Task GetWeatherData(Location location); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/CodeSource.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class CodeSource 4 | { 5 | public CodeSource(string name,string icon,string url) 6 | { 7 | Name = name; 8 | Icon = icon; 9 | Url = url; 10 | } 11 | public string? Name { get; set; } 12 | public string? Icon { get; set; } 13 | public string? Url { get;set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Tizen/Main.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Maui; 2 | using Microsoft.Maui.Hosting; 3 | using System; 4 | 5 | namespace YourWeather.Maui 6 | { 7 | internal class Program : MauiApplication 8 | { 9 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 10 | 11 | static void Main(string[] args) 12 | { 13 | var app = new Program(); 14 | app.Run(args); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.AspNetCore.Components.Web.Virtualization 6 | @using Microsoft.JSInterop 7 | @using BlazorComponent 8 | @using Masa.Blazor 9 | @using Masa.Blazor.Presets; 10 | @using YourWeather.Maui 11 | @using YourWeather.Rcl.Layout 12 | @using YourWeather.Shared 13 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Android/MainApplication.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Runtime; 3 | 4 | namespace YourWeather.Maui 5 | { 6 | [Application] 7 | public class MainApplication : MauiApplication 8 | { 9 | public MainApplication(IntPtr handle, JniHandleOwnership ownership) 10 | : base(handle, ownership) 11 | { 12 | } 13 | 14 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 15 | } 16 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Android/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content.PM; 3 | using Android.OS; 4 | 5 | namespace YourWeather.Maui 6 | { 7 | [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] 8 | public class MainActivity : MauiAppCompatActivity 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Components/WeatherLivesInfoCard.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components; 2 | 3 | namespace YourWeather.Rcl.Components 4 | { 5 | public partial class WeatherLivesInfoCard 6 | { 7 | [Parameter] 8 | public string? Value { get; set; } 9 | [Parameter] 10 | public string? Name { get; set; } 11 | [Parameter] 12 | public string? Icon { get; set; } 13 | [Parameter] 14 | public string? Unit { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/iOS/Program.cs: -------------------------------------------------------------------------------- 1 | using ObjCRuntime; 2 | using UIKit; 3 | 4 | namespace YourWeather.Maui 5 | { 6 | public class Program 7 | { 8 | // This is the main entry point of the application. 9 | static void Main(string[] args) 10 | { 11 | // if you want to use a different Application Delegate class from "AppDelegate" 12 | // you can specify it here. 13 | UIApplication.Main(args, null, typeof(AppDelegate)); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/SettingService/ISettingsService.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Shared; 2 | 3 | namespace YourWeather.Rcl.Services 4 | { 5 | public interface ISettingsService 6 | { 7 | Task Get(SettingType type); 8 | Task Get(SettingType type, T defaultValue); 9 | Task Get(string key, T defaultValue); 10 | Task Save(SettingType type, T value); 11 | Task Save(string key, T value); 12 | Task ContainsKey(string key); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/WeatherService/IWeatherService.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Shared; 2 | 3 | namespace YourWeather.Rcl.Services 4 | { 5 | public interface IWeatherService 6 | { 7 | Dictionary WeatherSources { get; } 8 | string GetWeatherIcon(string Weather); 9 | string GetWeatherIcon(string Weather, DateTime dateTime); 10 | Task GetWeatherData(WeatherSourceType weather, Location location, string? key = null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/MacCatalyst/Program.cs: -------------------------------------------------------------------------------- 1 | using ObjCRuntime; 2 | using UIKit; 3 | 4 | namespace YourWeather.Maui 5 | { 6 | public class Program 7 | { 8 | // This is the main entry point of the application. 9 | static void Main(string[] args) 10 | { 11 | // if you want to use a different Application Delegate class from "AppDelegate" 12 | // you can specify it here. 13 | UIApplication.Main(args, null, typeof(AppDelegate)); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Components.Forms 3 | @using Microsoft.AspNetCore.Components.Routing 4 | @using Microsoft.AspNetCore.Components.Web 5 | @using Microsoft.AspNetCore.Components.Web.Virtualization 6 | @using Microsoft.JSInterop 7 | @using BlazorComponent 8 | @using Masa.Blazor 9 | @using Masa.Blazor.Presets 10 | @using System.Net.Http.Json 11 | @using System.IO; 12 | @using System.Text.Json; 13 | @using YourWeather.Shared 14 | @using YourWeather.Rcl.Layout 15 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Services/PlatformIntegration.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Rcl.Services; 2 | 3 | namespace YourWeather.Maui.Services 4 | { 5 | public class PlatformIntegration : IPlatformIntegration 6 | { 7 | public string GetVersion() 8 | { 9 | return VersionTracking.Default.CurrentVersion.ToString(); 10 | } 11 | 12 | public Task OpenBrowserUrl(string url) 13 | { 14 | return Browser.Default.OpenAsync(url, BrowserLaunchMode.External); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using Microsoft.AspNetCore.Authorization 3 | @using Microsoft.AspNetCore.Components.Authorization 4 | @using Microsoft.AspNetCore.Components.Forms 5 | @using Microsoft.AspNetCore.Components.Routing 6 | @using Microsoft.AspNetCore.Components.Web 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using BlazorComponent 10 | @using Masa.Blazor 11 | @using Masa.Blazor.Presets 12 | @using YourWeather.Server 13 | @using YourWeather.Rcl 14 | @using YourWeather.Rcl.Layout 15 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using BlazorComponent 10 | @using Masa.Blazor 11 | @using Masa.Blazor.Presets 12 | @using YourWeather.WebAssembly 13 | @using YourWeather.Rcl 14 | @using YourWeather.Rcl.Layout 15 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/wwwroot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "YourWeather.WebAssembly", 3 | "short_name": "YourWeather.WebAssembly", 4 | "start_url": "./", 5 | "display": "standalone", 6 | "background_color": "#ffffff", 7 | "theme_color": "#03173d", 8 | "prefer_related_applications": false, 9 | "icons": [ 10 | { 11 | "src": "icon-512.png", 12 | "type": "image/png", 13 | "sizes": "512x512" 14 | }, 15 | { 16 | "src": "icon-192.png", 17 | "type": "image/png", 18 | "sizes": "192x192" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Layout/MainLayout.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Maui.Services; 2 | using YourWeather.Shared; 3 | 4 | namespace YourWeather.Maui.Layout 5 | { 6 | public class MainLayout : Rcl.Layout.MainLayout 7 | { 8 | protected override Task InitThemeServiceAsync() 9 | { 10 | return Task.CompletedTask; 11 | } 12 | 13 | protected override void ThemeChanged(ThemeType value) 14 | { 15 | base.ThemeChanged(value); 16 | 17 | TitleBarOrStatusBar.SetTitleBarOrStatusBar(value); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/GeolocationEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Darnton.Blazor.DeviceInterop.Geolocation 4 | { 5 | /// 6 | /// Geolocation event data. Provides the with the position or error associated with the event. 7 | /// 8 | public class GeolocationEventArgs : EventArgs 9 | { 10 | /// 11 | /// The associated with the event. 12 | /// 13 | public GeolocationResult GeolocationResult { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/material/icons.css: -------------------------------------------------------------------------------- 1 | /* fallback */ 2 | @font-face { 3 | font-family: 'Material Icons'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: url(./flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2'); 7 | } 8 | 9 | .material-icons { 10 | font-family: 'Material Icons'; 11 | font-weight: normal; 12 | font-style: normal; 13 | font-size: 24px; 14 | line-height: 1; 15 | letter-spacing: normal; 16 | text-transform: none; 17 | display: inline-block; 18 | white-space: nowrap; 19 | word-wrap: normal; 20 | direction: ltr; 21 | -webkit-font-smoothing: antialiased; 22 | } 23 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Extensions/ServiceCollectionExtensions/AddMauiExceptionHandle.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | using System.Diagnostics; 4 | 5 | namespace YourWeather.Maui.Extensions 6 | { 7 | public static partial class ServiceCollectionExtensions 8 | { 9 | public static IServiceCollection AddMauiExceptionHandle(this IServiceCollection services) 10 | { 11 | MauiExceptions.UnhandledException += 12 | (sender, args) => 13 | { 14 | #if DEBUG 15 | Debug.WriteLine(args.ExceptionObject.ToString()); 16 | #endif 17 | }; 18 | return services; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebView; 2 | 3 | namespace YourWeather.Maui 4 | { 5 | public partial class MainPage : ContentPage 6 | { 7 | public MainPage() 8 | { 9 | InitializeComponent(); 10 | 11 | blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing; 12 | blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; 13 | } 14 | 15 | private partial void BlazorWebViewInitializing(object sender, BlazorWebViewInitializingEventArgs e); 16 | private partial void BlazorWebViewInitialized(object sender, BlazorWebViewInitializedEventArgs e); 17 | } 18 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/Location.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace YourWeather.Shared 8 | { 9 | public class Location 10 | { 11 | public Location() { } 12 | public Location(string? name, string? info, double lat, double lon) 13 | { 14 | Name = name; 15 | Info = info; 16 | Lat = lat; 17 | Lon = lon; 18 | } 19 | 20 | public string? Name { get; set; } 21 | public string? Info { get; set; } 22 | public double Lat { get; set; } 23 | public double Lon { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/SilentPermissionRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Web.WebView2.Core; 2 | 3 | namespace YourWeather.Wpf 4 | { 5 | public class SilentPermissionRequestHandler : IPermissionRequestHandler 6 | { 7 | private static readonly Uri BaseUri = new("https://0.0.0.0"); 8 | 9 | public void OnPermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args) 10 | { 11 | args.State = Uri.TryCreate(args.Uri, UriKind.RelativeOrAbsolute, out var uri) && BaseUri.IsBaseOf(uri) ? 12 | CoreWebView2PermissionState.Allow : 13 | CoreWebView2PermissionState.Deny; 14 | args.Handled = true; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Windows/SilentPermissionRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Web.WebView2.Core; 2 | using System; 3 | 4 | namespace YourWeather.Maui.Platforms.Windows; 5 | 6 | internal class SilentPermissionRequestHandler : IPermissionRequestHandler 7 | { 8 | private static readonly Uri BaseUri = new("https://0.0.0.0"); 9 | 10 | public void OnPermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args) 11 | { 12 | args.State = Uri.TryCreate(args.Uri, UriKind.RelativeOrAbsolute, out var uri) && BaseUri.IsBaseOf(uri) ? 13 | CoreWebView2PermissionState.Allow : 14 | CoreWebView2PermissionState.Deny; 15 | args.Handled = true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/SilentPermissionRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Web.WebView2.Core; 2 | 3 | namespace YourWeather.Winform 4 | { 5 | public class SilentPermissionRequestHandler : IPermissionRequestHandler 6 | { 7 | private static readonly Uri BaseUri = new("https://0.0.0.0"); 8 | 9 | public void OnPermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args) 10 | { 11 | args.State = Uri.TryCreate(args.Uri, UriKind.RelativeOrAbsolute, out var uri) && BaseUri.IsBaseOf(uri) ? 12 | CoreWebView2PermissionState.Allow : 13 | CoreWebView2PermissionState.Deny; 14 | args.Handled = true; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Resources/Raw/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories). Deployment of the asset to your application 3 | is automatically handled by the following `MauiAsset` Build Action within your `.csproj`. 4 | 5 | 6 | 7 | These files will be deployed with you package and will be accessible using Essentials: 8 | 9 | async Task LoadMauiAsset() 10 | { 11 | using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); 12 | using var reader = new StreamReader(stream); 13 | 14 | var contents = reader.ReadToEnd(); 15 | } 16 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Web/YourWeather.Rcl.Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace YourWeather.Wpf 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | private void Application_Startup(object sender, StartupEventArgs e) 17 | { 18 | AppDomain.CurrentDomain.UnhandledException += (sender, error) => 19 | { 20 | MessageBox.Show(error.ExceptionObject.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); 21 | }; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Components/WeatherLivesInfoCard.razor: -------------------------------------------------------------------------------- 1 | @namespace YourWeather.Rcl.Components 2 | 3 | @if (!string.IsNullOrEmpty(Value)) 4 | { 5 | 6 | 7 | 8 | 9 | @Value@Unit 10 | 11 | 12 | @Name 13 | 14 | 15 | 16 | 17 | @Icon 18 | 19 | 20 | 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/GeolocationPositionError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Darnton.Blazor.DeviceInterop.Geolocation 4 | { 5 | /// 6 | /// The reason for a Geolocation error, based on . 7 | /// 8 | public class GeolocationPositionError 9 | { 10 | /// 11 | /// The for the error. 12 | /// 13 | public GeolocationPositionErrorCode Code { get; set; } 14 | 15 | /// 16 | /// Details of the error. Intended for debugging rather than display to the user. 17 | /// 18 | public string Message { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/Program.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Winform 2 | { 3 | internal static class Program 4 | { 5 | /// 6 | /// The main entry point for the application. 7 | /// 8 | [STAThread] 9 | static void Main() 10 | { 11 | AppDomain.CurrentDomain.UnhandledException += (sender, error) => 12 | { 13 | MessageBox.Show(text: error.ExceptionObject.ToString(), caption: "Error"); 14 | }; 15 | 16 | // To customize application configuration such as set high DPI settings or default font, 17 | // see https://aka.ms/applicationconfiguration. 18 | ApplicationConfiguration.Initialize(); 19 | Application.Run(new MainForm()); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/StaticWebAssets/IStaticWebAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace YourWeather.Rcl.Services 4 | { 5 | public interface IStaticWebAssets 6 | { 7 | /// 8 | /// 读取静态web资产的json文件 9 | /// 10 | /// 11 | /// 12 | /// 13 | Task ReadJsonAsync(string relativePath, bool isRcl = true, JsonSerializerOptions? jsonSerializerOptions = null); 14 | 15 | /// 16 | /// 读取静态web资产的文本内容 17 | /// 18 | /// 19 | /// 20 | Task ReadContentAsync(string relativePath, bool isRcl = true); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Windows/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | true/PM 12 | PerMonitorV2, PerMonitor 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Tizen/tizen-manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | maui-appicon-placeholder 7 | 8 | 9 | 10 | 11 | http://tizen.org/privilege/internet 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Services/SettingService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Maui.Storage; 2 | namespace YourWeather.Maui.Services 3 | { 4 | public class SettingService : Rcl.Services.SettingService 5 | { 6 | public override Task ContainsKey(string key) 7 | { 8 | var result = Preferences.Default.ContainsKey(key); 9 | return Task.FromResult(result); 10 | } 11 | 12 | public override Task Get(string key, T defaultValue) 13 | { 14 | var result = Preferences.Default.Get(key, defaultValue); 15 | return Task.FromResult(result); 16 | } 17 | 18 | public override Task Save(string key, T value) 19 | { 20 | Preferences.Default.Set(key, value); 21 | return Task.CompletedTask; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/Program.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using Darnton.Blazor.DeviceInterop.Geolocation; 3 | using Microsoft.AspNetCore.Components.Web; 4 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 5 | using YourWeather.WebAssembly; 6 | using YourWeather.WebAssembly.Extensions; 7 | 8 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 9 | builder.RootComponents.Add("#app"); 10 | builder.RootComponents.Add("head::after"); 11 | 12 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 13 | 14 | builder.Services.AddMasaBlazor(); 15 | builder.Services.AddBlazoredLocalStorage(); 16 | builder.Services.AddScoped(); 17 | builder.Services.AddDependencyInjection(); 18 | 19 | await builder.Build().RunAsync(); 20 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/Pages/Error.cshtml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using System.Diagnostics; 4 | 5 | namespace YourWeather.Server.Pages 6 | { 7 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 8 | [IgnoreAntiforgeryToken] 9 | public class ErrorModel : PageModel 10 | { 11 | public string? RequestId { get; set; } 12 | 13 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 14 | 15 | private readonly ILogger _logger; 16 | 17 | public ErrorModel(ILogger logger) 18 | { 19 | _logger = logger; 20 | } 21 | 22 | public void OnGet() 23 | { 24 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/StaticWebAssets/StaticWebAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace YourWeather.Rcl.Services 4 | { 5 | public abstract class StaticWebAssets : IStaticWebAssets 6 | { 7 | private static readonly Lazy _rclAssemblyName = new(() => typeof(StaticWebAssets).Assembly.GetName().Name); 8 | 9 | protected virtual JsonSerializerOptions DefaultJsonSerializerOptions { get; } = new() 10 | { 11 | PropertyNameCaseInsensitive = true 12 | }; 13 | 14 | public static string? RclAssemblyName => _rclAssemblyName.Value; 15 | 16 | public abstract Task ReadJsonAsync(string relativePath, bool isRcl = true, JsonSerializerOptions? jsonSerializerOptions = null); 17 | 18 | public abstract Task ReadContentAsync(string relativePath, bool isRcl = true); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Desktop/YourWeather.Rcl.Desktop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Extensions/ServiceCollectionExtensions/AddDependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Rcl.Services; 2 | 3 | namespace YourWeather.Maui.Extensions 4 | { 5 | public static partial class ServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddDependencyInjection(this IServiceCollection services) 8 | { 9 | services.AddSingleton(); 10 | services.AddScoped(); 11 | services.AddSingleton(); 12 | services.AddScoped(); 13 | services.AddSingleton(); 14 | services.AddScoped(); 15 | return services; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Windows/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | 3 | // To learn more about WinUI, the WinUI project structure, 4 | // and more about our project templates, see: http://aka.ms/winui-project-info. 5 | 6 | namespace YourWeather.Maui.WinUI 7 | { 8 | /// 9 | /// Provides application-specific behavior to supplement the default Application class. 10 | /// 11 | public partial class App : MauiWinUIApplication 12 | { 13 | /// 14 | /// Initializes the singleton application object. This is the first line of authored code 15 | /// executed, and as such is the logical equivalent of main() or WinMain(). 16 | /// 17 | public App() 18 | { 19 | this.InitializeComponent(); 20 | } 21 | 22 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 23 | } 24 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/Extensions/ServiceCollectionExtensions/AddDependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Rcl.Services; 2 | 3 | namespace YourWeather.Server.Extensions 4 | { 5 | public static partial class ServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddDependencyInjection(this IServiceCollection services) 8 | { 9 | services.AddScoped(); 10 | services.AddScoped(); 11 | services.AddScoped(); 12 | services.AddScoped(); 13 | services.AddScoped(); 14 | services.AddScoped(); 15 | return services; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/MainPage.xaml.MaciOS.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebView; 2 | 3 | namespace YourWeather.Maui; 4 | 5 | public partial class MainPage 6 | { 7 | // To manage iOS permissions, update Info.plist to include the necessary keys to access 8 | // the APIs required by your app. You may have to perform additional configuration to enable 9 | // use of those APIs from the WebView, as is done below. 10 | 11 | private partial void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e) 12 | { 13 | e.Configuration.AllowsInlineMediaPlayback = true; 14 | e.Configuration.MediaTypesRequiringUserActionForPlayback = WebKit.WKAudiovisualMediaTypes.None; 15 | } 16 | 17 | private partial void BlazorWebViewInitialized(object sender, BlazorWebViewInitializedEventArgs e) 18 | { 19 | e.WebView.ScrollView.ShowsVerticalScrollIndicator = false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/Extensions/ServiceCollectionExtensions/AddDependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using YourWeather.Rcl.Services; 3 | 4 | namespace YourWeather.Photino.Extensions 5 | { 6 | public static partial class ServiceCollectionExtend 7 | { 8 | public static IServiceCollection AddDependencyInjection(this IServiceCollection services) 9 | { 10 | services.AddScoped(); 11 | services.AddScoped(); 12 | services.AddSingleton(); 13 | services.AddScoped(); 14 | services.AddSingleton(); 15 | services.AddScoped(); 16 | return services; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/Extensions/ServiceCollectionExtensions/AddDependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Rcl.Services; 2 | 3 | namespace YourWeather.WebAssembly.Extensions 4 | { 5 | public static partial class ServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddDependencyInjection(this IServiceCollection services) 8 | { 9 | services.AddScoped(); 10 | services.AddScoped(); 11 | services.AddScoped(); 12 | services.AddScoped(); 13 | services.AddScoped(); 14 | services.AddScoped(); 15 | return services; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/YourWeather.WebAssembly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0 4 | enable 5 | enable 6 | service-worker-assets.js 7 | 1.4.4 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/sync-gitee-pages.yml: -------------------------------------------------------------------------------- 1 | name: Sync Gitee Pages 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Sync to Gitee 11 | uses: morbalint/git-mirror-action@master 12 | env: 13 | SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} 14 | with: 15 | source_repo: "git@github.com:Yu-Core/YourWeather.git" 16 | destination_repo: "git@gitee.com:Yu-core/YourWeather.git" 17 | branches: "gh-pages" 18 | force_push_branches: true 19 | 20 | - name: Build Gitee Pages 21 | uses: yanglbme/gitee-pages-action@master 22 | with: 23 | # 注意替换为你的 Gitee 用户名 24 | gitee-username: Yu-core 25 | # 注意在 Settings->Secrets 配置 GITEE_PASSWORD 26 | gitee-password: ${{ secrets.GITEE_PASSWORD }} 27 | # 注意替换为你的 Gitee 仓库 28 | gitee-repo: Yu-core/YourWeather 29 | branch: gh-pages 30 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Pages/WeatherPage.razor.css: -------------------------------------------------------------------------------- 1 | .weather-lives-item { 2 | display: flex; 3 | flex-wrap: wrap; 4 | justify-content: space-between; 5 | } 6 | 7 | .weather-lives-item ::deep > * { 8 | width: 48%; 9 | margin-bottom: 20px; 10 | } 11 | 12 | @media screen and (min-width:960px) and (max-width:1264px) { 13 | .weather-lives-item ::deep > * { 14 | width: 32%; 15 | } 16 | 17 | .weather-lives-item ::deep > *:last-child:nth-child(3n + 5) { 18 | margin-right: 34%; 19 | } 20 | } 21 | 22 | @media screen and (min-width:1264px) { 23 | .weather-lives-item ::deep > * { 24 | width: 23%; 25 | } 26 | 27 | .weather-lives-item ::deep > *:last-child:nth-child(4n + 6) { 28 | margin-right: calc(46% + 8% / 3 * 2); 29 | } 30 | 31 | .weather-lives-item ::deep > *:last-child:nth-child(4n + 7) { 32 | margin-right: calc(23% + 8% / 3 ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/Extensions/ServiceCollectionExtensions/AddDependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using YourWeather.Rcl.Services; 3 | 4 | namespace YourWeather.Wpf.Extend 5 | { 6 | public static partial class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddDependencyInjection(this IServiceCollection services) 9 | { 10 | services.AddScoped(); 11 | services.AddScoped(); 12 | services.AddSingleton(); 13 | services.AddScoped(); 14 | services.AddSingleton(); 15 | services.AddScoped(); 16 | return services; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/YourWeather.Rcl.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/Extensions/ServiceCollectionExtensions/AddDependencyInjection.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using YourWeather.Rcl.Services; 3 | 4 | namespace YourWeather.Winform.Extensions 5 | { 6 | public static partial class ServiceCollectionExtensions 7 | { 8 | public static IServiceCollection AddDependencyInjection(this IServiceCollection services) 9 | { 10 | services.AddScoped(); 11 | services.AddScoped(); 12 | services.AddSingleton(); 13 | services.AddScoped(); 14 | services.AddSingleton(); 15 | services.AddScoped(); 16 | return services; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherSource/BaseWeatherSource.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public abstract class BaseWeatherSource : IWeatherSource 4 | { 5 | protected string? _defaultKey; 6 | protected string? _key; 7 | public string? Name { get; set; } 8 | public string? Description { get; set; } 9 | public string? Key 10 | { 11 | get 12 | { 13 | if(!string.IsNullOrEmpty(_key)) 14 | { 15 | return _key; 16 | } 17 | 18 | return _defaultKey; 19 | } 20 | set 21 | { 22 | _defaultKey ??= value; 23 | _key = value; 24 | } 25 | } 26 | public WeatherData? WeatherData { get; set; } 27 | 28 | public virtual Task GetWeatherData(Location location) 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/JSBinder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Threading.Tasks; 3 | 4 | namespace Darnton.Blazor.DeviceInterop 5 | { 6 | internal class JSBinder 7 | { 8 | internal IJSRuntime JSRuntime; 9 | private string _importPath; 10 | private Task _module; 11 | 12 | public JSBinder(IJSRuntime jsRuntime, string importPath) 13 | { 14 | JSRuntime = jsRuntime; 15 | _importPath = importPath; 16 | } 17 | 18 | internal async Task GetModule() 19 | { 20 | return await (_module ??= JSRuntime.InvokeAsync("import", _importPath).AsTask()); 21 | } 22 | 23 | /// 24 | public async ValueTask DisposeAsync() 25 | { 26 | if (_module != null) 27 | { 28 | var module = await _module; 29 | await module.DisposeAsync(); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "iisExpress": { 4 | "applicationUrl": "http://localhost:58784", 5 | "sslPort": 44300 6 | } 7 | }, 8 | "profiles": { 9 | "http": { 10 | "commandName": "Project", 11 | "dotnetRunMessages": true, 12 | "launchBrowser": true, 13 | "applicationUrl": "http://localhost:5044", 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "https": { 19 | "commandName": "Project", 20 | "dotnetRunMessages": true, 21 | "launchBrowser": true, 22 | "applicationUrl": "https://localhost:7108;http://localhost:5044", 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | }, 27 | "IIS Express": { 28 | "commandName": "IISExpress", 29 | "launchBrowser": true, 30 | "environmentVariables": { 31 | "ASPNETCORE_ENVIRONMENT": "Development" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/GeolocationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Darnton.Blazor.DeviceInterop.Geolocation 5 | { 6 | /// 7 | /// The result of a geolocation request. Contains either a or a . 8 | /// 9 | public class GeolocationResult 10 | { 11 | /// 12 | /// The returned on successful geolocation. 13 | /// 14 | public GeolocationPosition Position { get; set; } 15 | /// 16 | /// The returned by a failed geolocation attempt. 17 | /// 18 | public GeolocationPositionError Error { get; set; } 19 | 20 | /// 21 | /// Indicates whether the geolocation attempt was successful. 22 | /// 23 | [JsonIgnore] 24 | public bool IsSuccess => !(Position is null); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/GeolocationPosition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Darnton.Blazor.DeviceInterop.Geolocation 5 | { 6 | /// 7 | /// Geolocation Position, based on . 8 | /// 9 | public class GeolocationPosition 10 | { 11 | /// 12 | /// The coordinates defining the current location 13 | /// 14 | public GeolocationCoordinates Coords { get; set; } 15 | 16 | /// 17 | /// The time the coordinates were taken, in milliseconds since the Unix epoch. 18 | /// 19 | public long Timestamp { get; set; } 20 | 21 | /// 22 | /// The derived from the , in UTC. 23 | /// 24 | [JsonIgnore] 25 | public DateTimeOffset DateTimeOffset => DateTimeOffset.FromUnixTimeMilliseconds(Timestamp); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/YourWeather.Photino.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | net8.0 6 | enable 7 | enable 8 | 1.4.4 9 | Resources/favicon.ico 10 | 11 | 12 | 13 | 14 | Always 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | Always 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/GeolocationPositionErrorCode.cs: -------------------------------------------------------------------------------- 1 | namespace Darnton.Blazor.DeviceInterop.Geolocation 2 | { 3 | /// 4 | /// An enumeration of error codes used by . 5 | /// . 6 | /// 7 | public enum GeolocationPositionErrorCode 8 | { 9 | /// 10 | /// Geolocation failoed because the device does not support geolocation. Not part of W3C spec. 11 | /// 12 | DEVICE_NOT_SUPPORTED = 0, 13 | /// 14 | /// Geolocation failed because permission to access location was denied. 15 | /// 16 | PERMISSION_DENIED = 1, 17 | /// 18 | /// Geolocation failed because of an internal error on the device. 19 | /// 20 | POSITION_UNAVAILABLE = 2, 21 | /// 22 | /// Geolocation failed because no position was returned in time. 23 | /// 24 | TIMEOUT = 3 25 | } 26 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Yu-Core 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/Services/SettingService.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | 3 | namespace YourWeather.Photino.Services 4 | { 5 | public class SettingService : Rcl.Services.SettingService 6 | { 7 | private ILocalStorageService LocalStorage { get; set; } 8 | 9 | public SettingService(ILocalStorageService localStorageService) 10 | { 11 | LocalStorage = localStorageService; 12 | } 13 | 14 | public override async Task ContainsKey(string key) 15 | { 16 | return await LocalStorage.ContainKeyAsync(key); 17 | } 18 | 19 | public override async Task Get(string key, T defaultValue) 20 | { 21 | bool flag = await LocalStorage.ContainKeyAsync(key); 22 | if (!flag) 23 | { 24 | return defaultValue; 25 | } 26 | 27 | return await LocalStorage.GetItemAsync(key); 28 | } 29 | 30 | public override async Task Save(string key, T value) 31 | { 32 | await LocalStorage.SetItemAsync(key, value); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Web/Services/SettingService.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | 3 | namespace YourWeather.Rcl.Web.Services 4 | { 5 | public class SettingService : Rcl.Services.SettingService 6 | { 7 | private ILocalStorageService LocalStorage { get; set; } 8 | 9 | public SettingService(ILocalStorageService localStorageService) 10 | { 11 | LocalStorage = localStorageService; 12 | } 13 | 14 | public override async Task ContainsKey(string key) 15 | { 16 | return await LocalStorage.ContainKeyAsync(key); 17 | } 18 | 19 | public override async Task Get(string key, T defaultValue) 20 | { 21 | bool flag = await LocalStorage.ContainKeyAsync(key); 22 | if (!flag) 23 | { 24 | return defaultValue; 25 | } 26 | 27 | return await LocalStorage.GetItemAsync(key); 28 | } 29 | 30 | public override async Task Save(string key, T value) 31 | { 32 | await LocalStorage.SetItemAsync(key, value); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Desktop/Services/SettingService.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | 3 | namespace YourWeather.Rcl.Desktop.Services 4 | { 5 | public class SettingService : Rcl.Services.SettingService 6 | { 7 | private ILocalStorageService LocalStorage { get; set; } 8 | 9 | public SettingService(ILocalStorageService localStorageService) 10 | { 11 | LocalStorage = localStorageService; 12 | } 13 | 14 | public override async Task ContainsKey(string key) 15 | { 16 | return await LocalStorage.ContainKeyAsync(key); 17 | } 18 | 19 | public override async Task Get(string key, T defaultValue) 20 | { 21 | bool flag = await LocalStorage.ContainKeyAsync(key); 22 | if (!flag) 23 | { 24 | return defaultValue; 25 | } 26 | 27 | return await LocalStorage.GetItemAsync(key); 28 | } 29 | 30 | public override async Task Save(string key, T value) 31 | { 32 | await LocalStorage.SetItemAsync(key, value); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | /*去除滚动条*/ 2 | * { 3 | -ms-overflow-style: none; 4 | scrollbar-width: none; 5 | } 6 | 7 | ::-webkit-scrollbar { 8 | display: none; 9 | } 10 | 11 | /*显示滚动条*/ 12 | .scroll-show::-webkit-scrollbar-track { 13 | border-radius: 15px; 14 | margin: 5px 0; 15 | } 16 | 17 | .scroll-show::-webkit-scrollbar { 18 | display: initial; 19 | width: 5px; 20 | height: 5px; 21 | margin: 5px 0; 22 | } 23 | 24 | .scroll-show::-webkit-scrollbar-thumb { 25 | margin: 5px 0; 26 | border-radius: 15px; 27 | background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.44, rgb(170 168 168)), color-stop(0.72, rgb(170 168 168)), color-stop(0.86, rgb(170 168 168))); 28 | } 29 | 30 | /*改变应用栏颜色*/ 31 | .theme--light.m-app-bar.m-toolbar.m-sheet { 32 | background: #fff !important; 33 | } 34 | 35 | /*天气逐小时*/ 36 | .weather-hours{ 37 | display:flex; 38 | max-width:100%; 39 | overflow-x:auto; 40 | } 41 | 42 | /*禁止选中内容*/ 43 | .user-select-none *{ 44 | -webkit-user-select: none; 45 | -moz-user-select: none; 46 | -ms-user-select: none; 47 | user-select: none; 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/Program.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using Darnton.Blazor.DeviceInterop.Geolocation; 3 | using YourWeather.Server.Extensions; 4 | 5 | var builder = WebApplication.CreateBuilder(args); 6 | // https://github.com/dotnet/aspnetcore/issues/38212 7 | builder.WebHost.UseStaticWebAssets(); 8 | 9 | // Add services to the container. 10 | builder.Services.AddRazorPages(); 11 | builder.Services.AddServerSideBlazor(); 12 | builder.Services.AddMasaBlazor(); 13 | builder.Services.AddBlazoredLocalStorage(); 14 | builder.Services.AddScoped(); 15 | builder.Services.AddDependencyInjection(); 16 | 17 | var app = builder.Build(); 18 | 19 | // Configure the HTTP request pipeline. 20 | if (!app.Environment.IsDevelopment()) 21 | { 22 | app.UseExceptionHandler("/Error"); 23 | // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. 24 | app.UseHsts(); 25 | } 26 | 27 | app.UseHttpsRedirection(); 28 | 29 | app.UseStaticFiles(); 30 | 31 | app.UseRouting(); 32 | 33 | app.MapBlazorHub(); 34 | app.MapFallbackToPage("/_Host"); 35 | 36 | app.Run(); 37 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Darnton.Blazor.DeviceInterop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | Darnton.Blazor.DeviceInterop 6 | true 7 | 0.1.3 8 | Bernard Darnton 9 | Blazor device interop library 10 | Blazor device interop library including wrappers for Geolocation API. Updated to .NET 5 11 | 2020 Bernard Darnton 12 | MIT 13 | https://github.com/darnton/BlazorDeviceInterop 14 | 0.0.1.3 15 | 0.0.1.3 16 | 17 | 18 | 19 | Darnton.Blazor.DeviceInterop.xml 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/App.xaml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | #fff 10 | #000 11 | 12 | 16 | 17 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/PositionOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Darnton.Blazor.DeviceInterop.Geolocation 2 | { 3 | /// 4 | /// Option properties to be passed to Geolocation functions, based on https://developer.mozilla.org/en-US/docs/Web/API/PositionOptions. 5 | /// 6 | public class PositionOptions 7 | { 8 | /// 9 | /// Enable high accuracy mode for best possible results. 10 | /// May be slower or increase power consumption. Defaults to false. 11 | /// 12 | public bool EnableHighAccuracy { get; set; } = false; 13 | 14 | /// 15 | /// Maximum length of time allowed to return a position (in milliseconds). 16 | /// Set to null for no timeout. Defaults to null. 17 | /// 18 | public long? Timeout { get; set; } = null; 19 | 20 | /// 21 | /// Maximum allowed age for a cached result. 22 | /// Set to null to disregard the age of cached results. 23 | /// Set to 0 to skip the cache and attempt a fresh result every time. Defaults to 0. 24 | /// 25 | public long? MaximumAge { get; set; } = 0; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/ThemeService/SystemThemeJSModule.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using YourWeather.Rcl.Services; 3 | using YourWeather.Shared; 4 | 5 | namespace SwashbucklerDiary.WebAssembly.Essentials 6 | { 7 | public class SystemThemeJSModule : BlazorComponent.JSInterop.JSModule 8 | { 9 | public ThemeType SystemTheme { get; set; } 10 | 11 | public event Func? SystemThemeChanged; 12 | 13 | public SystemThemeJSModule(IJSRuntime js) : base(js, $"./_content/{StaticWebAssets.RclAssemblyName}/js/systemTheme.js") 14 | { 15 | } 16 | 17 | public async Task InitializedAsync() 18 | { 19 | var dotNetObject = DotNetObjectReference.Create(this); 20 | bool dark = await InvokeAsync("registerForSystemThemeChanged", [dotNetObject, nameof(OnSystemThemeChanged)]); 21 | SystemTheme = dark ? ThemeType.Dark : ThemeType.Light; 22 | } 23 | 24 | [JSInvokable] 25 | public Task OnSystemThemeChanged(bool isDarkTheme) 26 | { 27 | SystemTheme = isDarkTheme ? ThemeType.Dark : ThemeType.Light; 28 | return SystemThemeChanged?.Invoke(SystemTheme) ?? Task.CompletedTask; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/Program.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using Darnton.Blazor.DeviceInterop.Geolocation; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Photino.Blazor; 5 | using YourWeather.Photino; 6 | using YourWeather.Photino.Extensions; 7 | 8 | internal class Program 9 | { 10 | [STAThread] 11 | private static void Main(string[] args) 12 | { 13 | var appBuilder = PhotinoBlazorAppBuilder.CreateDefault(args); 14 | 15 | appBuilder.RootComponents.Add("#app"); 16 | appBuilder.Services.AddMasaBlazor(); 17 | appBuilder.Services.AddBlazoredLocalStorage(); 18 | appBuilder.Services.AddScoped(); 19 | appBuilder.Services.AddDependencyInjection(); 20 | 21 | var app = appBuilder.Build(); 22 | 23 | app.MainWindow 24 | #if DEBUG 25 | .SetDevToolsEnabled(true) 26 | #endif 27 | .SetTitle("YourWeather.Photino") 28 | .SetGrantBrowserPermissions(true); 29 | #if Windows 30 | app.MainWindow.WindowCreated += (sender, e) => TitleBar.Init(app.MainWindow.WindowHandle); 31 | #endif 32 | AppDomain.CurrentDomain.UnhandledException += (sender, error) => 33 | { 34 | }; 35 | 36 | app.Run(); 37 | } 38 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/YourWeather.Winform.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net8.0-windows 5 | enable 6 | true 7 | enable 8 | Resources\favicon.ico 9 | 1.4.4 10 | 你的天气 11 | true 12 | true 13 | win-x64 14 | 15 | 16 | 17 | 18 | 19 | 20 | Always 21 | true 22 | PreserveNewest 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/Services/StaticWebAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace YourWeather.Photino.Services 4 | { 5 | public class StaticWebAssets : Rcl.Services.StaticWebAssets 6 | { 7 | public override async Task ReadJsonAsync(string relativePath, bool isRcl = true, JsonSerializerOptions? jsonSerializerOptions = null) 8 | { 9 | var contents = await ReadContentAsync(relativePath, isRcl).ConfigureAwait(false); 10 | return JsonSerializer.Deserialize(contents, jsonSerializerOptions ?? DefaultJsonSerializerOptions) ?? throw new($"{relativePath} deserialize fail"); 11 | } 12 | 13 | public override async Task ReadContentAsync(string relativePath, bool isRcl = true) 14 | { 15 | string path; 16 | #if !DEBUG 17 | if (isRcl) 18 | { 19 | path = $"wwwroot/_content/{RclAssemblyName}/{relativePath}"; 20 | } 21 | else 22 | { 23 | #endif 24 | path = $"wwwroot/{relativePath}"; 25 | #if !DEBUG 26 | } 27 | #endif 28 | if (!File.Exists(path)) 29 | { 30 | throw new Exception($"not find {path}"); 31 | } 32 | 33 | return await File.ReadAllTextAsync(path).ConfigureAwait(false); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Desktop/Services/StaticWebAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace YourWeather.Rcl.Desktop.Services 4 | { 5 | public class StaticWebAssets : Rcl.Services.StaticWebAssets 6 | { 7 | public override async Task ReadJsonAsync(string relativePath, bool isRcl = true, JsonSerializerOptions? jsonSerializerOptions = null) 8 | { 9 | var contents = await ReadContentAsync(relativePath, isRcl).ConfigureAwait(false); 10 | return JsonSerializer.Deserialize(contents, jsonSerializerOptions ?? DefaultJsonSerializerOptions) ?? throw new($"{relativePath} deserialize fail"); 11 | } 12 | 13 | public override async Task ReadContentAsync(string relativePath, bool isRcl = true) 14 | { 15 | string path; 16 | #if !DEBUG 17 | if (isRcl) 18 | { 19 | path = $"wwwroot/_content/{RclAssemblyName}/{relativePath}"; 20 | } 21 | else 22 | { 23 | #endif 24 | path = $"wwwroot/{relativePath}"; 25 | #if !DEBUG 26 | } 27 | #endif 28 | 29 | if (!File.Exists(path)) 30 | { 31 | throw new Exception($"not find {path}"); 32 | } 33 | 34 | return await File.ReadAllTextAsync(path).ConfigureAwait(false); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/PlatformIntegration/PlatformIntegration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System.Reflection; 3 | 4 | namespace YourWeather.Rcl.Services 5 | { 6 | public class PlatformIntegration : IPlatformIntegration 7 | { 8 | private readonly Lazy> _module; 9 | 10 | public PlatformIntegration(IJSRuntime jSRuntime) 11 | { 12 | _module = new(() => jSRuntime.InvokeAsync("import", $"./_content/{StaticWebAssets.RclAssemblyName}/js/platformIntegration.js")); 13 | } 14 | 15 | public virtual string GetVersion() 16 | { 17 | var assembly = Assembly.GetEntryAssembly(); 18 | if (assembly == null) 19 | { 20 | return string.Empty; 21 | } 22 | 23 | var assemblyFileVersionAttribute = assembly.GetCustomAttribute(); 24 | if (assemblyFileVersionAttribute == null) 25 | { 26 | return string.Empty; 27 | } 28 | 29 | return assemblyFileVersionAttribute.Version; 30 | } 31 | 32 | public virtual async Task OpenBrowserUrl(string url) 33 | { 34 | var module = await _module.Value; 35 | await module.InvokeVoidAsync("openBrowserUrl", url); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "iisExpress": { 4 | "applicationUrl": "http://localhost:58784", 5 | "sslPort": 44300 6 | } 7 | }, 8 | "profiles": { 9 | "http": { 10 | "commandName": "Project", 11 | "dotnetRunMessages": true, 12 | "launchBrowser": true, 13 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 14 | "applicationUrl": "http://localhost:5044", 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "https": { 20 | "commandName": "Project", 21 | "dotnetRunMessages": true, 22 | "launchBrowser": true, 23 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 24 | "applicationUrl": "https://localhost:7108;http://localhost:5044", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | }, 29 | "IIS Express": { 30 | "commandName": "IISExpress", 31 | "launchBrowser": true, 32 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 33 | "environmentVariables": { 34 | "ASPNETCORE_ENVIRONMENT": "Development" 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/Services/StaticWebAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace YourWeather.Server.Services 4 | { 5 | public class StaticWebAssets : Rcl.Services.StaticWebAssets 6 | { 7 | public override async Task ReadJsonAsync(string relativePath, bool isRcl = true, JsonSerializerOptions? jsonSerializerOptions = null) 8 | { 9 | var contents = await ReadContentAsync(relativePath, isRcl).ConfigureAwait(false); 10 | return JsonSerializer.Deserialize(contents, jsonSerializerOptions ?? DefaultJsonSerializerOptions) ?? throw new($"{relativePath} deserialize fail"); 11 | } 12 | 13 | public override async Task ReadContentAsync(string relativePath, bool isRcl = true) 14 | { 15 | string path; 16 | #if !DEBUG 17 | if (isRcl) 18 | { 19 | path = $"{AppContext.BaseDirectory}/wwwroot/_content/{RclAssemblyName}/{relativePath}"; 20 | } 21 | else 22 | { 23 | #endif 24 | path = $"{AppContext.BaseDirectory}/wwwroot/{relativePath}"; 25 | #if !DEBUG 26 | } 27 | #endif 28 | 29 | if (!File.Exists(path)) 30 | { 31 | throw new Exception($"not find {path}"); 32 | } 33 | 34 | return await File.ReadAllTextAsync(path).ConfigureAwait(false); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/MacCatalyst/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIDeviceFamily 6 | 7 | 1 8 | 2 9 | 6 10 | 11 | UIRequiredDeviceCapabilities 12 | 13 | arm64 14 | 15 | UISupportedInterfaceOrientations 16 | 17 | UIInterfaceOrientationPortrait 18 | UIInterfaceOrientationLandscapeLeft 19 | UIInterfaceOrientationLandscapeRight 20 | 21 | UISupportedInterfaceOrientations~ipad 22 | 23 | UIInterfaceOrientationPortrait 24 | UIInterfaceOrientationPortraitUpsideDown 25 | UIInterfaceOrientationLandscapeLeft 26 | UIInterfaceOrientationLandscapeRight 27 | 28 | XSAppIconAssets 29 | Assets.xcassets/appicon.appiconset 30 | NSLocationWhenInUseUsageDescription 31 | This app requires access to your location. Please grant access to your precise location when requested. 32 | 33 | 34 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/YourWeather.Wpf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net8.0-windows 5 | enable 6 | true 7 | YourWeather.Wpf 8 | enable 9 | Resources\favicon.ico 10 | 1.4.4 11 | 你的天气 12 | true 13 | true 14 | win-x64 15 | 16 | 17 | 18 | 19 | 20 | 21 | true 22 | PreserveNewest 23 | Always 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/Services/StaticWebAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | using System.Text.Json; 3 | 4 | namespace YourWeather.WebAssembly.Services 5 | { 6 | public class StaticWebAssets : Rcl.Services.StaticWebAssets 7 | { 8 | private readonly HttpClient _httpClient; 9 | 10 | public StaticWebAssets(HttpClient httpClient) 11 | { 12 | _httpClient = httpClient; 13 | } 14 | 15 | public override async Task ReadJsonAsync(string relativePath, bool isRcl = true, JsonSerializerOptions? jsonSerializerOptions = null) 16 | { 17 | string path = RelativePathToPath(relativePath, isRcl); 18 | var result = await _httpClient.GetFromJsonAsync(path, jsonSerializerOptions ?? DefaultJsonSerializerOptions); 19 | return result ?? throw new($"{relativePath} deserialize fail"); 20 | } 21 | 22 | public override async Task ReadContentAsync(string relativePath, bool isRcl = true) 23 | { 24 | string path = RelativePathToPath(relativePath, isRcl); 25 | var result = await _httpClient.GetStringAsync(path); 26 | return result ?? throw new Exception($"not find json {path}"); 27 | } 28 | 29 | private static string RelativePathToPath(string relativePath, bool isRcl) 30 | => isRcl ? $"_content/{RclAssemblyName}/{relativePath}" : relativePath; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Services/ThemeService/TitleBarOrStatusBar.cs: -------------------------------------------------------------------------------- 1 | #if ANDROID || IOS14_2_OR_GREATER 2 | using CommunityToolkit.Maui.Core; 3 | #endif 4 | #if WINDOWS || MACCATALYST 5 | using MauiBlazorToolkit; 6 | using MauiBlazorToolkit.Platform; 7 | #endif 8 | using YourWeather.Shared; 9 | 10 | namespace YourWeather.Maui.Services 11 | { 12 | public class TitleBarOrStatusBar 13 | { 14 | private static readonly Color statusBarColorLight = Color.FromRgb(255, 255, 255); 15 | private static readonly Color statusBarColorDark = Color.FromRgb(18, 18, 18); 16 | 17 | #pragma warning disable CA1416 18 | public static void SetTitleBarOrStatusBar(ThemeType themeState) 19 | { 20 | var Dark = themeState == ThemeType.Dark; 21 | Color backgroundColor = Dark ? statusBarColorDark : statusBarColorLight; 22 | #if WINDOWS || MACCATALYST 23 | TitleBar.SetColor(backgroundColor); 24 | TitleBarStyle titleBarStyle = Dark ? TitleBarStyle.LightContent : TitleBarStyle.DarkContent; 25 | TitleBar.SetStyle(titleBarStyle); 26 | #elif ANDROID || IOS14_2_OR_GREATER 27 | CommunityToolkit.Maui.Core.Platform.StatusBar.SetColor(backgroundColor); 28 | StatusBarStyle statusBarStyle = Dark ? StatusBarStyle.LightContent : StatusBarStyle.DarkContent; 29 | CommunityToolkit.Maui.Core.Platform.StatusBar.SetStyle(statusBarStyle); 30 | #endif 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/MauiProgram.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Maui; 2 | using Darnton.Blazor.DeviceInterop.Geolocation; 3 | using MauiBlazorToolkit.Extensions; 4 | using YourWeather.Maui.Extensions; 5 | 6 | namespace YourWeather.Maui 7 | { 8 | public static class MauiProgram 9 | { 10 | public static MauiApp CreateMauiApp() 11 | { 12 | var builder = MauiApp.CreateBuilder(); 13 | builder 14 | .UseMauiApp() 15 | .UseMauiCommunityToolkit() 16 | .UseMauiBlazorToolkit(options => 17 | { 18 | options.TitleBar = true; 19 | }) 20 | .ConfigureFonts(fonts => 21 | { 22 | fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); 23 | }) 24 | .ConfigureEssentials(essentials => 25 | { 26 | essentials.UseVersionTracking(); 27 | }); ; 28 | 29 | builder.Services.AddMauiBlazorWebView(); 30 | #if DEBUG 31 | builder.Services.AddBlazorWebViewDeveloperTools(); 32 | #endif 33 | builder.Services.AddMasaBlazor(); 34 | builder.Services.AddScoped(); 35 | builder.Services.AddDependencyInjection(); 36 | builder.Services.AddMauiExceptionHandle(); 37 | 38 | return builder.Build(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | YourWeather.WPF 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
Loading...
23 | 24 |
25 | An unhandled error has occurred. 26 | Reload 27 | 🗙 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | YourWeather.Photino 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
Loading...
23 | 24 |
25 | An unhandled error has occurred. 26 | Reload 27 | 🗙 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | YourWeather.Winform 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
Loading...
23 | 24 |
25 | An unhandled error has occurred. 26 | Reload 27 | 🗙 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSRequiresIPhoneOS 6 | 7 | UIDeviceFamily 8 | 9 | 1 10 | 2 11 | 12 | UIRequiredDeviceCapabilities 13 | 14 | arm64 15 | 16 | UISupportedInterfaceOrientations 17 | 18 | UIInterfaceOrientationPortrait 19 | UIInterfaceOrientationLandscapeLeft 20 | UIInterfaceOrientationLandscapeRight 21 | 22 | UISupportedInterfaceOrientations~ipad 23 | 24 | UIInterfaceOrientationPortrait 25 | UIInterfaceOrientationPortraitUpsideDown 26 | UIInterfaceOrientationLandscapeLeft 27 | UIInterfaceOrientationLandscapeRight 28 | 29 | XSAppIconAssets 30 | Assets.xcassets/appicon.appiconset 31 | NSLocationWhenInUseUsageDescription 32 | This app requires access to your location. Please grant access to your precise location when requested. 33 | UIViewControllerBasedStatusBarAppearance 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | YourWeather.Maui 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
Loading...
23 | 24 |
25 | An unhandled error has occurred. 26 | Reload 27 | 🗙 28 |
29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Services/StaticWebAssets.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace YourWeather.Maui.Services 4 | { 5 | public class StaticWebAssets : Rcl.Services.StaticWebAssets 6 | { 7 | public override async Task ReadJsonAsync(string relativePath, bool isRcl = true, JsonSerializerOptions? jsonSerializerOptions = null) 8 | { 9 | var contents = await ReadContentAsync(relativePath, isRcl).ConfigureAwait(false); 10 | return JsonSerializer.Deserialize(contents, jsonSerializerOptions ?? DefaultJsonSerializerOptions) ?? throw new($"{relativePath} deserialize fail"); 11 | } 12 | 13 | public override async Task ReadContentAsync(string relativePath, bool isRcl = true) 14 | { 15 | string path; 16 | if (isRcl) 17 | { 18 | path = $"wwwroot/_content/{RclAssemblyName}/{relativePath}"; 19 | } 20 | else 21 | { 22 | path = $"wwwroot/{relativePath}"; 23 | } 24 | 25 | bool exists = await FileSystem.AppPackageFileExistsAsync(path).ConfigureAwait(false); 26 | if (!exists) 27 | { 28 | throw new Exception($"not find {path}"); 29 | } 30 | 31 | using var stream = await FileSystem.OpenAppPackageFileAsync(path).ConfigureAwait(false); 32 | using var reader = new StreamReader(stream); 33 | return reader.ReadToEnd(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Pages/AddLocationPage.razor: -------------------------------------------------------------------------------- 1 | @page "/addLocation" 2 | @inherits PageComponentBase 3 | 4 | 13 | 14 | 15 | 17 | 18 | 22 | 23 | 24 | 25 | @($"{context.Name}({context.Info})") 26 | 27 | 28 | @if (context != LocationDatas.LastOrDefault()) 29 | { 30 | 31 | } 32 | 33 | 34 | @if (LocationDatas.Count == 0) 35 | { 36 | 37 | 38 | 未找到你想要的结果 39 | 40 | 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/GeolocationCoordinates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Darnton.Blazor.DeviceInterop.Geolocation 4 | { 5 | /// 6 | /// Geolocation Coordinates, based on . 7 | /// 8 | public class GeolocationCoordinates 9 | { 10 | /// 11 | /// Latitude in decimal degrees. 12 | /// 13 | public double Latitude { get; set; } 14 | 15 | /// 16 | /// Longitude in decimal degrees. 17 | /// 18 | public double Longitude { get; set; } 19 | 20 | /// 21 | /// Altitude in metres, relative to sea level. 22 | /// 23 | public double? Altitude { get; set; } 24 | 25 | /// 26 | /// Accuracy of the latitude and longitude properties, in metres. 27 | /// 28 | public double Accuracy { get; set; } 29 | 30 | /// 31 | /// Accuracy of the altitude, in metres. 32 | /// 33 | public double? AltitudeAccuracy { get; set; } 34 | 35 | /// 36 | /// The direction the device is travelling, in degrees clockwise from true north. 37 | /// 38 | public double? Heading { get; set; } 39 | 40 | /// 41 | /// The velocity of the device, in metres per second. 42 | /// 43 | public double? Speed { get; set; } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/SettingService/SettingService.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Shared; 2 | 3 | namespace YourWeather.Rcl.Services 4 | { 5 | public class SettingService : ISettingsService 6 | { 7 | protected readonly Dictionary Settings = new() 8 | { 9 | {SettingType.Theme,(int)ThemeType.Light }, 10 | {SettingType.WeatherSource,(int)WeatherSourceType.Amap }, 11 | {SettingType.Location,"" }, 12 | {SettingType.Locations,"" } 13 | }; 14 | 15 | public virtual Task ContainsKey(string key) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | 20 | public Task Get(SettingType type) 21 | { 22 | var defaultValue = Settings[type]; 23 | return Get(type, defaultValue); 24 | } 25 | 26 | public Task Get(SettingType type, T defaultValue) 27 | { 28 | var key = Enum.GetName(typeof(SettingType), type); 29 | return Get(key!, defaultValue); 30 | } 31 | 32 | public virtual Task Get(string key, T defaultValue) 33 | { 34 | throw new NotImplementedException(); 35 | } 36 | 37 | public Task Save(SettingType type, T value) 38 | { 39 | var key = Enum.GetName(typeof(SettingType), type); 40 | return Save(key!, value); 41 | } 42 | 43 | public virtual Task Save(string key, T value) 44 | { 45 | throw new NotImplementedException(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherResult/VisualCrossingResult.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | 4 | public class VisualCrossingResult 5 | { 6 | public VisualCrossingDay[]? days { get; set; } 7 | public VisualCrossingCurrentconditions? currentConditions { get; set; } 8 | } 9 | 10 | public class VisualCrossingCurrentconditions 11 | { 12 | public string? datetime { get; set; } 13 | public int datetimeEpoch { get; set; } 14 | public float temp { get; set; } 15 | public float feelslike { get; set; } 16 | public float humidity { get; set; } 17 | public object? windgust { get; set; } 18 | public float windspeed { get; set; } 19 | public float winddir { get; set; } 20 | public float pressure { get; set; } 21 | public float visibility { get; set; } 22 | public float cloudcover { get; set; } 23 | public string? conditions { get; set; } 24 | } 25 | 26 | public class VisualCrossingDay 27 | { 28 | public string? datetime { get; set; } 29 | public int datetimeEpoch { get; set; } 30 | public float tempmax { get; set; } 31 | public float tempmin { get; set; } 32 | public float temp { get; set; } 33 | public string? conditions { get; set; } 34 | public VisualCrossingHour[]? hours { get; set; } 35 | } 36 | 37 | public class VisualCrossingHour 38 | { 39 | public string? datetime { get; set; } 40 | public int datetimeEpoch { get; set; } 41 | public float temp { get; set; } 42 | public string? conditions { get; set; } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherResult/OpenWeatherResult.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class OpenWeatherResultLives 4 | 5 | { 6 | public OpenWeatherWeather[]? weather { get; set; } 7 | public OpenWeatherMain? main { get; set; } 8 | public int visibility { get; set; } 9 | public OpenWeatherWind? wind { get; set; } 10 | public OpenWeatherClouds? clouds { get; set; } 11 | public int dt { get; set; } 12 | public int timezone { get; set; } 13 | public int id { get; set; } 14 | public string? name { get; set; } 15 | public int cod { get; set; } 16 | } 17 | 18 | public class OpenWeatherMain 19 | { 20 | public float temp { get; set; } 21 | public float feels_like { get; set; } 22 | public float temp_min { get; set; } 23 | public float temp_max { get; set; } 24 | public int pressure { get; set; } 25 | public int humidity { get; set; } 26 | public int sea_level { get; set; } 27 | public int grnd_level { get; set; } 28 | } 29 | 30 | public class OpenWeatherWind 31 | { 32 | public float speed { get; set; } 33 | public int deg { get; set; } 34 | public float gust { get; set; } 35 | } 36 | 37 | public class OpenWeatherClouds 38 | { 39 | public int all { get; set; } 40 | } 41 | 42 | 43 | public class OpenWeatherWeather 44 | { 45 | public int id { get; set; } 46 | public string? main { get; set; } 47 | public string? description { get; set; } 48 | public string? icon { get; set; } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Services/ThemeService/ThemeService.cs: -------------------------------------------------------------------------------- 1 | using YourWeather.Rcl.Services; 2 | using YourWeather.Shared; 3 | 4 | namespace YourWeather.Maui.Services 5 | { 6 | public class ThemeService : IThemeService 7 | { 8 | private ThemeType? _theme; 9 | 10 | public event Action? OnChanged; 11 | 12 | public ThemeType RealTheme => _theme switch 13 | { 14 | ThemeType.System => Application.Current!.RequestedTheme == AppTheme.Dark ? ThemeType.Dark : ThemeType.Light, 15 | ThemeType.Dark => ThemeType.Dark, 16 | _ => ThemeType.Light 17 | }; 18 | 19 | public Task SetThemeAsync(ThemeType theme) 20 | { 21 | if (_theme == theme) 22 | { 23 | return Task.CompletedTask; 24 | } 25 | 26 | //跟随系统主题改变 27 | if (theme == ThemeType.System) 28 | { 29 | Application.Current!.RequestedThemeChanged += HandleAppThemeChanged; 30 | } 31 | //取消跟随系统主题改变 32 | else if (_theme == ThemeType.System) 33 | { 34 | Application.Current!.RequestedThemeChanged -= HandleAppThemeChanged; 35 | } 36 | 37 | _theme = theme; 38 | 39 | InternalNotifyStateChanged(); 40 | return Task.CompletedTask; 41 | } 42 | 43 | private void HandleAppThemeChanged(object? sender, AppThemeChangedEventArgs e) 44 | { 45 | InternalNotifyStateChanged(); 46 | } 47 | 48 | private void InternalNotifyStateChanged() 49 | { 50 | OnChanged?.Invoke(RealTheme); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Pages/WeatherSourcePage.razor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.Web; 2 | using YourWeather.Rcl.Components; 3 | using YourWeather.Shared; 4 | 5 | namespace YourWeather.Rcl.Pages 6 | { 7 | public partial class WeatherSourcePage : PageComponentBase,IDisposable 8 | { 9 | private string? Key; 10 | private bool ShowEditDialog; 11 | private WeatherSourceType WeatherSourceType; 12 | private Dictionary WeatherSources => WeatherService.WeatherSources; 13 | 14 | protected override async Task OnInitializedAsync() 15 | { 16 | await base.OnInitializedAsync(); 17 | Title = "天气源配置"; 18 | MainLayout.ShowBack(true, "setting"); 19 | } 20 | 21 | private async Task OpenEditDialog(WeatherSourceType type) 22 | { 23 | WeatherSourceType = type; 24 | Key = await SettingsService.Get(type.ToString(), ""); 25 | ShowEditDialog = true; 26 | } 27 | 28 | private async Task HandleOnEdit() 29 | { 30 | ShowEditDialog = false; 31 | await SettingsService.Save(WeatherSourceType.ToString(), Key); 32 | } 33 | 34 | private async Task HandleOnEnter(KeyboardEventArgs args) 35 | { 36 | if (!ShowEditDialog) 37 | { 38 | return; 39 | } 40 | 41 | if (args.Key == "Enter") 42 | { 43 | await HandleOnEdit(); 44 | } 45 | } 46 | 47 | public void Dispose() 48 | { 49 | MainLayout.ShowBack(false, ""); 50 | GC.SuppressFinalize(this); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Components/PageComponentBase.cs: -------------------------------------------------------------------------------- 1 | using Masa.Blazor; 2 | using Microsoft.AspNetCore.Components; 3 | using YourWeather.Rcl.Services; 4 | using YourWeather.Rcl.Layout; 5 | 6 | namespace YourWeather.Rcl.Components 7 | { 8 | public class PageComponentBase : ComponentBase 9 | { 10 | private string? _title; 11 | 12 | [Inject] 13 | protected ISettingsService SettingsService { get; set; } = default!; 14 | [Inject] 15 | protected IThemeService ThemeService { get; set; } = default!; 16 | [Inject] 17 | protected IWeatherService WeatherService { get; set; } = default!; 18 | [Inject] 19 | protected IPopupService PopupService { get; set; } = default!; 20 | [Inject] 21 | protected ILocationService LocationService { get; set; } = default!; 22 | [Inject] 23 | protected IPlatformIntegration PlatformService { get; set; } = default!; 24 | [Inject] 25 | protected NavigationManager Navigation { get; set; } = default!; 26 | 27 | [CascadingParameter] 28 | public MainLayout MainLayout { get; set; } = default!; 29 | 30 | protected virtual string? Title 31 | { 32 | get => _title; 33 | set => SetTitle(value); 34 | } 35 | 36 | protected override async Task OnInitializedAsync() 37 | { 38 | await base.OnInitializedAsync(); 39 | } 40 | 41 | private void SetTitle(string? value) 42 | { 43 | _title = value; 44 | UpdateTitle(value); 45 | } 46 | 47 | private void UpdateTitle(string? value) 48 | { 49 | MainLayout.UpdateTitle(value); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/appiconfg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Resources/AppIcon/appiconfg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Resources/Splash/splash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherDate/WeatherLives.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class WeatherLives 4 | { 5 | /// 6 | /// 城市名 7 | /// 8 | public string? City { get; set; } 9 | /// 10 | /// 天气描述 11 | /// 12 | public string? Weather { get; set; } 13 | /// 14 | /// 温度,摄氏度 15 | /// 16 | public string? Temp { get; set; } 17 | /// 18 | /// 体感温度 19 | /// 20 | public string? FeelsLike { get; set; } 21 | /// 22 | /// 最低温度 23 | /// 24 | public string? MinTemp { get; set; } 25 | /// 26 | /// 最高温度 27 | /// 28 | public string? MaxTemp { get; set; } 29 | /// 30 | /// 湿度 31 | /// 32 | public string? Humidity { get; set; } 33 | /// 34 | /// 风速 35 | /// 36 | public string? WindSpeed { get; set; } 37 | /// 38 | /// 风向 39 | /// 40 | public string? WindDeg { get; set; } 41 | /// 42 | /// 风力级别 43 | /// 44 | public string? WindScale { get; set; } 45 | /// 46 | /// 能见度 47 | /// 48 | public string? Visibility { get; set; } 49 | /// 50 | /// 大气压强 51 | /// 52 | public string? Pressure { get; set; } 53 | /// 54 | /// 云层 55 | /// 56 | public string? Cloud { get; set; } 57 | public DateTime LastUpdate { get; set; } = DateTime.Now; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/MainPage.xaml.Android.cs: -------------------------------------------------------------------------------- 1 | using AndroidX.Activity; 2 | using Microsoft.AspNetCore.Components.WebView; 3 | using Microsoft.Maui.Platform; 4 | using YourWeather.Maui.Platforms.Android; 5 | 6 | namespace YourWeather.Maui; 7 | 8 | public partial class MainPage 9 | { 10 | // To manage Android permissions, update AndroidManifest.xml to include the permissions and 11 | // features required by your app. You may have to perform additional configuration to enable 12 | // use of those APIs from the WebView, as is done below. A custom WebChromeClient is needed 13 | // to define what happens when the WebView requests a set of permissions. See 14 | // PermissionManagingBlazorWebChromeClient.cs to explore the approach taken in this example. 15 | 16 | private partial void BlazorWebViewInitializing(object sender, BlazorWebViewInitializingEventArgs e) 17 | { 18 | } 19 | 20 | private partial void BlazorWebViewInitialized(object sender, BlazorWebViewInitializedEventArgs e) 21 | { 22 | if (e.WebView.Context?.GetActivity() is not ComponentActivity activity) 23 | { 24 | throw new InvalidOperationException($"The permission-managing WebChromeClient requires that the current activity be a '{nameof(ComponentActivity)}'."); 25 | } 26 | 27 | e.WebView.VerticalScrollBarEnabled = false; 28 | e.WebView.Settings.JavaScriptEnabled = true; 29 | e.WebView.Settings.AllowFileAccess = true; 30 | e.WebView.Settings.MediaPlaybackRequiresUserGesture = false; 31 | e.WebView.Settings.SetGeolocationEnabled(true); 32 | e.WebView.Settings.SetGeolocationDatabasePath(e.WebView.Context.FilesDir.Path); 33 | e.WebView.SetWebChromeClient(new PermissionManagingBlazorWebChromeClient(e.WebView.WebChromeClient, activity)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/LocationService/LocationService.cs: -------------------------------------------------------------------------------- 1 | using Darnton.Blazor.DeviceInterop.Geolocation; 2 | using YourWeather.Shared; 3 | 4 | namespace YourWeather.Rcl.Services 5 | { 6 | public class LocationService : ILocationService 7 | { 8 | private Location? CurrentLocation; 9 | private List AllLocations = new(); 10 | private readonly IGeolocationService GeolocationService; 11 | private readonly IStaticWebAssets StaticWebAssets; 12 | 13 | public LocationService(IGeolocationService geolocationService, IStaticWebAssets staticWebAssets) 14 | { 15 | GeolocationService = geolocationService; 16 | StaticWebAssets = staticWebAssets; 17 | } 18 | 19 | public async Task GetCurrentLocation() 20 | { 21 | if (CurrentLocation != null) 22 | { 23 | return CurrentLocation; 24 | } 25 | 26 | var result = await GeolocationService.GetCurrentPosition(); 27 | if (!result.IsSuccess) 28 | { 29 | return null; 30 | } 31 | else 32 | { 33 | CurrentLocation = new Location() 34 | { 35 | Lat = result.Position.Coords.Latitude, 36 | Lon = result.Position.Coords.Longitude, 37 | }; 38 | return CurrentLocation; 39 | } 40 | } 41 | 42 | public async Task> GetAllLocations() 43 | { 44 | if (AllLocations == null || AllLocations.Count == 0) 45 | { 46 | AllLocations = await StaticWebAssets.ReadJsonAsync>("json/location.json"); 47 | } 48 | 49 | return AllLocations; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/Pages/Error.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model YourWeather.Server.Pages.ErrorModel 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 | 22 | 23 | 24 | 25 |
26 |
27 |

Error.

28 |

An error occurred while processing your request.

29 | 30 | @if (Model.ShowRequestId) 31 | { 32 |

33 | Request ID: @Model.RequestId 34 |

35 | } 36 | 37 |

Development Mode

38 |

39 | Swapping to the Development environment displays detailed information about the error 40 | that occurred. 41 |

42 |

43 | The Development environment shouldn't be enabled for deployed applications. 44 | It can result in displaying sensitive information from exceptions to end users. 45 | For local debugging, enable the Development environment by setting the 46 | ASPNETCORE_ENVIRONMENT environment variable to Development 47 | and restarting the app. 48 |

49 |
50 |
51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Pages/WeatherSourcePage.razor: -------------------------------------------------------------------------------- 1 | @page "/weatherSource" 2 | @inherits PageComponentBase 3 | 4 | @foreach (var item in WeatherSources) 5 | { 6 | 10 | 11 | 12 | 13 | @(item.Value.Name) 14 | 15 | 16 | 17 | mdi-pencil 18 | 19 | 20 | 21 | 22 | } 23 | 24 | 26 | 27 | 28 | @(WeatherSources[WeatherSourceType].Name) 29 | 30 | 31 | mdi-close 32 | 33 | 34 | 35 | 42 | 43 | 44 | 45 | 46 | 47 | 取消 48 | 49 | 52 | 确定 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/Pages/_Host.cshtml: -------------------------------------------------------------------------------- 1 | @page "/" 2 | @using Microsoft.AspNetCore.Components.Web 3 | @using Microsoft.AspNetCore.Mvc.TagHelpers 4 | @namespace YourWeather.Server.Pages 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | An error has occurred. This application may no longer respond until reloaded. 31 | 32 | 33 | An unhandled exception has occurred. See browser dev tools for details. 34 | 35 | Reload 36 | 🗙 37 |
38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using Darnton.Blazor.DeviceInterop.Geolocation; 3 | using Microsoft.AspNetCore.Components.WebView; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using System.Windows; 6 | using System.Windows.Interop; 7 | using YourWeather.Rcl.Desktop; 8 | using YourWeather.Wpf.Extend; 9 | 10 | namespace YourWeather.Wpf 11 | { 12 | /// 13 | /// Interaction logic for MainWindow.xaml 14 | /// 15 | public partial class MainWindow : Window 16 | { 17 | public MainWindow() 18 | { 19 | InitializeComponent(); 20 | var hwnd = new WindowInteropHelper(GetWindow(this)).EnsureHandle(); 21 | TitleBar.Init(hwnd); 22 | var serviceCollection = new ServiceCollection(); 23 | serviceCollection.AddWpfBlazorWebView(); 24 | #if DEBUG 25 | serviceCollection.AddBlazorWebViewDeveloperTools(); 26 | #endif 27 | serviceCollection.AddMasaBlazor(); 28 | serviceCollection.AddBlazoredLocalStorage(); 29 | serviceCollection.AddScoped(); 30 | serviceCollection.AddDependencyInjection(); 31 | 32 | blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing; 33 | blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; 34 | Resources.Add("services", serviceCollection.BuildServiceProvider()); 35 | } 36 | 37 | private void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e) 38 | { 39 | } 40 | 41 | private void BlazorWebViewInitialized(object? sender, BlazorWebViewInitializedEventArgs e) 42 | { 43 | var permissionHandler = new SilentPermissionRequestHandler(); 44 | 45 | e.WebView.CoreWebView2.PermissionRequested += (s, e) => permissionHandler.OnPermissionRequested((Microsoft.Web.WebView2.Core.CoreWebView2)s!, e); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Services/ThemeService/ThemeService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using SwashbucklerDiary.WebAssembly.Essentials; 3 | using YourWeather.Shared; 4 | 5 | namespace YourWeather.Rcl.Services 6 | { 7 | public class ThemeService : IThemeService 8 | { 9 | private ThemeType? _theme; 10 | 11 | private readonly SystemThemeJSModule _systemThemeJSModule; 12 | 13 | public event Action? OnChanged; 14 | 15 | public ThemeType RealTheme => _theme switch 16 | { 17 | ThemeType.System => _systemThemeJSModule.SystemTheme, 18 | ThemeType.Dark => ThemeType.Dark, 19 | _ => ThemeType.Light 20 | }; 21 | 22 | public ThemeService(IJSRuntime jSRuntime) 23 | { 24 | _systemThemeJSModule = new SystemThemeJSModule(jSRuntime); 25 | } 26 | 27 | public Task InitializedAsync() => _systemThemeJSModule.InitializedAsync(); 28 | 29 | public Task SetThemeAsync(ThemeType theme) 30 | { 31 | if (_theme == theme) 32 | { 33 | return Task.CompletedTask; 34 | } 35 | 36 | //跟随系统主题改变 37 | if (theme == ThemeType.System) 38 | { 39 | _systemThemeJSModule.SystemThemeChanged += HandleAppThemeChanged; 40 | } 41 | //取消跟随系统主题改变 42 | else if (_theme == ThemeType.System) 43 | { 44 | _systemThemeJSModule.SystemThemeChanged -= HandleAppThemeChanged; 45 | } 46 | 47 | _theme = theme; 48 | 49 | InternalNotifyStateChanged(); 50 | return Task.CompletedTask; 51 | } 52 | 53 | private Task HandleAppThemeChanged(ThemeType theme) 54 | { 55 | InternalNotifyStateChanged(); 56 | return Task.CompletedTask; 57 | } 58 | 59 | private void InternalNotifyStateChanged() 60 | { 61 | OnChanged?.Invoke(RealTheme); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/Platforms/Windows/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | $placeholder$ 15 | User Name 16 | $placeholder$.png 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/MainPage.xaml.Windows.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Components.WebView; 2 | using YourWeather.Maui.Platforms.Windows; 3 | 4 | namespace YourWeather.Maui; 5 | 6 | public partial class MainPage 7 | { 8 | // It might be perfectly acceptable to not override the WebView2 permission requesting behavior 9 | // at all, relying on the default popup to decide whether to grant specific permissions. However, 10 | // one reason to override this behavior is to prevent the user from getting "stuck" if they accidentally 11 | // deny a permission requested by the WebView. In an actual web browser, the user could change their 12 | // decision using the browser's UI, but no such UI is built in to the WebView2 control. This 13 | // leaves it up to us to decide what to do in these cases. 14 | // 15 | // One option is to take a simple approach, allowing all permission requests originating from 16 | // the base URI while denying all requests coming from an unknown source. No action from the user is 17 | // required. This results in a user experience that matches what one might expect from a typical 18 | // native Windows app. 19 | // 20 | // Alternatively, you could implement a dialog system that prompts the user when a device permission 21 | // is requested. This allows the user to view the details of the request and make a decision for themselves. 22 | // 23 | // This example includes both implementations. You can switch between them by adding/removing the line in 24 | // the .csproj file defining the HANDLE_WEBVIEW2_PERMISSIONS_SILENTLY constant. 25 | 26 | private partial void BlazorWebViewInitializing(object sender, BlazorWebViewInitializingEventArgs e) 27 | { 28 | } 29 | 30 | private partial void BlazorWebViewInitialized(object sender, BlazorWebViewInitializedEventArgs e) 31 | { 32 | var permissionHandler = new SilentPermissionRequestHandler(); 33 | 34 | e.WebView.CoreWebView2.PermissionRequested += permissionHandler.OnPermissionRequested; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/MainForm.cs: -------------------------------------------------------------------------------- 1 | using Blazored.LocalStorage; 2 | using Darnton.Blazor.DeviceInterop.Geolocation; 3 | using Microsoft.AspNetCore.Components.WebView; 4 | using Microsoft.AspNetCore.Components.WebView.WindowsForms; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using YourWeather.Rcl.Desktop; 7 | using YourWeather.Winform.Extensions; 8 | 9 | namespace YourWeather.Winform 10 | { 11 | public partial class MainForm : Form 12 | { 13 | public MainForm() 14 | { 15 | InitializeComponent(); 16 | 17 | TitleBar.Init(Handle); 18 | 19 | var services = new ServiceCollection(); 20 | services.AddWindowsFormsBlazorWebView(); 21 | 22 | #if DEBUG 23 | services.AddBlazorWebViewDeveloperTools(); 24 | #endif 25 | services.AddMasaBlazor(); 26 | services.AddBlazoredLocalStorage(); 27 | services.AddScoped(); 28 | services.AddDependencyInjection(); 29 | blazorWebView.HostPage = "wwwroot\\index.html"; 30 | blazorWebView.Services = services.BuildServiceProvider(); 31 | blazorWebView.RootComponents.Add("#app"); 32 | blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing; 33 | blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; 34 | } 35 | 36 | protected override void OnClosed(EventArgs e) 37 | { 38 | base.OnClosed(e); 39 | Environment.Exit(0); 40 | } 41 | 42 | private void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e) 43 | { 44 | } 45 | 46 | private void BlazorWebViewInitialized(object? sender, BlazorWebViewInitializedEventArgs e) 47 | { 48 | var permissionHandler = new SilentPermissionRequestHandler(); 49 | 50 | e.WebView.CoreWebView2.PermissionRequested += (s, e) => permissionHandler.OnPermissionRequested((Microsoft.Web.WebView2.Core.CoreWebView2)s!, e); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | DOTNET_NOLOGO: true # Disable the .NET logo 8 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable the .NET first time experience 9 | DOTNET_CLI_TELEMETRY_OPTOUT: true # Disable sending .NET CLI telemetry 10 | DOTNET_VERSION: '8.0' 11 | CSPROJ_FILE_PATH: 'YourWeather/YourWeather.WebAssembly/YourWeather.WebAssembly.csproj' 12 | 13 | jobs: 14 | deploy: 15 | runs-on: ubuntu-latest 16 | steps: 17 | # Checkout the code 18 | - uses: actions/checkout@v3 19 | with: 20 | fetch-depth: 0 21 | 22 | # Install .NET SDK 23 | - name: Setup .NET SDK 24 | uses: actions/setup-dotnet@v3 25 | with: 26 | dotnet-version: '${{ env.DOTNET_VERSION }}.x' 27 | 28 | - name: Install .NET WebAssembly Tools 29 | run: dotnet workload install wasm-tools 30 | 31 | # Get the commit count and format the version 32 | - name: Get commit count and format version 33 | id: version 34 | run: | 35 | commit_count=$(git rev-list --count HEAD) 36 | major=$((commit_count / 100)) 37 | minor=$((commit_count % 100 / 10)) 38 | patch=$((commit_count % 10)) 39 | formatted_version="$major.$minor.$patch" 40 | echo "FORMATTED_VERSION=$formatted_version" >> $GITHUB_ENV 41 | 42 | # Replace the version in the csproj file 43 | - name: Update version in csproj 44 | run: | 45 | sed -i "s/.*<\/Version>/${{ env.FORMATTED_VERSION }}<\/Version>/" \ 46 | ${{ env.CSPROJ_FILE_PATH }} 47 | 48 | # Publish the site 49 | - name: Publish 50 | run: dotnet publish ${{ env.CSPROJ_FILE_PATH }} -c Release -o public -p GHPages=true 51 | 52 | # Deploy the site 53 | - name: Deploy 54 | uses: peaceiris/actions-gh-pages@v3 55 | with: 56 | github_token: ${{ secrets.GITHUB_TOKEN }} 57 | publish_dir: public/wwwroot 58 | force_orphan: true 59 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Extensions/WeatherExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public static class WeatherExtensions 4 | { 5 | private static Dictionary VisualCrossingDic = new() 6 | { 7 | {"晴朗","晴" }, 8 | {"部分多云","少云" }, 9 | {"阴云密布","多云" }, 10 | }; 11 | public static string ToWindDir(this int windeg) 12 | { 13 | if (windeg < 0 || windeg > 360) 14 | return string.Empty; 15 | if (windeg > 337.5 || windeg <= 22.5) 16 | return "北风"; 17 | if (windeg > 22.5 && windeg <= 67.5) 18 | return "东北风"; 19 | if (windeg > 67.5 && windeg <= 112.5) 20 | return "东风"; 21 | if (windeg > 112.5 && windeg <= 157.5) 22 | return "东南风"; 23 | if (windeg > 157.5 && windeg <= 202.5) 24 | return "南风"; 25 | if (windeg > 202.5 && windeg <= 247.5) 26 | return "西南风"; 27 | if (windeg > 247.5 && windeg <= 292.5) 28 | return "西风"; 29 | if (windeg > 292.5 && windeg <= 337.5) 30 | return "西北风"; 31 | return string.Empty; 32 | } 33 | public static DateTime ToDateTime(this int timestamp) 34 | { 35 | string ID = TimeZoneInfo.Local.Id; 36 | DateTime start = new DateTime(1970, 1, 1) + TimeZoneInfo.Local.GetUtcOffset(DateTime.Now); 37 | DateTime startTime = TimeZoneInfo.ConvertTime(start, TimeZoneInfo.FindSystemTimeZoneById(ID)); 38 | return startTime.AddSeconds(timestamp); 39 | } 40 | public static string ToWeather(this string weather) 41 | { 42 | if (string.IsNullOrEmpty(weather)) 43 | return "未知"; 44 | 45 | weather = weather.Split(",")[0]; 46 | weather = weather.Split(",")[0]; 47 | if (VisualCrossingDic.ContainsKey(weather)) 48 | { 49 | weather = VisualCrossingDic[weather]; 50 | } 51 | 52 | return weather; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherResult/SeniverseResult.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class SeniverseResultLives 4 | { 5 | public SeniverseLivesResult[]? results { get; set; } 6 | } 7 | 8 | public class SeniverseLivesResult 9 | { 10 | public SeniverseLocation? location { get; set; } 11 | public SeniverseNow? now { get; set; } 12 | public DateTime last_update { get; set; } 13 | } 14 | 15 | public class SeniverseLocation 16 | { 17 | public string? id { get; set; } 18 | public string? name { get; set; } 19 | public string? country { get; set; } 20 | public string? path { get; set; } 21 | public string? timezone { get; set; } 22 | public string? timezone_offset { get; set; } 23 | } 24 | 25 | public class SeniverseNow 26 | { 27 | public string? text { get; set; } 28 | public string? code { get; set; } 29 | public string? temperature { get; set; } 30 | } 31 | 32 | 33 | public class SeniverseResultForeastDay 34 | { 35 | public ForeastDayResult[]? results { get; set; } 36 | } 37 | 38 | public class ForeastDayResult 39 | { 40 | public SeniverseLocation? location { get; set; } 41 | public SeniverseDaily[]? daily { get; set; } 42 | public DateTime last_update { get; set; } 43 | } 44 | 45 | public class SeniverseDaily 46 | { 47 | public string? date { get; set; } 48 | public string? text_day { get; set; } 49 | public string? code_day { get; set; } 50 | public string? text_night { get; set; } 51 | public string? code_night { get; set; } 52 | public string? high { get; set; } 53 | public string? low { get; set; } 54 | public string? rainfall { get; set; } 55 | public string? precip { get; set; } 56 | public string? wind_direction { get; set; } 57 | public string? wind_direction_degree { get; set; } 58 | public string? wind_speed { get; set; } 59 | public string? wind_scale { get; set; } 60 | public string? humidity { get; set; } 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Server/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: lightyellow; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 5 | display: none; 6 | left: 0; 7 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 8 | position: fixed; 9 | width: 100%; 10 | z-index: 1000; 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: 0.75rem; 17 | top: 0.5rem; 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Winform 2 | { 3 | partial class MainForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); 32 | blazorWebView = new Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView(); 33 | SuspendLayout(); 34 | // 35 | // blazorWebView 36 | // 37 | blazorWebView.Dock = DockStyle.Fill; 38 | blazorWebView.Location = new Point(0, 0); 39 | blazorWebView.Name = "blazorWebView"; 40 | blazorWebView.Size = new Size(800, 450); 41 | blazorWebView.TabIndex = 0; 42 | blazorWebView.Text = "blazorWebView"; 43 | // 44 | // MainForm 45 | // 46 | AutoScaleDimensions = new SizeF(9F, 20F); 47 | AutoScaleMode = AutoScaleMode.Font; 48 | ClientSize = new Size(800, 450); 49 | Controls.Add(blazorWebView); 50 | Icon = (Icon)resources.GetObject("$this.Icon"); 51 | Name = "MainForm"; 52 | Text = "你的天气"; 53 | ResumeLayout(false); 54 | } 55 | 56 | #endregion 57 | 58 | private Microsoft.AspNetCore.Components.WebView.WindowsForms.BlazorWebView blazorWebView; 59 | } 60 | } -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherSource/OpenWeatherSource.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | 3 | namespace YourWeather.Shared 4 | { 5 | public class OpenWeatherSource : BaseWeatherSource 6 | { 7 | public override async Task GetWeatherData(Location location) 8 | { 9 | var lat = location.Lat; 10 | var lon = location.Lon; 11 | WeatherLives? lives = await Lives(lat, lon); 12 | WeatherData data = new() 13 | { 14 | Lives = lives 15 | }; 16 | return data; 17 | } 18 | 19 | public async Task Lives(double lat, double lon) 20 | { 21 | using HttpClient Http = new(); 22 | //获取天气实况 23 | var livesUrl = $"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&units=metric&appid={Key}&lang=zh_cn"; 24 | OpenWeatherResultLives? lives = null; 25 | try 26 | { 27 | lives = await Http.GetFromJsonAsync(livesUrl); 28 | } 29 | catch (Exception) 30 | { 31 | 32 | throw; 33 | } 34 | 35 | 36 | if (lives is null) 37 | return null; 38 | 39 | bool state = lives.cod != 200; 40 | if (state) 41 | return null; 42 | 43 | WeatherLives weatherLives = new() 44 | { 45 | City = lives.name, 46 | Weather = lives?.weather?[0].description?.ToWeather(), 47 | Temp = Convert.ToInt32( lives?.main?.temp).ToString(), 48 | WindDeg = lives?.wind?.deg.ToWindDir(), 49 | WindSpeed = lives?.wind?.speed.ToString(), 50 | Humidity = lives?.main?.humidity.ToString(), 51 | FeelsLike = Convert.ToInt32(lives?.main?.feels_like).ToString(), 52 | Pressure = lives?.main?.pressure.ToString(), 53 | Visibility = (Convert.ToInt32(lives?.visibility)/1000).ToString(), 54 | MaxTemp = Convert.ToInt32(lives?.main?.temp_max).ToString(), 55 | MinTemp = Convert.ToInt32(lives?.main?.temp_min).ToString(), 56 | Cloud = lives?.clouds?.all.ToString(), 57 | }; 58 | return weatherLives; 59 | } 60 | 61 | 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/TitleBar.cs: -------------------------------------------------------------------------------- 1 | using PInvoke; 2 | using System.Runtime.InteropServices; 3 | using static PInvoke.User32; 4 | 5 | namespace YourWeather.Photino 6 | { 7 | public static class TitleBar 8 | { 9 | private static IntPtr _handle; 10 | public static void Init(IntPtr handle) 11 | { 12 | _handle = handle; 13 | } 14 | 15 | public static void EnableDarkMode(bool value) 16 | { 17 | UseImmersiveDarkMode(_handle, value); 18 | } 19 | 20 | [DllImport("dwmapi.dll")] 21 | private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); 22 | 23 | private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; 24 | private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; 25 | 26 | private static bool UseImmersiveDarkMode(IntPtr handle, bool enabled) 27 | { 28 | if (IsWindows10OrGreater(17763)) 29 | { 30 | var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; 31 | if (IsWindows10OrGreater(18985)) 32 | { 33 | attribute = DWMWA_USE_IMMERSIVE_DARK_MODE; 34 | } 35 | 36 | int useImmersiveDarkMode = enabled ? 1 : 0; 37 | bool flag = DwmSetWindowAttribute(handle, (int)attribute, ref useImmersiveDarkMode, sizeof(int)) == 0; 38 | var activeWindow = User32.GetActiveWindow(); 39 | if (_handle == activeWindow) 40 | { 41 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x00), IntPtr.Zero); 42 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x01), IntPtr.Zero); 43 | } 44 | else 45 | { 46 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x01), IntPtr.Zero); 47 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x00), IntPtr.Zero); 48 | } 49 | 50 | return flag; 51 | } 52 | 53 | return false; 54 | } 55 | 56 | private static bool IsWindows10OrGreater(int build = -1) 57 | { 58 | return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl.Desktop/TitleBar.cs: -------------------------------------------------------------------------------- 1 | using PInvoke; 2 | using System.Runtime.InteropServices; 3 | using static PInvoke.User32; 4 | 5 | namespace YourWeather.Rcl.Desktop 6 | { 7 | public static class TitleBar 8 | { 9 | private static IntPtr _handle; 10 | public static void Init(IntPtr handle) 11 | { 12 | _handle = handle; 13 | } 14 | 15 | public static void EnableDarkMode(bool value) 16 | { 17 | UseImmersiveDarkMode(_handle, value); 18 | } 19 | 20 | [DllImport("dwmapi.dll")] 21 | private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); 22 | 23 | private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; 24 | private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; 25 | 26 | private static bool UseImmersiveDarkMode(IntPtr handle, bool enabled) 27 | { 28 | if (IsWindows10OrGreater(17763)) 29 | { 30 | var attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; 31 | if (IsWindows10OrGreater(18985)) 32 | { 33 | attribute = DWMWA_USE_IMMERSIVE_DARK_MODE; 34 | } 35 | 36 | int useImmersiveDarkMode = enabled ? 1 : 0; 37 | bool flag = DwmSetWindowAttribute(handle, (int)attribute, ref useImmersiveDarkMode, sizeof(int)) == 0; 38 | var activeWindow = User32.GetActiveWindow(); 39 | if (_handle == activeWindow) 40 | { 41 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x00), IntPtr.Zero); 42 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x01), IntPtr.Zero); 43 | } 44 | else 45 | { 46 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x01), IntPtr.Zero); 47 | User32.PostMessage(_handle, WindowMessage.WM_NCACTIVATE, new IntPtr((int)0x00), IntPtr.Zero); 48 | } 49 | 50 | return flag; 51 | } 52 | 53 | return false; 54 | } 55 | 56 | private static bool IsWindows10OrGreater(int build = -1) 57 | { 58 | return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= build; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherResult/QWeatherResult.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | public class QWeatherResultLives 4 | { 5 | public string? code { get; set; } 6 | public DateTime updateTime { get; set; } 7 | public string? fxLink { get; set; } 8 | public QWeatherNow? now { get; set; } 9 | } 10 | 11 | public class QWeatherNow 12 | { 13 | public DateTime obsTime { get; set; } 14 | public string? temp { get; set; } 15 | public string? feelsLike { get; set; } 16 | public string? icon { get; set; } 17 | public string? text { get; set; } 18 | public string? wind360 { get; set; } 19 | public string? windDir { get; set; } 20 | public string? windScale { get; set; } 21 | public string? windSpeed { get; set; } 22 | public string? humidity { get; set; } 23 | public string? precip { get; set; } 24 | public string? pressure { get; set; } 25 | public string? vis { get; set; } 26 | public string? cloud { get; set; } 27 | public string? dew { get; set; } 28 | } 29 | 30 | 31 | public class QWeatherResultCity 32 | { 33 | public string? code { get; set; } 34 | public QWeatherLocation[]? location { get; set; } 35 | } 36 | 37 | public class QWeatherLocation 38 | { 39 | public string? name { get; set; } 40 | } 41 | 42 | 43 | public class QWeatherResultForeastDay 44 | { 45 | public string? code { get; set; } 46 | public string? updateTime { get; set; } 47 | public string? fxLink { get; set; } 48 | public QWeatherDaily[]? daily { get; set; } 49 | } 50 | 51 | public class QWeatherDaily 52 | { 53 | public string? fxDate { get; set; } 54 | public string? tempMax { get; set; } 55 | public string? tempMin { get; set; } 56 | public string? textDay { get; set; } 57 | } 58 | 59 | 60 | public class QWeatherResultForeastHours 61 | { 62 | public string? code { get; set; } 63 | public string? updateTime { get; set; } 64 | public string? fxLink { get; set; } 65 | public QWeatherHourly[]? hourly { get; set; } 66 | } 67 | 68 | 69 | public class QWeatherHourly 70 | { 71 | public DateTime fxTime { get; set; } 72 | public string? temp { get; set; } 73 | public string? text { get; set; } 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.WebAssembly/wwwroot/service-worker.published.js: -------------------------------------------------------------------------------- 1 | // Caution! Be sure you understand the caveats before publishing an application with 2 | // offline support. See https://aka.ms/blazor-offline-considerations 3 | 4 | self.importScripts('./service-worker-assets.js'); 5 | self.addEventListener('install', event => event.waitUntil(onInstall(event))); 6 | self.addEventListener('activate', event => event.waitUntil(onActivate(event))); 7 | self.addEventListener('fetch', event => event.respondWith(onFetch(event))); 8 | 9 | const cacheNamePrefix = 'offline-cache-'; 10 | const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; 11 | const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ]; 12 | const offlineAssetsExclude = [ /^service-worker\.js$/ ]; 13 | 14 | async function onInstall(event) { 15 | console.info('Service worker: Install'); 16 | 17 | // Fetch and cache all matching items from the assets manifest 18 | const assetsRequests = self.assetsManifest.assets 19 | .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) 20 | .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) 21 | .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' })); 22 | await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); 23 | } 24 | 25 | async function onActivate(event) { 26 | console.info('Service worker: Activate'); 27 | 28 | // Delete unused caches 29 | const cacheKeys = await caches.keys(); 30 | await Promise.all(cacheKeys 31 | .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) 32 | .map(key => caches.delete(key))); 33 | } 34 | 35 | async function onFetch(event) { 36 | let cachedResponse = null; 37 | if (event.request.method === 'GET') { 38 | // For all navigation requests, try to serve index.html from cache 39 | // If you need some URLs to be server-rendered, edit the following check to exclude those URLs 40 | const shouldServeIndexHtml = event.request.mode === 'navigate'; 41 | 42 | const request = shouldServeIndexHtml ? 'index.html' : event.request; 43 | const cache = await caches.open(cacheName); 44 | cachedResponse = await cache.match(request); 45 | } 46 | 47 | return cachedResponse || fetch(event.request); 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/IGeolocationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Darnton.Blazor.DeviceInterop.Geolocation 5 | { 6 | /// 7 | /// A wrapper around the device's Geolocation API services. 8 | /// . 9 | /// 10 | public interface IGeolocationService 11 | { 12 | /// 13 | /// A wrapper around the function, 14 | /// used to get the current position of the device. 15 | /// 16 | /// used to modify the request. 17 | /// The result of the geolocation request. 18 | Task GetCurrentPosition(PositionOptions options = null); 19 | /// 20 | /// A wrapper around the function, 21 | /// used to listen for position changes. If the service is listening, a event is fired 22 | /// each time the device's position changes. 23 | /// 24 | /// used to modify the request. 25 | /// A watch ID that refers to the handler. The ID can be used to unregister the handler with . 26 | Task WatchPosition(PositionOptions options = null); 27 | /// 28 | /// Handles the receipt of new positions. Fired whenever a handler is registered and the device's position changes. 29 | /// Invoked with the sender and the . 30 | /// 31 | event EventHandler WatchPositionReceived; 32 | /// 33 | /// A wrapper around the function, 34 | /// used to unregister a handler created with . 35 | /// 36 | /// The ID of the registered watch handler. 37 | /// A task that represents the async clear operation. 38 | Task ClearWatch(long watchId); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/wwwroot/js/geolocation.js: -------------------------------------------------------------------------------- 1 | export let Geolocation = { 2 | 3 | getCurrentPosition: async function (options) { 4 | var result = { position: null, error: null }; 5 | var getCurrentPositionPromise = new Promise((resolve, reject) => { 6 | if (!navigator.geolocation) { 7 | reject({ code: 0, message: 'This device does not support geolocation.' }); 8 | } else { 9 | navigator.geolocation.getCurrentPosition(resolve, reject, options); 10 | } 11 | }); 12 | await getCurrentPositionPromise.then( 13 | (position) => { this.mapPositionToResult(position, result) } 14 | ).catch( 15 | (error) => { this.mapErrorToResult(error, result) } 16 | ); 17 | return result; 18 | }, 19 | 20 | watchPosition: async function (dotNetCallbackRef, callbackMethod, options) { 21 | if (!navigator.geolocation) return null; 22 | 23 | return navigator.geolocation.watchPosition( 24 | (position) => { 25 | let result = { position: null, error: null }; 26 | this.mapPositionToResult(position, result); 27 | dotNetCallbackRef.invokeMethodAsync(callbackMethod, result); 28 | }, 29 | (error) => { 30 | let result = { position: null, error: null }; 31 | this.mapErrorToResult(error, result); 32 | dotNetCallbackRef.invokeMethodAsync(callbackMethod, result); 33 | }, 34 | options); 35 | }, 36 | 37 | clearWatch: function (id) { 38 | if (navigator.geolocation) { 39 | navigator.geolocation.clearWatch(id); 40 | } 41 | }, 42 | 43 | mapPositionToResult: function (position, result) { 44 | result.position = { 45 | coords: { 46 | latitude: position.coords.latitude, 47 | longitude: position.coords.longitude, 48 | altitude: position.coords.altitude, 49 | accuracy: position.coords.accuracy, 50 | altitudeAccuracy: position.coords.altitudeAccuracy, 51 | heading: position.coords.heading, 52 | speed: position.coords.speed 53 | }, 54 | timestamp: position.timestamp 55 | } 56 | }, 57 | 58 | mapErrorToResult: function (error, result) { 59 | result.error = { 60 | code: error.code, 61 | message: error.message 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherResult/AmapResult.cs: -------------------------------------------------------------------------------- 1 | namespace YourWeather.Shared 2 | { 3 | 4 | public class AmapResultLives 5 | { 6 | 7 | public string? Status { get; set; } 8 | public string? Infocode { get; set; } 9 | public AmapLife[]? Lives { get; set; } 10 | } 11 | 12 | 13 | public class AmapLife 14 | { 15 | public string? City { get; set; } 16 | public string? Adcode { get; set; } 17 | public string? Weather { get; set; } 18 | public string? Temperature { get; set; } 19 | public string? Winddirection { get; set; } 20 | public string? Windpower { get; set; } 21 | public string? Humidity { get; set; } 22 | public string? Reporttime { get; set; } 23 | } 24 | 25 | 26 | public class AmapResultForecastDay 27 | { 28 | 29 | public string? Status { get; set; } 30 | public string? Count { get; set; } 31 | public string? Info { get; set; } 32 | public string? Infocode { get; set; } 33 | 34 | public AmapForecast[]? Forecasts { get; set; } 35 | } 36 | 37 | 38 | public class AmapForecast 39 | { 40 | public string? City { get; set; } 41 | public string? Adcode { get; set; } 42 | public string? Province { get; set; } 43 | public string? Reporttime { get; set; } 44 | public AmapCast[]? Casts { get; set; } 45 | } 46 | 47 | 48 | public class AmapCast 49 | { 50 | public string? Date { get; set; } 51 | public string? Week { get; set; } 52 | public string? Dayweather { get; set; } 53 | public string? Nightweather { get; set; } 54 | public string? Daytemp { get; set; } 55 | public string? Nighttemp { get; set; } 56 | public string? Daywind { get; set; } 57 | public string? Nightwind { get; set; } 58 | public string? Daypower { get; set; } 59 | public string? Nightpower { get; set; } 60 | } 61 | 62 | 63 | public class AmapResultAdcode 64 | { 65 | 66 | public string? status { get; set; } 67 | 68 | public AmapRegeocode? regeocode { get; set; } 69 | 70 | public string? infocode { get; set; } 71 | } 72 | 73 | 74 | public class AmapRegeocode 75 | { 76 | public AmapAddresscomponent? addressComponent { get; set; } 77 | } 78 | 79 | 80 | public class AmapAddresscomponent 81 | { 82 | public string? adcode { get; set; } 83 | } 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /YourWeather/Darnton.Blazor.DeviceInterop/Geolocation/GeolocationService.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.JSInterop; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | namespace Darnton.Blazor.DeviceInterop.Geolocation 6 | { 7 | /// 8 | /// An implementation of that provides 9 | /// an interop layer for the device's Geolocation API. 10 | /// 11 | public class GeolocationService : IGeolocationService 12 | { 13 | private readonly JSBinder _jsBinder; 14 | 15 | /// 16 | public event EventHandler WatchPositionReceived; 17 | 18 | /// 19 | /// Constructs a object. 20 | /// 21 | /// 22 | public GeolocationService(IJSRuntime JSRuntime) 23 | { 24 | _jsBinder = new JSBinder(JSRuntime, "./_content/Darnton.Blazor.DeviceInterop/js/geolocation.js"); 25 | } 26 | 27 | /// 28 | public async Task GetCurrentPosition(PositionOptions options = null) 29 | { 30 | var module = await _jsBinder.GetModule(); 31 | return await module.InvokeAsync("Geolocation.getCurrentPosition", options); 32 | } 33 | 34 | /// 35 | public async Task WatchPosition(PositionOptions options = null) 36 | { 37 | var module = await _jsBinder.GetModule(); 38 | var callbackObj = DotNetObjectReference.Create(this); 39 | return await module.InvokeAsync("Geolocation.watchPosition", 40 | callbackObj, nameof(SetWatchPosition), options); 41 | } 42 | 43 | /// 44 | /// Invokes the event handler. 45 | /// Invoked by the success and error callbacks of the JavaScript watchPosition() function. 46 | /// 47 | /// A passed back from JavaScript. 48 | [JSInvokable] 49 | public void SetWatchPosition(GeolocationResult watchResult) 50 | { 51 | WatchPositionReceived?.Invoke(this, new GeolocationEventArgs 52 | { 53 | GeolocationResult = watchResult 54 | }); 55 | } 56 | 57 | /// 58 | public async Task ClearWatch(long watchId) 59 | { 60 | var module = await _jsBinder.GetModule(); 61 | await module.InvokeVoidAsync("Geolocation.clearWatch", watchId); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Maui/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: lightyellow; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 5 | display: none; 6 | left: 0; 7 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 8 | position: fixed; 9 | width: 100%; 10 | z-index: 1000; 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: 0.75rem; 17 | top: 0.5rem; 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | 30 | .status-bar-safe-area { 31 | display: none; 32 | } 33 | 34 | @supports (-webkit-touch-callout: none) { 35 | .status-bar-safe-area { 36 | display: flex; 37 | position: sticky; 38 | top: 0; 39 | height: env(safe-area-inset-top); 40 | background-color: #f7f7f7; 41 | width: 100%; 42 | z-index: 1; 43 | } 44 | 45 | .flex-column, .navbar-brand { 46 | padding-left: env(safe-area-inset-left); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Wpf/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: lightyellow; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 5 | display: none; 6 | left: 0; 7 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 8 | position: fixed; 9 | width: 100%; 10 | z-index: 1000; 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: 0.75rem; 17 | top: 0.5rem; 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | 30 | .status-bar-safe-area { 31 | display: none; 32 | } 33 | 34 | @supports (-webkit-touch-callout: none) { 35 | .status-bar-safe-area { 36 | display: flex; 37 | position: sticky; 38 | top: 0; 39 | height: env(safe-area-inset-top); 40 | background-color: #f7f7f7; 41 | width: 100%; 42 | z-index: 1; 43 | } 44 | 45 | .flex-column, .navbar-brand { 46 | padding-left: env(safe-area-inset-left); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Photino/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: lightyellow; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 5 | display: none; 6 | left: 0; 7 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 8 | position: fixed; 9 | width: 100%; 10 | z-index: 1000; 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: 0.75rem; 17 | top: 0.5rem; 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | 30 | .status-bar-safe-area { 31 | display: none; 32 | } 33 | 34 | @supports (-webkit-touch-callout: none) { 35 | .status-bar-safe-area { 36 | display: flex; 37 | position: sticky; 38 | top: 0; 39 | height: env(safe-area-inset-top); 40 | background-color: #f7f7f7; 41 | width: 100%; 42 | z-index: 1; 43 | } 44 | 45 | .flex-column, .navbar-brand { 46 | padding-left: env(safe-area-inset-left); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Winform/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | #blazor-error-ui { 2 | background: lightyellow; 3 | bottom: 0; 4 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); 5 | display: none; 6 | left: 0; 7 | padding: 0.6rem 1.25rem 0.7rem 1.25rem; 8 | position: fixed; 9 | width: 100%; 10 | z-index: 1000; 11 | } 12 | 13 | #blazor-error-ui .dismiss { 14 | cursor: pointer; 15 | position: absolute; 16 | right: 0.75rem; 17 | top: 0.5rem; 18 | } 19 | 20 | .blazor-error-boundary { 21 | background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; 22 | padding: 1rem 1rem 1rem 3.7rem; 23 | color: white; 24 | } 25 | 26 | .blazor-error-boundary::after { 27 | content: "An error has occurred." 28 | } 29 | 30 | .status-bar-safe-area { 31 | display: none; 32 | } 33 | 34 | @supports (-webkit-touch-callout: none) { 35 | .status-bar-safe-area { 36 | display: flex; 37 | position: sticky; 38 | top: 0; 39 | height: env(safe-area-inset-top); 40 | background-color: #f7f7f7; 41 | width: 100%; 42 | z-index: 1; 43 | } 44 | 45 | .flex-column, .navbar-brand { 46 | padding-left: env(safe-area-inset-left); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Shared/Models/WeatherSource/SeniverseSource.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | 3 | namespace YourWeather.Shared 4 | { 5 | public class SeniverseSource : BaseWeatherSource 6 | { 7 | public override async Task GetWeatherData(Location location) 8 | { 9 | var lat = location.Lat; 10 | var lon = location.Lon; 11 | using HttpClient Http = new(); 12 | WeatherLives? lives = await Lives(lat, lon,Http); 13 | List? forecastDays = await ForecastDay(lat, lon, Http); 14 | WeatherData data = new() 15 | { 16 | Lives = lives, 17 | ForecastDays = forecastDays 18 | }; 19 | return data; 20 | } 21 | 22 | public async Task?> ForecastDay(double lat, double lon,HttpClient Http) 23 | { 24 | var forecastUrl = $"https://api.seniverse.com/v3/weather/daily.json?key={Key}&location={lat}:{lon}"; 25 | SeniverseResultForeastDay? forecast = null; 26 | try 27 | { 28 | forecast = await Http.GetFromJsonAsync(forecastUrl); 29 | } 30 | catch (Exception) 31 | { 32 | 33 | throw; 34 | } 35 | 36 | if(forecast is null) 37 | return null; 38 | 39 | List forecastDays = new(); 40 | foreach (var item in forecast.results[0].daily) 41 | { 42 | WeatherForecastDay forecastDay = new() 43 | { 44 | Weather = item.text_day, 45 | Date = DateTime.ParseExact(item.date, "yyyy-MM-dd", System.Globalization.CultureInfo.CurrentCulture), 46 | MaxTemp = item.high, 47 | MinTemp = item.low, 48 | }; 49 | forecastDays.Add(forecastDay); 50 | } 51 | 52 | return forecastDays; 53 | } 54 | 55 | 56 | public async Task Lives(double lat, double lon, HttpClient Http) 57 | { 58 | //获取天气实况 59 | var livesUrl = $"https://api.seniverse.com/v3/weather/now.json?key={Key}&location={lat}:{lon}"; 60 | SeniverseResultLives? lives = null; 61 | try 62 | { 63 | lives = await Http.GetFromJsonAsync(livesUrl); 64 | } 65 | catch (Exception) 66 | { 67 | 68 | throw; 69 | } 70 | 71 | 72 | if (lives is null) 73 | return null; 74 | 75 | WeatherLives weatherLives = new() 76 | { 77 | City = lives?.results?[0].location?.name, 78 | Weather = lives?.results?[0].now?.text, 79 | Temp = lives?.results?[0].now?.temperature, 80 | }; 81 | return weatherLives; 82 | } 83 | 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Pages/WeatherPage.razor.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using YourWeather.Rcl.Components; 3 | using YourWeather.Shared; 4 | 5 | namespace YourWeather.Rcl.Pages 6 | { 7 | public partial class WeatherPage : PageComponentBase 8 | { 9 | private Location? Location; 10 | private WeatherSourceType WeatherSourceType; 11 | private WeatherData WeatherData = new(); 12 | private string? Key; 13 | private bool ShowLoading = true; 14 | 15 | protected override async Task OnInitializedAsync() 16 | { 17 | await base.OnInitializedAsync(); 18 | Title = "天气"; 19 | } 20 | 21 | protected override async Task OnAfterRenderAsync(bool firstRender) 22 | { 23 | await base.OnAfterRenderAsync(firstRender); 24 | if (firstRender) 25 | { 26 | await LoadSettings(); 27 | await UpdateWeatherDate(); 28 | } 29 | } 30 | 31 | private WeatherLives? Lives => WeatherData.Lives; 32 | private List? ForecastHours => WeatherData.ForecastHours; 33 | private List? ForecastDays => WeatherData.ForecastDays; 34 | 35 | private async Task LoadSettings() 36 | { 37 | var weatherSourceType = await SettingsService.Get(SettingType.WeatherSource); 38 | WeatherSourceType = (WeatherSourceType)weatherSourceType; 39 | Key = await SettingsService.Get(WeatherSourceType.ToString(), null); 40 | var location = await SettingsService.Get(SettingType.Location); 41 | if (!string.IsNullOrEmpty(location)) 42 | { 43 | Location = JsonSerializer.Deserialize(location); 44 | } 45 | else 46 | { 47 | Location = await LocationService.GetCurrentLocation(); 48 | } 49 | } 50 | 51 | private async Task UpdateWeatherDate() 52 | { 53 | await UpdateWeatherDate(WeatherSourceType, Location); 54 | ShowLoading = false; 55 | await InvokeAsync(StateHasChanged); 56 | } 57 | 58 | private async Task UpdateWeatherDate(WeatherSourceType weather, Location? location) 59 | { 60 | if (location != null) 61 | { 62 | var weatherData = await WeatherService.GetWeatherData(weather, location, Key); 63 | if (weatherData != null) 64 | { 65 | WeatherData = weatherData; 66 | } 67 | else 68 | { 69 | await PopupService.EnqueueSnackbarAsync(new() 70 | { 71 | Content = "天气数据请求失败,请尝试更换key,或更换天气源", 72 | Type = BlazorComponent.AlertTypes.Error, 73 | }); 74 | } 75 | } 76 | 77 | await InvokeAsync(StateHasChanged); 78 | } 79 | 80 | private string GetDayWeatherIcon(string weather) => WeatherService.GetWeatherIcon(weather); 81 | private string GetWeatherIcon(string weather, DateTime dateTime) => WeatherService.GetWeatherIcon(weather, dateTime); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /YourWeather/YourWeather.Rcl/Pages/LocationPage.razor.cs: -------------------------------------------------------------------------------- 1 | using Masa.Blazor; 2 | using System.Text.Json; 3 | using YourWeather.Rcl.Components; 4 | using YourWeather.Shared; 5 | 6 | namespace YourWeather.Rcl.Pages 7 | { 8 | public partial class LocationPage : PageComponentBase 9 | { 10 | private bool ShowDetails; 11 | private Location? Location; 12 | private Location CurrentLocation = new(); 13 | private Location DetailsLocation = new(); 14 | private List LocationDatas = new (); 15 | 16 | protected override async Task OnInitializedAsync() 17 | { 18 | await base.OnInitializedAsync(); 19 | Title = "位置"; 20 | } 21 | 22 | protected override async Task OnAfterRenderAsync(bool firstRender) 23 | { 24 | await base.OnAfterRenderAsync(firstRender); 25 | if (firstRender) 26 | { 27 | await LoadSettings(); 28 | await InvokeAsync(StateHasChanged); 29 | } 30 | } 31 | 32 | private async Task LoadSettings() 33 | { 34 | var locationsJson = await SettingsService.Get(SettingType.Locations); 35 | if (!string.IsNullOrEmpty(locationsJson)) 36 | { 37 | LocationDatas = JsonSerializer.Deserialize>(locationsJson) ?? new(); 38 | } 39 | 40 | var locationJson = await SettingsService.Get(SettingType.Location); 41 | if (!string.IsNullOrEmpty(locationJson)) 42 | { 43 | var location = JsonSerializer.Deserialize(locationJson) ?? new(); 44 | Location = LocationDatas.FirstOrDefault(it=>it.Info == location.Info); 45 | } 46 | 47 | CurrentLocation = await LocationService.GetCurrentLocation(); 48 | } 49 | private async Task SetLocation(Location? value) 50 | { 51 | Location = value; 52 | string? location = null; 53 | if(value != null) 54 | { 55 | location = JsonSerializer.Serialize(value); 56 | } 57 | await SettingsService.Save(SettingType.Location, location); 58 | } 59 | 60 | private async Task DeleteLocation(Location location) 61 | { 62 | var confirmed = await PopupService.ConfirmAsync(param => 63 | { 64 | param.Title = "删除"; 65 | param.Content = "确定删除这个位置吗?"; 66 | param.OkText = "删除"; 67 | param.CancelText = "取消"; 68 | param.OkProps = it => { it.Color = "red"; }; 69 | }); 70 | if (!confirmed) 71 | { 72 | return; 73 | } 74 | 75 | if(Location != null && location.Info == Location.Info) 76 | { 77 | await SetLocation(null); 78 | } 79 | 80 | LocationDatas.Remove(location); 81 | var locations = JsonSerializer.Serialize(LocationDatas); 82 | await SettingsService.Save(SettingType.Locations, locations); 83 | } 84 | 85 | private void OpenDetails(Location? location) 86 | { 87 | DetailsLocation = location ?? new(); 88 | ShowDetails = true; 89 | } 90 | } 91 | } 92 | --------------------------------------------------------------------------------