Compare commits

...

25 Commits

Author SHA1 Message Date
Lou BRODA fcd9533b95 Mise à jour de 'README.md'
1 year ago
Lou BRODA a98405ded3 Mise à jour de 'README.md'
1 year ago
Lou BRODA 12fdc3747f Mise à jour de 'README.md'
1 year ago
Lou BRODA 6858476667 Mise à jour de 'README.md'
1 year ago
Lou BRODA 150487b6fe Merge branch 'fix-bugs-22-10'
1 year ago
Lou BRODA aa96dc3442 ADD : Infos en ligne + Description
1 year ago
Lou BRODA 038cdeb01a Mise à jour de 'README.md'
1 year ago
Lou BRODA 5f624d0de1 Mise à jour de 'README.md'
1 year ago
Lou BRODA c63ed977fa ADD : Changement Status Working + Navigation Status bug fixed
1 year ago
Lou BRODA bb53472775 Merge branch 'fix-bugs-21-10'
1 year ago
Lou BRODA 8920b37bb8 Mise à jour de 'README.md'
1 year ago
Lou BRODA 7f0cdf9bda Mise à jour de 'README.md'
1 year ago
Lou BRODA 094b18895f Transférer les fichiers vers 'documentation'
1 year ago
Lou BRODA 50fd90e87d Mise à jour de 'README.md'
1 year ago
Lou BRODA cbfcc95e55 ADD : Correct some bugs (Prêts & Contacts)
1 year ago
Lou BRODA 327415d79e Mise à jour de 'README.md'
1 year ago
Lou BRODA 4a47c2198b Mise à jour de 'README.md'
1 year ago
Lou BRODA 68371c878f ADD : Avancement ScanView (ToFinish)
1 year ago
Lou BRODA 37cd46dee8 ADD : Scan BarCode working - method to add book ToAdd
1 year ago
Lou BRODA a480a564ad ADD : Start Camera Scan implementation
1 year ago
Lou BRODA d1c24c19d2 Merge branch 'master' of https://codefirst.iut.uca.fr/git/lou.broda/LivreLand
1 year ago
Lou BRODA 6681924b44 Mise à jour de 'README.md'
1 year ago
Lou BRODA 96b535075f Mise à jour de 'README.md'
1 year ago
Lou BRODA 7e3e2a9852 Mise à jour de 'README.md'
1 year ago
Lou BRODA 54e3491f62 Mise à jour de 'README.md'
1 year ago

@ -15,6 +15,7 @@ public partial class AppShell : Shell
Routing.RegisterRoute("library/date/", typeof(FiltrageDateView));
Routing.RegisterRoute("library/note/", typeof(FiltrageNoteView));
Routing.RegisterRoute("library/contacts/", typeof(ContactsView));
Routing.RegisterRoute("library/scan/", typeof(ScanView));
Routing.RegisterRoute("library/tous/details/", typeof(DetailsLivreView));
InitializeComponent();
}

@ -53,6 +53,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Camera.MAUI" Version="1.4.4" />
<PackageReference Include="CommunityToolkit.Maui" Version="5.3.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
</ItemGroup>
@ -137,6 +138,9 @@
<MauiXaml Update="View\HeaderPage.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="View\ScanView.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="View\StatutLectureView.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>

