Skip to content

Commit e419e7f

Browse files
committed
Add save and load logic.
1 parent 4e76d69 commit e419e7f

23 files changed

+1090
-804
lines changed

samples/Unity.Mvvm.ToDoList/Assets/Scripts/App.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
1-
using UnityEngine;
1+
using Interfaces;
2+
using UnityEngine;
23

34
public class App : MonoBehaviour
45
{
56
[SerializeField] private AppContext _appContext;
67

8+
private IDataStoreService _dataStoreService;
9+
710
private void Awake()
811
{
912
_appContext.Construct();
13+
_dataStoreService = _appContext.Resolve<IDataStoreService>();
1014
}
1115

1216
private void Start()
1317
{
1418
Application.targetFrameRate = 300;
1519
}
1620

21+
private void OnEnable()
22+
{
23+
_dataStoreService.Enable();
24+
}
25+
26+
private void OnDisable()
27+
{
28+
_dataStoreService.Disable();
29+
}
30+
1731
private void OnDestroy()
1832
{
1933
_appContext.Dispose();

samples/Unity.Mvvm.ToDoList/Assets/Scripts/AppContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ public void Construct()
2929

3030
RegisterInstance(new TaskBroker());
3131
RegisterInstance<IDialogsService>(new DialogsService(this));
32-
32+
3333
RegisterInstance(new MainViewModel(this));
3434
RegisterInstance(new AddTaskDialogViewModel(this));
3535

36+
RegisterInstance<IDataStoreService>(new DataStoreService(this));
3637
RegisterInstance<IBindableElementsWrapper>(new ToDoListBindableElementsWrapper(_taskItemAsset));
3738
RegisterInstance(GetValueConverters());
3839
}

samples/Unity.Mvvm.ToDoList/Assets/Scripts/BindableUIElementWrappers/BindableAddTaskButtonWrapper.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,26 @@
22
using BindableUIElements;
33
using UnityMvvmToolkit.Core;
44
using UnityMvvmToolkit.Core.Interfaces;
5+
using UnityMvvmToolkit.UniTask.Interfaces;
56

67
namespace BindableUIElementWrappers
78
{
89
public class BindableAddTaskButtonWrapper : BindablePropertyElement, IInitializable, IDisposable
910
{
1011
private readonly BindableAddTaskButton _addTaskButton;
11-
private readonly ICommand _command;
12+
private readonly IAsyncCommand _asyncCommand;
1213
private readonly IReadOnlyProperty<bool> _isCancelStateProperty;
1314

1415
public BindableAddTaskButtonWrapper(BindableAddTaskButton addTaskButton, IObjectProvider objectProvider)
1516
: base(objectProvider)
1617
{
1718
_addTaskButton = addTaskButton;
1819

19-
_command = GetCommand<ICommand>(_addTaskButton.Command);
20+
_asyncCommand = GetCommand<IAsyncCommand>(_addTaskButton.Command);
2021
_isCancelStateProperty = GetReadOnlyProperty<bool>(_addTaskButton.BindingIsCancelStatePath);
2122
}
2223

23-
public bool CanInitialize => _command != null && _isCancelStateProperty != null;
24+
public bool CanInitialize => _asyncCommand != null && _isCancelStateProperty != null;
2425

2526
public void Initialize()
2627
{
@@ -39,7 +40,7 @@ public void Dispose()
3940

4041
private void OnButtonClicked()
4142
{
42-
_command.Execute();
43+
_asyncCommand.Execute();
4344
}
4445
}
4546
}

samples/Unity.Mvvm.ToDoList/Assets/Scripts/BindableUIElements/BindablePageBlocker.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using UnityEngine;
1+
using Cysharp.Threading.Tasks;
2+
using Extensions;
3+
using UnityEngine;
24
using UnityEngine.UIElements;
35
using UnityMvvmToolkit.UI.BindableUIElements;
46

@@ -14,19 +16,25 @@ public BindablePageBlocker()
1416
}
1517
}
1618

17-
public void Activate()
19+
public async UniTask ActivateAsync()
1820
{
1921
visible = true;
22+
style.opacity = 1;
23+
await this.WaitForTransitionFinish();
2024
}
2125

22-
public void Deactivate()
26+
public async UniTask DeactivateAsync()
2327
{
28+
style.opacity = 0;
29+
await this.WaitForTransitionFinish();
30+
2431
visible = false;
2532
}
2633

2734
private void OnLayoutCalculated(GeometryChangedEvent e)
2835
{
29-
Deactivate();
36+
visible = false;
37+
style.opacity = 0;
3038
UnregisterCallback<GeometryChangedEvent>(OnLayoutCalculated);
3139
}
3240

