Mobile UI LoginOrRegisterPage 90%

This commit is contained in:
Aigio Liu
2021-05-29 23:35:29 +08:00
parent 37fc76000c
commit bd3bfa5448
13 changed files with 232 additions and 139 deletions
@@ -82,5 +82,11 @@ namespace System.Application.UI.ViewModels
isDialog: true // 锁定父窗口,防止打开多个 WebViewWindow
);
}
internal Task GoToLoginOrRegisterByPhoneNumberAsync()
{
LoginState = 1;
return Task.CompletedTask;
}
}
}
@@ -51,6 +51,7 @@ namespace System.Application.UI.ViewModels
switch (channel)
{
case nameof(PhoneNumber):
await GoToLoginOrRegisterByPhoneNumberAsync();
break;
}
}
@@ -106,7 +107,8 @@ namespace System.Application.UI.ViewModels
set => this.RaiseAndSetIfChanged(ref _IsLoading, value);
}
short _LoginState = 2;
const short _LoginStateDefault = 2;
short _LoginState = _LoginStateDefault;
public short LoginState
{
get => _LoginState;
@@ -147,10 +149,12 @@ namespace System.Application.UI.ViewModels
Toast.Show(msg);
}
#if !__MOBILE__
public void ChangeLoginState(short state)
{
LoginState = state;
}
#endif
public Action? Close { get; set; }
@@ -24,7 +24,7 @@ namespace System.Application.Converters
nameof(FastLoginChannel.Apple) => ColorFromHex("#000000"),
nameof(FastLoginChannel.QQ) => ColorFromHex("#12B7F5"),
"Phone" or "PhoneNumber" => ColorFromHex("#2196F3"),
_ => null,
_ => Binding.DoNothing,
};
}
@@ -11,9 +11,9 @@ namespace System.Application.Converters
{
public class LoginOrRegisterChannelTextConverter : IMultiValueConverter
{
public object? Convert(IList<object?> values, Type? targetType, object? parameter, CultureInfo? culture)
public object? Convert(IList<object?>? values, Type? targetType, object? parameter, CultureInfo? culture)
{
if (values.Count >= 3)
if (values != null && values.Count >= 3)
{
var stringValues = values.Select(x => (x is not string str) ? x?.ToString() ?? string.Empty : str).ToArray();
var args = stringValues.Skip(1).FirstOrDefault();
@@ -28,12 +28,12 @@ namespace System.Application.Converters
var format = stringValues.FirstOrDefault() ?? string.Empty;
return format.Format(args);
}
return null;
return Binding.DoNothing;
}
public object? Convert(object?[] values, Type? targetType, object? parameter, CultureInfo? culture)
public object? Convert(object?[]? values, Type? targetType, object? parameter, CultureInfo? culture)
{
IList<object?> values_ = values;
IList<object?>? values_ = values;
return Convert(values_, targetType, parameter, culture);
}
@@ -17,7 +17,7 @@ namespace System.Application.Converters
catch
{
}
return null;
return Binding.DoNothing;
}
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
@@ -28,7 +28,7 @@ namespace System.Application.Converters
var r = GetGeometryByPathString(this, valueStr);
return r;
}
return null;
return Binding.DoNothing;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException();
@@ -20,7 +20,7 @@ namespace System.Application.Converters
return r;
}
}
return null;
return Binding.DoNothing;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException();
@@ -42,7 +42,7 @@ namespace System.Application.Converters
var r = GetResourceByKey(valueStr);
return r;
}
return null;
return Binding.DoNothing;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => throw new NotImplementedException();
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
#if !__MOBILE__
using Avalonia.Data.Converters;
#else
using Xamarin.Forms;
#endif
namespace System.Application.Converters
{
public class SwitchMultiValueConverter : IMultiValueConverter
{
public object? Convert(IList<object?>? values, Type? targetType, object? parameter, CultureInfo? culture)
{
if (values != null && values.Count > 0)
{
var first = values.FirstOrDefault();
if (first != null)
{
try
{
var index = System.Convert.ToInt32(first);
if (index > 0 && index < values.Count)
{
return values[index];
}
}
catch
{
}
}
}
return Binding.DoNothing;
}
public object? Convert(object?[]? values, Type? targetType, object? parameter, CultureInfo? culture)
{
IList<object?>? values_ = values;
return Convert(values_, targetType, parameter, culture);
}
public object? ConvertBack(object? value, Type? targetType, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
public object[]? ConvertBack(object? value, Type[]? targetTypes, object? parameter, CultureInfo? culture) => throw new NotImplementedException();
}
}
+3 -1
View File
@@ -5,12 +5,14 @@ namespace System.Application.UI
{
public partial class AppShell : Shell
{
public const string Route_LoginOrRegister_Secondary = "LoginOrRegister/Secondary";
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(ItemDetailPage), typeof(ItemDetailPage));
Routing.RegisterRoute(nameof(NewItemPage), typeof(NewItemPage));
Routing.RegisterRoute("LoginOrRegister/PhoneNumber", typeof(LoginOrRegisterPage));
Routing.RegisterRoute(Route_LoginOrRegister_Secondary, typeof(LoginOrRegisterPage));
}
}
}
@@ -11,7 +11,7 @@ namespace System.Application.UI.ViewModels
Title = "About";
OpenWebCommand = new Command(async ()
//=> await Browser.OpenAsync("https://aka.ms/xamarin-quickstart")
=> await Shell.Current.GoToAsync("//LoginOrRegister/PhoneNumber")
=> await Shell.Current.GoToAsync($"//LoginOrRegister")
);
}
@@ -1,7 +1,10 @@
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace System.Application.UI.ViewModels
{
[QueryProperty(nameof(LoginState_), nameof(LoginState))]
partial class LoginOrRegisterPageViewModel : PageViewModel
{
internal static Task FastLoginOrRegisterAsync(Action? close = null, FastLoginChannel channel = FastLoginChannel.Steam, bool isBind = false)
@@ -9,5 +12,18 @@ namespace System.Application.UI.ViewModels
Toast.Show(channel.ToString());
return Task.CompletedTask;
}
static async Task GoToLoginOrRegisterByPhoneNumberAsync()
{
var url = $"//{AppShell.Route_LoginOrRegister_Secondary}?{nameof(LoginState)}=1";
await Shell.Current.GoToAsync(url);
}
public short LoginState_
{
set => LoginState = value == default ? _LoginStateDefault : value;
}
public ICommand? TbPhoneNumberReturnCommand { get; set; }
}
}
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
@@ -14,8 +14,10 @@
android:TabbedPage.ToolbarPlacement="Top"
x:Class="System.Application.UI.Views.LoginOrRegisterPage"
Title="{Binding Title}"
BackgroundColor="{DynamicResource WindowBackground}"
Shell.NavBarIsVisible="True">
<TabbedPage.Resources>
<ContentPage.Resources>
<converters:SwitchMultiValueConverter x:Key="SwitchMultiValueConverter"/>
<ControlTemplate x:Key="AgreementAndPrivacy">
<Label HorizontalOptions="Center" BindingContext="{Binding Path=., Source={RelativeSource AncestorType={x:Type viewmodels:LoginOrRegisterPageViewModel}}}">
<Label.FormattedText>
@@ -45,131 +47,146 @@
</Label.FormattedText>
</Label>
</ControlTemplate>
<converters:LoginOrRegisterChannelTextConverter x:Key="LoginOrRegisterChannelTextConverter"/>
<converters:PathIconConverter x:Key="PathIconConverter"/>
<converters:LoginOrRegisterChannelBackgroundColorConverter x:Key="LoginOrRegisterChannelBackgroundColorConverter"/>
</TabbedPage.Resources>
<TabbedPage.Children>
<ContentPage Title="{x:Static resx:AppResources.User_FastLogin}"
BackgroundColor="{DynamicResource WindowBackground}">
<ContentPage.Content>
<ScrollView>
<NavigationPage.TitleView>
<Slider HeightRequest="44" WidthRequest="300" />
</NavigationPage.TitleView>
<StackLayout VerticalOptions="CenterAndExpand">
<Label Padding="22" FontSize="17" Text="{x:Static resx:AppResources.User_FastLoginTip}"/>
<StackLayout BindableLayout.ItemsSource="{Binding Channels}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView VerticalOptions="CenterAndExpand">
<controls:CardButton Padding="0"
<ControlTemplate x:Key="LoginState_2">
<ScrollView BindingContext="{Binding Path=., Source={RelativeSource AncestorType={x:Type viewmodels:LoginOrRegisterPageViewModel}}}">
<ScrollView.Triggers>
<DataTrigger TargetType="ScrollView" Binding="{Binding IsLoading}" Value="True">
<Setter Property="IsVisible" Value="False" />
</DataTrigger>
</ScrollView.Triggers>
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand">
<Label Padding="22" FontSize="17" Text="{x:Static resx:AppResources.User_FastLoginTip}"/>
<StackLayout BindableLayout.ItemsSource="{Binding Channels}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView VerticalOptions="CenterAndExpand">
<controls:CardButton Padding="0"
Margin="22,0,22,22"
CommandParameter="{Binding}"
CommandParameter="{Binding Path=.}"
Command="{Binding Path=ChooseChannel, Source={RelativeSource AncestorType={x:Type viewmodels:LoginOrRegisterPageViewModel}}}">
<StackLayout Orientation="Horizontal">
<ContentView Padding="16"
BackgroundColor="{Binding Converter={StaticResource LoginOrRegisterChannelBackgroundColorConverter}}">
<Path Style="{DynamicResource PathIcon}"
Data="{Binding Converter={StaticResource PathIconConverter}}"/>
</ContentView>
<Label Margin="16" FontSize="20">
<Label.Text>
<MultiBinding Converter="{StaticResource LoginOrRegisterChannelTextConverter}">
<Binding Source="{x:Static resx:AppResources.User_UseFastLoginChannel_}"/>
<Binding Path="."/>
<Binding Source="{x:Static resx:AppResources.User_UsePhoneNumLoginChannel}"/>
</MultiBinding>
</Label.Text>
</Label>
</StackLayout>
</controls:CardButton>
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<ContentView Margin="0,0,0,22" ControlTemplate="{StaticResource AgreementAndPrivacy}"/>
<StackLayout Orientation="Horizontal">
<StackLayout.Resources>
<converters:LoginOrRegisterChannelTextConverter x:Key="LoginOrRegisterChannelTextConverter"/>
<converters:PathIconConverter x:Key="PathIconConverter"/>
<converters:LoginOrRegisterChannelBackgroundColorConverter x:Key="LoginOrRegisterChannelBackgroundColorConverter"/>
</StackLayout.Resources>
<ContentView Padding="16" BackgroundColor="{Binding Path=., Converter={StaticResource LoginOrRegisterChannelBackgroundColorConverter}}">
<Path Style="{DynamicResource PathIcon}" Data="{Binding Converter={StaticResource PathIconConverter}}"/>
</ContentView>
<Label Margin="16" FontSize="20">
<Label.Text>
<MultiBinding Converter="{StaticResource LoginOrRegisterChannelTextConverter}">
<Binding Source="{x:Static resx:AppResources.User_UseFastLoginChannel_}"/>
<Binding Path="."/>
<Binding Source="{x:Static resx:AppResources.User_UsePhoneNumLoginChannel}"/>
</MultiBinding>
</Label.Text>
</Label>
</StackLayout>
</controls:CardButton>
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
<!--<CollectionView ItemsSource="{Binding FastLoginChannels}" HeightRequest="450" VerticalOptions="CenterAndExpand">
<CollectionView.Header>
<Label Padding="22" FontSize="16" Text="{x:Static resx:AppResources.User_FastLoginTip}"/>
</CollectionView.Header>
<CollectionView.Footer>
<ContentView Margin="0,0,0,22" ControlTemplate="{StaticResource AgreementAndPrivacy}"/>
</CollectionView.Footer>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="st_root:FastLoginChannel">
<ContentView>
<Frame Padding="16" Margin="22,0,22,22">
<Frame.GestureRecognizers>
<TapGestureRecognizer CommandParameter="{Binding}"
Command="{Binding Path=FastLogin, Source={RelativeSource AncestorType={x:Type viewmodels:LoginOrRegisterPageViewModel}}}"/>
</Frame.GestureRecognizers>
<StackLayout Spacing="16">
<Label Text="{Binding}"
FontSize="24"/>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>-->
</ContentPage.Content>
</ContentPage>
<ContentPage Title="{x:Static resx:AppResources.User_PhoneLogin}"
BackgroundColor="{DynamicResource WindowBackground}">
<ContentPage.Content>
<ScrollView>
<StackLayout Orientation="Vertical">
<ActivityIndicator IsRunning="True"
IsVisible="False"
Color="{StaticResource Primary}"
VerticalOptions="CenterAndExpand"/>
<Grid Margin="22,42" IsVisible="True" VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="192"/>
<RowDefinition Height="56"/>
<RowDefinition Height="56"/>
<RowDefinition Height="56"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="106"/>
</Grid.ColumnDefinitions>
<Image Scale="2" Source="splash_logo.png" Grid.ColumnSpan="2"/>
<controls:TextBox x:Name="TbPhoneNumber"
ReturnType="Next"
Margin="0,0,0,10"
Placeholder="{x:Static resx:AppResources.User_Phone}"
Keyboard="Telephone"
MaxLength="{Binding Path=PhoneNumber, Mode=OneTime, Source={x:Static ccl_services:InputLengthConstants.Current}}"
Grid.Row="1"
Grid.ColumnSpan="2"/>
<controls:TextBox x:Name="TbSmsCode"
ReturnType="Done"
Margin="0,0,0,10"
Placeholder="{x:Static resx:AppResources.User_SMSCode}"
MaxLength="{Binding Path=SMS_CAPTCHA, Mode=OneTime, Source={x:Static ccl_services:InputLengthConstants.Current}}"
Keyboard="Numeric"
Grid.Row="2"
Grid.ColumnSpan="2"/>
<controls:TextButton Margin="0,0,0,8"
<ContentView Margin="0,0,0,22" ControlTemplate="{StaticResource AgreementAndPrivacy}"/>
</StackLayout>
</ScrollView>
</ControlTemplate>
<ControlTemplate x:Key="LoginState_1">
<ScrollView BindingContext="{Binding Path=., Source={RelativeSource AncestorType={x:Type viewmodels:LoginOrRegisterPageViewModel}}}">
<ScrollView.Triggers>
<DataTrigger TargetType="ScrollView" Binding="{Binding IsLoading}" Value="True">
<Setter Property="IsVisible" Value="False" />
</DataTrigger>
</ScrollView.Triggers>
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand">
<Grid Margin="22,42" VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="192"/>
<RowDefinition Height="56"/>
<RowDefinition Height="56"/>
<RowDefinition Height="56"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="106"/>
</Grid.ColumnDefinitions>
<Image Scale="2" Source="splash_logo.png" Grid.ColumnSpan="2"/>
<controls:TextBox x:Name="TbPhoneNumber"
ReturnType="Next"
ReturnCommand="{Binding TbPhoneNumberReturnCommand}"
ReturnCommandParameter="{x:Reference TbSmsCode}"
Margin="0,0,0,10"
Placeholder="{x:Static resx:AppResources.User_Phone}"
Keyboard="Telephone"
MaxLength="{Binding Path=PhoneNumber, Mode=OneTime, Source={x:Static ccl_services:InputLengthConstants.Current}}"
Grid.Row="1"
Grid.ColumnSpan="2"/>
<controls:TextBox x:Name="TbSmsCode"
ReturnType="Done"
ReturnCommand="{Binding Submit}"
Margin="0,0,0,10"
Placeholder="{x:Static resx:AppResources.User_SMSCode}"
MaxLength="{Binding Path=SMS_CAPTCHA, Mode=OneTime, Source={x:Static ccl_services:InputLengthConstants.Current}}"
Keyboard="Numeric"
Grid.Row="2"
Grid.ColumnSpan="2"/>
<controls:TextButton Margin="0,0,0,8"
Text="{Binding BtnSendSmsCodeText}"
Grid.Row="2"
Grid.Column="1"
Command="{Binding SendSms}"/>
<Button Margin="0,0,0,12"
<Button Margin="0,0,0,12"
Text="{x:Static resx:AppResources.LoginAndRegister}"
Command="{Binding Submit}"
Grid.Row="3"
Grid.ColumnSpan="2"/>
<ContentView Margin="0,0,0,32" Grid.Row="4" Grid.ColumnSpan="2" ControlTemplate="{StaticResource AgreementAndPrivacy}"/>
</Grid>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
</TabbedPage.Children>
</TabbedPage>
<ContentView Margin="0,0,0,32" Grid.Row="4" Grid.ColumnSpan="2" ControlTemplate="{StaticResource AgreementAndPrivacy}"/>
</Grid>
</StackLayout>
</ScrollView>
</ControlTemplate>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand">
<ActivityIndicator IsRunning="True"
IsVisible="False"
Color="{StaticResource Primary}"
VerticalOptions="CenterAndExpand"/>
<ContentView>
<ContentView.ControlTemplate>
<MultiBinding Converter="{StaticResource SwitchMultiValueConverter}">
<Binding Path="LoginState"/>
<Binding Source="{StaticResource LoginState_1}"/>
<Binding Source="{StaticResource LoginState_2}"/>
</MultiBinding>
</ContentView.ControlTemplate>
</ContentView>
</StackLayout>
<!--<CollectionView ItemsSource="{Binding FastLoginChannels}" HeightRequest="450" VerticalOptions="CenterAndExpand">
<CollectionView.Header>
<Label Padding="22" FontSize="16" Text="{x:Static resx:AppResources.User_FastLoginTip}"/>
</CollectionView.Header>
<CollectionView.Footer>
<ContentView Margin="0,0,0,22" ControlTemplate="{StaticResource AgreementAndPrivacy}"/>
</CollectionView.Footer>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="st_root:FastLoginChannel">
<ContentView>
<Frame Padding="16" Margin="22,0,22,22">
<Frame.GestureRecognizers>
<TapGestureRecognizer CommandParameter="{Binding}"
Command="{Binding Path=FastLogin, Source={RelativeSource AncestorType={x:Type viewmodels:LoginOrRegisterPageViewModel}}}"/>
</Frame.GestureRecognizers>
<StackLayout Spacing="16">
<Label Text="{Binding}"
FontSize="24"/>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>-->
</ContentPage.Content>
</ContentPage>
@@ -1,3 +1,4 @@
using ReactiveUI;
using System.Application.UI.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
@@ -10,15 +11,14 @@ namespace System.Application.UI.Views
public LoginOrRegisterPage()
{
InitializeComponent();
BindingContext = new LoginOrRegisterPageViewModel();
TbPhoneNumber.ReturnCommand = new Command(() => TbSmsCode.Focus());
TbSmsCode.ReturnCommand = new Command(() =>
BindingContext = new LoginOrRegisterPageViewModel
{
if (BindingContext is LoginOrRegisterPageViewModel vm)
TbPhoneNumberReturnCommand = ReactiveCommand.Create<Entry>(textBox =>
{
vm.Submit.Invoke();
}
});
if (BindingContext is LoginOrRegisterPageViewModel vm) vm.SendSms.Invoke();
textBox?.Focus();
}),
};
}
}
}