@ -1,4 +1,5 @@
using CommunityToolkit.Maui;
using Camera.MAUI;
using CommunityToolkit.Maui;
using LivreLand.View;
using LivreLand.View.ContentViews;
using LivreLand.ViewModel;
@ -17,6 +18,7 @@ public static class MauiProgram
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.UseMauiCameraView()
.ConfigureFonts(fonts =>
{
fonts.AddFont("SF-Compact-Display-Black.otf", "SFCompactDisplayBlack");
@ -35,6 +37,7 @@ public static class MauiProgram
.AddSingleton<FavorisView>()
.AddSingleton<ContactsView>()
.AddSingleton<PopupISBNView>()
.AddSingleton<ScanView>()
.AddSingleton<NavigatorVM>()
@ -54,7 +57,8 @@ public static class MauiProgram
.AddSingleton<EmpruntsPretsVM>()
.AddSingleton<FavorisVM>()
.AddSingleton<ContactsVM>()
.AddSingleton<PopupISBNVM>();
.AddSingleton<PopupISBNVM>()
.AddSingleton<ScanVM>();
#if DEBUG
builder.Logging.AddDebug();

@ -3,4 +3,5 @@
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
</manifest>

@ -28,5 +28,7 @@
</array>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/appicon.appiconset</string>
<key>NSCameraUsageDescription</key>
<string>This app uses camera for scanning barcodes</string>
</dict>
</plist>

@ -25,13 +25,15 @@
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="10"/>
<RowDefinition Height="*"/>
<RowDefinition Height="10"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<Grid BackgroundColor="{AppThemeBinding Light={StaticResource HeaderGray}, Dark={StaticResource Gray900}}"
Grid.Row="0">
Grid.Row="0"
Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="auto"/>
@ -44,9 +46,10 @@
Grid.Column="1"/>
</Grid>
<Grid HorizontalOptions="Center"
Grid.Row="2">
Grid.Row="1"
Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
@ -62,8 +65,14 @@
Text="{Binding ContactsVM.Manager.GivenLastName}"/>
<Button Text="Enregistrer"
BackgroundColor="Transparent"
BorderColor="Black"
Command="{Binding ContactsVM.Manager.AddContactCommand}"/>
Command="{Binding ContactsVM.Manager.AddContactCommand}">
<Button.BorderColor>
<AppThemeBinding Light="{StaticResource Black}" Dark="{StaticResource White}" />
</Button.BorderColor>
<Button.TextColor>
<AppThemeBinding Light="{StaticResource Black}" Dark="{StaticResource White}" />
</Button.TextColor>
</Button>
</VerticalStackLayout>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ContactsVM.AddContactDataFormCommand}"/>
@ -74,7 +83,8 @@
SelectionChangedCommand="{Binding ContactsVM.BookLendedCommand}"
SelectionChangedCommandParameter="{Binding ContactsVM.Manager.SelectedContact}"
SelectionMode="Single"
Grid.Row="4">
Grid.Row="1"
Grid.Column="1">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="viewModel:ContactVM">
<VerticalStackLayout>

@ -26,10 +26,6 @@
Margin="10,5,10,5">
<Label Text="Ajouter un livre"
Style="{StaticResource MasterStateBookText}"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Navigator.NavigationCommand}"
CommandParameter="/add"/>
</Grid.GestureRecognizers>
</Grid>
<contentView:SeparatorEntireView Grid.Row="1"/>
@ -52,6 +48,10 @@
<toolkit:IconTintColorBehavior TintColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
</Image.Behaviors>
</Image>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Navigator.NavigationCommand}"
CommandParameter="/scan"/>
</Grid.GestureRecognizers>
</Grid>
<contentView:SeparatorEntireView Grid.Row="3"/>

@ -102,6 +102,9 @@
<toolkit:IconTintColorBehavior TintColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
</Image.Behaviors>
</Image>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding DetailsLivreVM.OpenInfoCommand}"/>
</Grid.GestureRecognizers>
</Grid>
<contentView:SeparatorCutStartView/>
@ -178,7 +181,7 @@
<Label Text="Résumé"
Style="{StaticResource DetailsLivreTitle}"
Grid.Row="0"/>
<Label Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Et malesuada fames ac turpis egestas integer eget aliquet. Nunc sed id semper risus. Nisl purus in mollis nunc sed id semper risus. Egestas congue quisque egestas diam in arcu cursus euismod. Elementum integer enim neque volutpat ac tincidunt vitae. Amet luctus venenatis lectus magna fringilla urna porttitor rhoncus dolor. Sollicitudin tempor id eu nisl nunc. Eget mauris pharetra et ultrices neque. In vitae turpis massa sed elementum tempus. Posuere ac ut consequat semper viverra nam. Quisque non tellus orci ac auctor augue mauris augue. Cursus in hac habitasse platea dictumst. Pellentesque diam volutpat commodo sed egestas egestas fringilla phasellus faucibus. Vel fringilla est ullamcorper eget nulla facilisi etiam."
<Label Text="{Binding DetailsLivreVM.Book.WorkDescription}"
Style="{StaticResource DetailsLivreBody}"
Grid.Row="1"/>
</Grid>
@ -300,7 +303,21 @@
<contentView:SeparatorCutStartView/>
</VerticalStackLayout>
<Picker ItemsSource="{Binding DetailsLivreVM.Manager.AllStatus}"
SelectedItem="{Binding DetailsLivreVM.Manager.SelectedStatus}"
IsVisible="{Binding DetailsLivreVM.IsPickerVisible}"
HorizontalOptions="Center"
VerticalOptions="Center"
Grid.Row="1">
<Picker.Behaviors>
<toolkit:EventToCommandBehavior EventName="SelectedIndexChanged"
Command="{Binding DetailsLivreVM.Manager.UpdateStatusBookCommand}"
CommandParameter="{Binding DetailsLivreVM.Book}"/>
</Picker.Behaviors>
</Picker>
<!--Buttons-->
<VerticalStackLayout Grid.Row="2">
@ -344,18 +361,6 @@
</VerticalStackLayout>
<Picker ItemsSource="{Binding DetailsLivreVM.Manager.AllStatus}"
SelectedItem="{Binding DetailsLivreVM.Manager.SelectedStatus}"
IsVisible="{Binding DetailsLivreVM.IsPickerVisible}"
Grid.RowSpan="3"
HorizontalOptions="Center"
VerticalOptions="Center">
<Picker.Behaviors>
<toolkit:EventToCommandBehavior Command="{Binding DetailsLivreVM.Manager.UpdateStatusBookCommand}"
CommandParameter="{Binding DetailsLivreVM.Book}"/>
</Picker.Behaviors>
</Picker>
</Grid>
</ScrollView>
</Grid>