samples/Unity.Mvvm.ToDoList/Assets/Scripts/Extensions/VisualElementExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Extensions
88
public static class VisualElementExtensions
99
{
1010
public static async UniTask WaitForTransitionFinish(this VisualElement element, int timeoutMs = 1000,
11-
CancellationToken cancellationToken = default)
11+
CancellationToken cancellationToken = default) // TODO: All transitions or Any?
1212
{
1313
if (AnyTransitionHasDuration(element) == false)
1414
{
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Interfaces
2+
{
3+
public interface IDataStoreService
4+
{
5+
void Enable();
6+
void Disable();
7+
}
8+
}

samples/Unity.Mvvm.ToDoList/Assets/Scripts/Interfaces/IDataStoreService.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
namespace Interfaces
1+
using Cysharp.Threading.Tasks;
2+
3+
namespace Interfaces
24
{
35
public interface IDialogsService
46
{
57
bool IsAddTaskDialogActive { get; }
68

7-
void ShowAddTaskDialog();
8-
void HideAddTaskDialog();
9+
UniTask ShowAddTaskDialogAsync();
10+
UniTask HideAddTaskDialogAsync();
911
}
1012
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using System.Collections.Specialized;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Xml.Serialization;
5+
using Cysharp.Threading.Tasks;
6+
using Interfaces;
7+
using Unity.VisualScripting;
8+
using UnityEngine;
9+
using ViewModels;
10+
11+
namespace Services
12+
{
13+
public class DataStoreService : IDataStoreService
14+
{
15+
private const string DataFileName = "todolist.xml";
16+
17+
private readonly MainViewModel _mainViewModel;
18+
private readonly XmlSerializer _serializer;
19+
private readonly string _dataFilePath;
20+
21+
private AsyncLazy _saveDataTask;
22+
private AsyncLazy<TaskItemData[]> _loadDataTask;
23+
24+
public DataStoreService(IAppContext appContext)
25+
{
26+
_mainViewModel = appContext.Resolve<MainViewModel>();
27+
_dataFilePath = Path.Combine(Application.persistentDataPath, DataFileName);
28+
_serializer = new XmlSerializer(typeof(TaskItemData[]));
29+
}
30+
31+
public async void Enable()
32+
{
33+
try
34+
{
35+
_mainViewModel.TaskItems.AddRange(await LoadDataAsync());
36+
}
37+
finally
38+
{
39+
_mainViewModel.TaskItems.CollectionChanged += OnTaskItemsCollectionChanged;
40+
}
41+
}
42+
43+
public void Disable()
44+
{
45+
_mainViewModel.TaskItems.CollectionChanged -= OnTaskItemsCollectionChanged;
46+
}
47+
48+
private void OnTaskItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
49+
{
50+
if (e.Action is
51+
NotifyCollectionChangedAction.Add or
52+
NotifyCollectionChangedAction.Remove or
53+
NotifyCollectionChangedAction.Replace)
54+
{
55+
SaveDataAsync().Forget();
56+
}
57+
}
58+
59+
private async UniTask SaveDataAsync()
60+
{
61+
if (_saveDataTask?.Task.Status.IsCompleted() ?? true)
62+
{
63+
_saveDataTask = SaveDataAsync(_dataFilePath).ToAsyncLazy();
64+
}
65+
66+
await _saveDataTask;
67+
}
68+
69+
private async UniTask<TaskItemData[]> LoadDataAsync()
70+
{
71+
if (_loadDataTask?.Task.Status.IsCompleted() ?? true)
72+
{
73+
_loadDataTask = LoadDataAsync(_dataFilePath).ToAsyncLazy();
74+
}
75+
76+
return await _loadDataTask;
77+
}
78+
79+
private async UniTask SaveDataAsync(string filePath)
80+
{
81+
await using var writeStream = File.Create(filePath);
82+
_serializer.Serialize(writeStream, _mainViewModel.TaskItems.ToArray());
83+
}
84+
85+
private async UniTask<TaskItemData[]> LoadDataAsync(string filePath)
86+
{
87+
if (File.Exists(filePath) == false)
88+
{
89+
return GetDefaultDataSet();
90+
}
91+
92+
await using var readStream = File.OpenRead(filePath);
93+
return (TaskItemData[]) _serializer.Deserialize(readStream);
94+
}
95+
96+
private TaskItemData[] GetDefaultDataSet()
97+
{
98+
return new TaskItemData[]
99+
{
100+
new() { Name = "Add UnitTests" },
101+
new() { Name = "Create UGUI ListView" },
102+
new() { Name = "Write an article" },
103+
new() { Name = "Add UI Toolkit ListView" },
104+
new() { Name = "Fix Command bindings" }
105+
};
106+
}
107+
}
108+
}

samples/Unity.Mvvm.ToDoList/Assets/Scripts/Services/DataStoreService.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)