@ -36,7 +36,7 @@
SelectedItem="{Binding EmpruntsPretsVM.Manager.SelectedLoan}"
SelectionMode="Single"
SelectionChangedCommand="{Binding EmpruntsPretsVM.OnSelectionLoanChangedCommand}"
SelectionChangedCommandParameter="{Binding EmpruntsPretsVM.Manager.SelectedBook}"
SelectionChangedCommandParameter="{Binding EmpruntsPretsVM.Manager.SelectedLoan}"
IsVisible="{Binding EmpruntsPretsVM.PretCollectionIsVisible}"
IsGrouped="True">
<CollectionView.GroupHeaderTemplate>

@ -42,8 +42,10 @@
Grid.Column="1"/>
</Grid>
<CollectionView ItemsSource="{Binding FavorisVM.Manager.AllFavoriteBooks}"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged"
SelectedItem="{Binding FavorisVM.Manager.SelectedBook}"
SelectionMode="Single"
SelectionChangedCommand="{Binding FavorisVM.OnSelectionChangedCommand}"
SelectionChangedCommandParameter="{Binding FavorisVM.Manager.SelectedBook}"
Grid.Row="2">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="viewModel:BookVM">

@ -24,10 +24,5 @@ public partial class FavorisView : ContentPage
#region Methods
void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
//App.Current.MainPage.Navigation.PushAsync(new DetailsLivreView());
}
#endregion
}

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:cv="clr-namespace:Camera.MAUI;assembly=Camera.MAUI"
x:Class="LivreLand.View.ScanView"
Title="ScanView">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<cv:CameraView x:Name="cameraView"
BarCodeDetectionEnabled="True"
Camera="{Binding ScanVM.Camera}"
Cameras="{Binding ScanVM.Cameras}"
NumCamerasDetected="{Binding ScanVM.CamerasCount}"
BarCodeResults="{Binding ScanVM.BarCodeResult}"
WidthRequest="300"
HeightRequest="200">
<cv:CameraView.Behaviors>
<toolkit:EventToCommandBehavior EventName="BarcodeDetected"
Command="{Binding ScanVM.BarcodeDetectCommand}"/>
<toolkit:EventToCommandBehavior EventName="CamerasLoaded"
Command="{Binding ScanVM.CamerasLoadCommand}"/>
</cv:CameraView.Behaviors>
</cv:CameraView>
</Grid>
<Grid Margin="2"
Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Label Text="Annuler"
TextColor="{StaticResource Blue100Accent}"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ScanVM.Navigator.PopupBackButtonNavigationCommand}"/>
</Grid.GestureRecognizers>
</Grid>
<Grid IsVisible="{Binding ScanVM.AddIsVisible}"
Grid.Column="2">
<Label Text="Ajouter le livre !"
TextColor="{StaticResource Blue100Accent}"/>
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ScanVM.AddBookISBNDetectedCommand}"
CommandParameter="{Binding ScanVM.BarcodeText}"/>
</Grid.GestureRecognizers>
</Grid>
<Label Text="{Binding ScanVM.BarcodeText}"
TextColor="{StaticResource Blue100Accent}"
Grid.Column="4"/>
</Grid>
</Grid>
</ContentPage>

@ -0,0 +1,24 @@
using LivreLand.ViewModel;
namespace LivreLand.View;
public partial class ScanView : ContentPage
{
#region Properties
public ScanVM ScanVM { get; set; }
#endregion
#region Constructor
public ScanView(ScanVM scanVM)
{
ScanVM = scanVM;
InitializeComponent();
ScanVM.CameraView = cameraView; //ToRemove
BindingContext = this;
}
#endregion
}

@ -155,7 +155,11 @@
<ImageButton Source="chevron_right.png"
Rotation="180"
Command="{Binding TousVM.Manager.PreviousCommand}"
Grid.Column="1"/>
Grid.Column="1">
<ImageButton.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
</ImageButton.Behaviors>
</ImageButton>
<Label Style="{StaticResource MasterTitleBookText}"
VerticalOptions="FillAndExpand"
Grid.Column="3">
@ -169,7 +173,11 @@
</Label>
<ImageButton Source="chevron_right.png"
Command="{Binding TousVM.Manager.NextCommand}"
Grid.Column="5"/>
Grid.Column="5">
<ImageButton.Behaviors>
<toolkit:IconTintColorBehavior TintColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
</ImageButton.Behaviors>
</ImageButton>
</Grid>
</Grid>
</ScrollView>

@ -77,6 +77,8 @@ namespace LivreLand.ViewModel
public ICommand RemoveBookCommand { get; private set; }
public ICommand OpenInfoCommand { get; private set; }
#endregion
#region Constructor
@ -92,6 +94,7 @@ namespace LivreLand.ViewModel
AddBookToReadListCommand = new RelayCommand<BookVM>((bookVM) => AddBookToReadList(bookVM));
LoanBookCommand = new RelayCommand<BookVM>((bookVM) => LoanBook(bookVM));
RemoveBookCommand = new RelayCommand<BookVM>((bookVM) => RemoveBook(bookVM));
OpenInfoCommand = new RelayCommand(() => OpenInfo());
}
#endregion
@ -106,7 +109,7 @@ namespace LivreLand.ViewModel
private void ShowPicker()
{
Manager.GetAllStatusCommand.Execute(null);
Manager.SelectedStatus = this.Book.Status;
Manager.SelectedStatus = Book.Status;
IsPickerVisible = true;
}
@ -167,6 +170,13 @@ namespace LivreLand.ViewModel
Navigator.PopupBackButtonNavigationCommand.Execute(null);
}
private async Task OpenInfo()
{
var isbn = Manager.SelectedBook.ISBN13;
string url = "https://openlibrary.org/isbn/" + isbn;
await Launcher.OpenAsync(url);
}
#endregion
}
}

@ -136,7 +136,7 @@ namespace LivreLand.ViewModel
private void OnSelectionLoanChanged(LoanVM loanVM)
{
if (loanVM == null)
if (loanVM != null)
{
foreach (var b in Manager.AllCurrentLoans)
{

@ -1,9 +1,11 @@
using PersonalMVVMToolkit;
using LivreLand.View;
using PersonalMVVMToolkit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using ViewModels;
namespace LivreLand.ViewModel
@ -16,6 +18,8 @@ namespace LivreLand.ViewModel
public ManagerVM Manager { get; private set; }
public ICommand OnSelectionChangedCommand { get; private set; }
#endregion
#region Constructor
@ -24,6 +28,20 @@ namespace LivreLand.ViewModel
{
Navigator = navigatorVM;
Manager = managerVM;
OnSelectionChangedCommand = new RelayCommand<BookVM>((bookVM) => OnSelectionChanged(bookVM));
}
#endregion
#region Methods
private void OnSelectionChanged(BookVM bookVM)
{
if (bookVM != null)
{
var result = new DetailsLivreVM(Manager, Navigator, bookVM);
App.Current.MainPage.Navigation.PushAsync(new DetailsLivreView(result));
}
}
#endregion

@ -0,0 +1,183 @@
using Camera.MAUI;
using CommunityToolkit.Maui.Alerts;
using PersonalMVVMToolkit;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using ViewModels;
using ZXing;
namespace LivreLand.ViewModel
{
public class ScanVM : BaseViewModel
{
#region Fields
private CameraView cameraView;
private CameraInfo camera = null;
private ObservableCollection<CameraInfo> cameras = new();
private Result[] result;
private string barcodeText = "ISBN";
private bool addIsVisible = false;
#endregion
#region Properties
public CameraView CameraView
{
get => cameraView;
set
{
if (cameraView != value)
{
cameraView = value;
OnPropertyChanged(nameof(CameraView));
}
}
}
public CameraInfo Camera
{
get => camera;
set
{
if (camera != value)
{
camera = value;
OnPropertyChanged(nameof(Camera));
}
}
}
public ObservableCollection<CameraInfo> Cameras
{
get => cameras;
set
{
if (cameras != value)
{
cameras = value;
OnPropertyChanged(nameof(Cameras));
}
}
}
public int CamerasCount
{
get => Cameras.Count();
set
{
if (value > 0)
{
Camera = Cameras.First();
}
}
}
public Result[] BarCodeResult
{
get => result;
set
{
if (result != value)
{
result = value;
OnPropertyChanged(nameof(BarCodeResult));
}
}
}
public string BarcodeText
{
get => barcodeText;
set
{
if (barcodeText != value)
{
barcodeText = value;
OnPropertyChanged(nameof(BarcodeText));
}
}
}
public bool AddIsVisible
{
get => addIsVisible;
set
{
if (addIsVisible != value)
{
addIsVisible = value;
OnPropertyChanged(nameof(AddIsVisible));
}
}
}
public NavigatorVM Navigator { get; private set; }
public ManagerVM Manager { get; private set; }
public ICommand CamerasLoadCommand { get; private set; }
public ICommand BarcodeDetectCommand { get; private set; }
public ICommand AddBookISBNDetectedCommand { get; private set; }
#endregion
#region Constructor
public ScanVM(NavigatorVM navigatorVM, ManagerVM managerVM)
{
Navigator = navigatorVM;
Manager = managerVM;
CamerasLoadCommand = new RelayCommand(() => CamerasLoad());
BarcodeDetectCommand = new RelayCommand(() => BarcodeDetect());
AddBookISBNDetectedCommand = new RelayCommand<string>((isbn) => AddBookISBNDetected(isbn));
}
#endregion
#region Methods
private async Task CamerasLoad()
{
if (Cameras.Count > 0)
{
Camera = Cameras.First();
MainThread.BeginInvokeOnMainThread(async () =>
{
await CameraView.StopCameraAsync();
await CameraView.StartCameraAsync();
});
}
}
private async Task BarcodeDetect()
{
MainThread.BeginInvokeOnMainThread(() =>
{
BarcodeText = BarCodeResult[0].Text;
AddIsVisible = true;
});
}
private async Task AddBookISBNDetected(string isbn)
{
Manager.AddBookCommand.Execute(isbn);
var toast = Toast.Make("Livre ajouté !", CommunityToolkit.Maui.Core.ToastDuration.Short);
await toast.Show();
Manager.GetBooksFromCollectionCommand.Execute(null);
Navigator.NavigationCommand.Execute("/tous");
}
#endregion
}
}

@ -4,32 +4,37 @@
*******
Sommaire
### Sommaire
1. [Accessibilité](#acces)
2. [Progression](#progression)
3. [Présentation du projet](#presentation)
4. [Contenu](#contenu)
5. [Auteurs](#auteurs)
4. [Architecture](#architecture)
5. [Contenu](#contenu)
6. [Auteurs](#auteurs)
*******
<div id='acces'/>
Pour accéder au code de l'application, vous pouvez cloner la branche `master` du dépôt Code#0 et ouvrir celle-ci dans `Microsoft Visual Studio` par exemple.
Pour accéder au code de l'application, vous pouvez cloner la branche `master` du dépôt Code#0 et ouvrir celle-ci dans `Microsoft Visual Studio` par exemple.
Disponible sur :
![](https://img.shields.io/badge/Android-3DDC84?style=for-the-badge&logo=android&logoColor=white)
![](https://img.shields.io/badge/iOS-000000?style=for-the-badge&logo=ios&logoColor=white)
> **Warning**: L'application est fonctionnelle sous Windows et Android mais n'a pas été testée sous IOS.
> **Warning**: Le déploiement n'a pas encore été fait.
- Dans la branche **Master**, vous retrouverez l'intégralité de l'application avec les fonctionnalités du `TP2` ainsi que le scan de code-barres.
Disponible sur :
![](https://img.shields.io/badge/Android-3DDC84?style=for-the-badge&logo=android&logoColor=white)
![](https://img.shields.io/badge/iOS-000000?style=for-the-badge&logo=ios&logoColor=white)
- Dans la branche **TP3**, vous retrouverez l'implémentation du `MVVM Community Toolkit` dans notre application.
*******
<div id='progression'/>
🚧 __EN PROGRESSION__
## 🚧 __EN PROGRESSION__
Étape 1 : Développement des vues en XAML
### Étape 1 : Développement des vues en XAML
- Intégralité des pages proposées sur la page d'accueil développées
- Mode clair & Mode sombre disponibles (pas très esthétique)
- Utilisable en mode portrait ou mode paysage
@ -38,11 +43,163 @@ Disponible sur :
- View Model non utilisés et fonctionnalités pas toutes mises en place (seulement les vues)
- Navigation généralement utilisable mais pas parfaitement codée
---
### Étape 2 : Personnal MVVM Toolkit
La création de ce `Toolkit Personnel` a pour but de faciliter le développement de l'application en fournissant un ensemble de fonctionnalités et de composants réutilisables. De plus, à l'aide d'une classe comme `RelayCommand`, notre objectif est de ne pas inclure dans nos ViewModels une dépendance avec les Commands de .NET MAUI.
Nous pouvons représenter la structure de notre toolkit avec le diagramme suivant :
```mermaid
classDiagram
direction LR
class INotifyPropertyChanged {
<<interface>>
}
class ObservableObject{
+PropertyChanged: event PropertyChangedEventHandler?;
#OnPropertyChanged (string PropertyName = null) : void
#SetProperty<T> (T member, T value, Action<T> action, string propertyName = null) : void
#SetProperty<T> (ref T member, T value, string propertyName = null) : void
}
class BaseViewModel{
+Model: TModel;
-model: TModel;
+BaseViewModel(TModel model)
+BaseViewModel() : this(default)
}
class ICommand{
<<interface>>
}
class RelayCommand{
+CanExecuteChanged: event EventHandler?;
+CanExecute (object? parameter) : bool
+Execute (object? parameter) : void
+RefreshCommand() : void
}
ObservableObject ..|> INotifyPropertyChanged
BaseViewModel --|> ObservableObject
RelayCommand ..|> ICommand
```
Cette structure est une version remplaçant pour le moment le `Community Toolkit` mis en place par Microsoft qui permet aussi de supprimer beaucoup de code inutile en remplaçant celui-ci par des annotations et des classes partielles.
---
### Étape 3 : MVVM
Nous utilisons au sein de notre projet le **patron d'architecture MVVM** avec les ViewModels Wrapping et Applicatives.
Nous retrouvons donc les 3 grandes parties du patron :
- **Model** :
Le `Model` représente la `logique métier`. Il est écrit en `C#` et est adpaté pour diifférentes applications.
- **View** :
Les `Vues` sont écrites en `XAML` et représentent l'interface utilisateur avec les vues de l'application. Le `Data Binding` est utilisé entre les propriétés du XAML et celles des ViewModels. Enfin, des évènements sont déclenchés à partir de certains composants des vues.
- **ViewModel** :
Les `ViewModels` sont écrits en `C#` et sont divisables en deux grandes catégories :
* Les **Wrapping ViewModel** encapsulent les données du modèle et exposent des propriétés et des commandes nécessaires à la vue pour interagir avec le modèle.
* Les **Applicative ViewModel** peuvent inclure une logique métier spécifique et des propriétés calculées, elles peuvent également exposer des commandes pour effectuer des actions spécifiques liées à la vue.
Le schéma suivant montre bien les relations entre les grandes parties du `patron MVVM` :
![Schema_MVVM](documentation/schema_mvvm.png)
Le **diagramme de classes** pouvant être extrèmement grand à cause des multiples classes au sein de notre projet, j'ai décidé de représenter une partie de celui-ci qui pourrait se répéter pour toutes les autres parties. L'objectif principal étant de comprendre comment fonctionne le **modèle MVVM** et comment les classes intéragissent entre elles, j'ai choisi de faire mon exemple avec la partie des livres qui est la plus générale du sujet.
```mermaid
classDiagram
class Book {
Id: string
Title: string
Publishers: List<string>
PublishDate: DateTime
ISBN13: string
Series: List<string>
NbPages: int
Format: string
Language: Languages
Contributors: List<Contributor>
ImageSmall: string
ImageMedium: string
ImageLarge: string
Works: List<Work>
Authors: List<Author>
Status: Status
UserTags: List<string>
UserRating: float?
UserNote: string
Equals(other: Book): bool
GetHashCode(): int
}
class DetailsLivreView {
DetailsLivreVM: DetailsLivreVM
}
class DetailsLivreVM {
isPickerVisible: bool
addFavorisButtonText: string
Manager: ManagerVM
Navigator: NavigatorVM
Book: BookVM
IsPickerVisible: bool
AddFavorisButtonText: string
BackButtonCommand: ICommand
ShowPickerCommand: ICommand
AddRemoveBookToFavoritesCommand: ICommand
AddBookToReadListCommand: ICommand
LoanBookCommand: ICommand
RemoveBookCommand: ICommand
OpenInfoCommand: ICommand
BackButton()
ShowPicker()
AddRemoveBookToFavorites(bookVM: BookVM)
AddBookToReadList(bookVM: BookVM)
LoanBook(bookVM: BookVM)
RemoveBook(bookVM: BookVM)
OpenInfo()
}
class BookVM {
Id: string
ISBN13: string
Title: string
Publishers: List<string>
PublishDate: DateTime
Works: List<WorkVM>
WorkDescription: string
Authors: List<AuthorVM>
Author: string
Status: Status
NbPages: int
Language: Languages
ImageSmall: string
UserRating: float?
}
DetailsLivreView --> DetailsLivreVM
DetailsLivreVM --> BookVM
DetailsLivreVM --> ManagerVM
DetailsLivreVM --> NavigatorVM
BookVM --> Book
BookVM --> WorkVM
BookVM --> AuthorVM
```
*******
<div id='presentation'/>
### **Présentation**
## **Présentation**
LivreLand : votre bibliothèque connectée !
Retrouver tous vos livres préférés en un clic.
@ -53,17 +210,200 @@ Retrouver tous vos livres préférés en un clic.
## Fonctionnalités
- Livres triés par auteur, date, notes, statut de lecture...
- Livres à lire plus tard
- Livres prêtés
- Livres favoris
**TP2 - Base** :
- [x] Page d'accueil
- [x] Affichage des livres de l'utilisateur : afficher tous les livres de l'utilisateur dans la vue BooksPage et permettre la sélection d'un livre et la navigation vers la page BookPage
* seule la note n'est pas encore affichée sous la forme d'étoiles (commencée dans `star-notation-22-10`)
- [x] Filtrage par auteur et par date de publication : afficher dans la vue de filtrage (FilterPage)
**TP2 - Ajouts** :
- [x] Changer le statut de lecture d'un livre
- [x] Ajouter un livre aux favoris
- [x] Filtrer les livres par Auteur, Date de publication, Note
* le filtrage fonctionne, au deuxième clique sur une date par exemple une fois une première date visitée, je remarque des soucis avec de temps à autre une exception
- [x] Ajouter un livre à sa collection en saisissant l'ISBN
- [x] Supprimer un livre
- [x] Prêter un livre (et ajouter un contact si besoin)
* la page avec les contacts n'est pas esthétiquement très réussie
- [x] Consulter la liste des livres prêtés
* j'ai fait le choix de n'afficher que les livres _actuellement_ prêtés ou empruntés
_Erreurs rencontrées_ :
* L'ajout en favoris fonctionne, cependant il m'ait arrivé lorque je choisissais à partir de la page BooksPage d'ajouter un livre qui ne se trouve pas sur la première page, que celui-ci supprime souvent tous les livres déjà en favoris.
* J'ai également eu à certains moments des problèmes avec l'accession à la page de détails et une double page qui s'ouvrait; dans ce cas je relance généralement l'application.
**TP3** :
- [X] Modifier l'intégralité du code pour que l'application utilise désormais le MVVM Community Toolkit à la place du toolkit personnel
* lecture de la documentation et implémentation dans la branche `TP3` (à affiner pour respecter parfaitement le Toolkit)
**TP 4** :
Ajouter les vues et les VM nécessaires pour permettre :
- [x] Le scan de code-barres afin d'ajouter de nouveaux livres
* le scan de code-barres fonctionne mais le livre n'est pas encore directement ajouté dans la liste (affichage de l'isbn dans en bas de page & du bouton d'ajout du livre)
- [X] La recherche en ligne (via le web service)
* il est possible d'accéder à la page d'un livre en ligne en cliquant dans la partie "Infos en ligne" de la page Détails
*******
<div id='architecture'/>
## Architectures du modèle et des services fournises
Dans cette partie, vous retrouverez dans un premier temps deux diagrammes mis à disposition dans le sujet représentant d'abord le `Modèle` puis les `Services et Interfaces` :
### Modèle
```mermaid
classDiagram
direction LR
class Book {
+Id : string
+Title : string
+Publishers : List~string~
+PublishDate : DateTime
+ISBN13 : string
+Series : List~string~
+NbPages : int
+Format : string
+ImageSmall : string
+ImageMedium : string
+ImageLarge : string
}
class Languages {
<<enum>>
Unknown,
French,
}
class Work {
+Id : string
+Description : string
+Title : string
+Subjects : List~string~
}
class Contributor {
+Name : string
+Role : string
}
class Author {
+Id : string
+Name : string
+ImageSmall : string
+ImageMedium : string
+ImageLarge : string
+Bio : string
+AlternateNames : List~string~
+BirthDate : DateTime?
+DeathDate : DateTime?
}
class Link {
+Title : string
+Url : string
}
class Ratings {
+Average : float
+Count : int
}
Book --> "1" Languages : Language
Book --> "*" Contributor : Contributors
Author --> "*" Link : Links
Work --> "*" Author : Authors
Work --> "1" Ratings : Ratings
Book --> "*" Author : Authors
Book --> "*" Work : Works
class Status {
<<enum>>
Unknown,
Finished,
Reading,
NotRead,
ToBeRead
}
class Contact{
+string Id;
+string FirstName;
+string LastName;
}
```
---
### Services et Interfaces
```mermaid
classDiagram
direction LR
Book --> "1" Languages : Language
Book --> "*" Contributor : Contributors
Author --> "*" Link : Links
Work --> "1" Ratings : Ratings
Work --> "*" Author : Authors
Book --> "*" Work : Works
Book --> "*" Author : Authors
class IUserLibraryManager {
<<interface>>
+AddBook(Book book) : Task<Book>
+AddBook(string id) : Task<Book>
+AddBookByIsbn(string isbn) : Task<Book>
+RemoveBook(Book book) : Task<bool>
+RemoveBook(string id) : Task<bool>
+RemoveBook(string id) : Task<bool>
+AddToFavorites(Book book) : Task<bool>
+AddToFavorites(string bookId) : Task<bool>
+RemoveFromFavorites(Book book) : Task<bool>
+RemoveFromFavorites(string bookId) : Task<bool>
+UpdateBook(Book updatedBook) : Task<Book>
+AddContact(Contact contact) : Task<Contact>
+RemoveContact(Contact contact) : Task<bool>
+LendBook(Book book, Contact contact, DateTime? loanDate) : Task<bool>
+GetBackBook(Book book, DateTime? returnedDate) : Task<bool>
+BorrowBook(Book book, Contact owner, DateTime? borrowedDate) : Task<bool>
+GiveBackBook(Book book, DateTime? returnedDate) : Task<bool>
+GetCurrentLoans(int index, int count)
+GetPastLoans(int index, int count)
+GetCurrentBorrowings(int index, int count)
+GetPastBorrowings(int index, int count)
+GetContacts(int index, int count)
}
class ILibraryManager {
<<interface>>
+GetBookById(string id)
+GetBookByIsbn(string isbn)
+GetBooksByTitle(string title, int index, int count, string sort)
+GetBooksByAuthorId(string authorId, int index, int count, string sort)
+GetBooksByAuthor(string author, int index, int count, string sort)
+GetAuthorById(string id)
+GetAuthorsByName(string substring, int index, int count, string sort)
}
class Status {
<<enum>>
}
IUserLibraryManager ..|> ILibraryManager
IUserLibraryManager ..> Status
IUserLibraryManager ..> Contact
IUserLibraryManager ..> Book
ILibraryManager ..> Book
ILibraryManager ..> Author
```
*******
## Ressources
- Temps
- 4 Septembre au
- 4 Septembre au 22 Octobre 2023
- Matériel
- Ordinateurs portables sous Windows
- Émulateur sous Visual Studio 2022

@ -42,6 +42,13 @@ namespace ViewModels
get => Model.PublishDate;
}
public List<WorkVM> Works
{
get => Model.Works.Select(w => new WorkVM(w)).ToList();
}
public string WorkDescription => Model.Works.Count > 0 ? Model.Works.First().Description : " ";
public List<AuthorVM> Authors
{
get => Model.Authors.Select(a => new AuthorVM(a)).ToList();
@ -52,6 +59,7 @@ namespace ViewModels
public Status Status
{
get => Model.Status;
set => SetProperty(Model.Status, value, status => Model.Status = status);
}
public int NbPages

@ -472,6 +472,7 @@ namespace ViewModels
{
await Model.AddBookToCollection(result.Id);
}
GetBooksFromCollectionCommand.Execute(null);
}
private async Task UpdateBook(BookVM bookVM)
@ -485,6 +486,9 @@ namespace ViewModels
var book = await Model.GetBookById(bookVM.Id);
book.Status = SelectedStatus;
await Model.UpdateBook(book);
var updatedBook = new BookVM(book);
SelectedBook.Status = updatedBook.Status;
OnPropertyChanged(nameof(SelectedBook));
}
private async Task UpdateToBeReadBook(BookVM bookVM)

@ -0,0 +1,42 @@
using Model;
using PersonalMVVMToolkit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ViewModels
{
public class WorkVM : BaseViewModel<Work>
{
#region Fields
#endregion
#region Properties
public string Id
{
get => Model.Id;
set => SetProperty(Model.Id, value, v => Model.Id = value);
}
public string Description
{
get => Model.Description;
set => SetProperty(Model.Description, value, v => Model.Description = value);
}
#endregion
#region Constructor
public WorkVM(Work w) : base(w)
{
}
#endregion
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Loading…
Cancel
Save