Line data Source code
1 : // Copyright 2023 The terCAD team. All rights reserved.
2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
3 :
4 : import 'package:app_finance/_classes/herald/app_design.dart';
5 : import 'package:app_finance/_classes/herald/app_locale.dart';
6 : import 'package:app_finance/_classes/herald/app_sync.dart';
7 : import 'package:app_finance/_classes/storage/app_data.dart';
8 : import 'package:app_finance/_configs/custom_color_scheme.dart';
9 : import 'package:app_finance/_configs/theme_helper.dart';
10 : import 'package:app_finance/_ext/build_context_ext.dart';
11 : import 'package:app_finance/design/form/simple_input.dart';
12 : import 'package:app_finance/design/wrapper/row_widget.dart';
13 : import 'package:app_finance/design/wrapper/table_widget.dart';
14 : import 'package:app_finance/design/wrapper/text_wrapper.dart';
15 : import 'package:app_finance/design/generic/loading_widget.dart';
16 : import 'package:flutter/foundation.dart';
17 : import 'package:flutter/material.dart';
18 : import 'package:provider/provider.dart';
19 :
20 : class SyncTab extends StatefulWidget {
21 1 : const SyncTab({
22 : super.key,
23 : });
24 :
25 0 : @override
26 0 : SyncTabState createState() => SyncTabState();
27 : }
28 :
29 : class SyncTabState extends State<SyncTab> {
30 : final _controller = TextEditingController();
31 : final _scroll = ScrollController();
32 : late AppSync sync;
33 : late AppData dataProvider;
34 : List<String> request = [];
35 : bool loading = false;
36 :
37 0 : @override
38 : void dispose() {
39 0 : _controller.dispose();
40 0 : sync.unfollow(runtimeType);
41 0 : super.dispose();
42 : }
43 :
44 0 : void ping(Uint8List id) {
45 0 : final uuid = String.fromCharCodes(id);
46 0 : if (sync.getPeers().where((e) => e.id == uuid).isEmpty) {
47 0 : setState(() => request.add(uuid));
48 : }
49 0 : ScaffoldMessenger.of(context).showSnackBar(
50 0 : SnackBar(content: Text(AppLocale.labels.pongStatus(uuid))),
51 : );
52 : }
53 :
54 0 : void synchronize() {
55 0 : setState(() => loading = true);
56 0 : dataProvider.getList(AppDataType.bills).forEach((o) => sync.send(o.toStream()));
57 0 : dataProvider.getList(AppDataType.invoice).forEach((o) => sync.send(o.toStream()));
58 0 : dataProvider.getList(AppDataType.budgets).forEach((o) => sync.send(o.toStream()));
59 0 : dataProvider.getList(AppDataType.accounts).forEach((o) => sync.send(o.toStream()));
60 0 : dataProvider.getList(AppDataType.currencies).forEach((o) => sync.send(o.toStream()));
61 0 : setState(() => loading = false);
62 0 : ScaffoldMessenger.of(context).showSnackBar(
63 0 : SnackBar(content: Text(AppLocale.labels.peerSent)),
64 : );
65 : }
66 :
67 0 : @override
68 : Widget build(BuildContext context) {
69 0 : final textTheme = context.textTheme;
70 0 : final indent = ThemeHelper.getIndent();
71 0 : return Consumer<AppSync>(builder: (context, appSync, _) {
72 0 : sync = appSync..followBinary(runtimeType, ping);
73 0 : dataProvider = Provider.of<AppData>(context, listen: false);
74 0 : final data = sync.getPeers();
75 0 : return LayoutBuilder(builder: (context, constraints) {
76 0 : double width = ThemeHelper.getWidth(context, 4, constraints);
77 0 : if (ThemeHelper.isNavRight(context, constraints)) {
78 0 : width -= ThemeHelper.barHeight;
79 : }
80 0 : return SingleChildScrollView(
81 0 : controller: _scroll,
82 0 : child: Padding(
83 0 : padding: EdgeInsets.all(indent),
84 0 : child: Column(
85 0 : mainAxisAlignment: AppDesign.getAlignment<MainAxisAlignment>(),
86 0 : children: [
87 0 : RowWidget(
88 : maxWidth: width,
89 : alignment: MainAxisAlignment.spaceBetween,
90 : indent: indent,
91 : chunk: const [null, 85],
92 0 : children: [
93 0 : [
94 0 : Text(
95 0 : AppLocale.labels.peerId,
96 0 : style: textTheme.bodyLarge,
97 : ),
98 0 : Container(
99 0 : padding: EdgeInsets.all(indent),
100 0 : color: context.colorScheme.fieldBackground,
101 0 : child: SelectableText(
102 0 : sync.getUuid() ?? AppLocale.labels.pearDisabled,
103 0 : style: textTheme.bodyLarge,
104 : ),
105 : ),
106 : ],
107 0 : [
108 0 : Text(
109 0 : sync.isActive() ? AppLocale.labels.peerOnline : AppLocale.labels.peerClosed,
110 0 : style: textTheme.bodyLarge,
111 : ),
112 0 : Align(
113 : alignment: Alignment.topRight,
114 0 : child: Switch(
115 0 : value: sync.isActive(),
116 0 : onChanged: (value) => setState(() => value ? sync.enable() : sync.disable()),
117 : ),
118 : ),
119 : ],
120 : ],
121 : ),
122 : ThemeHelper.hIndent2x,
123 0 : if (sync.isActive())
124 0 : loading
125 0 : ? Row(
126 : crossAxisAlignment: CrossAxisAlignment.center,
127 0 : children: [
128 0 : Text(AppLocale.labels.pearLoading),
129 0 : LoadingWidget(isLoading: loading, size: const Size(48, 48)),
130 : ],
131 : )
132 0 : : SizedBox(
133 : width: double.infinity,
134 0 : child: FloatingActionButton(
135 : heroTag: 'sync_tab_sync',
136 0 : onPressed: synchronize,
137 0 : tooltip: AppLocale.labels.peerSync,
138 0 : child: Text(AppLocale.labels.peerSync),
139 : ),
140 : ),
141 0 : ThemeHelper.hIndent4x,
142 0 : TableWidget(
143 : width: width,
144 0 : shadowColor: context.colorScheme.onSurface.withOpacity(0.1),
145 : chunk: const [80, null, 80, 90],
146 0 : data: [
147 0 : [
148 0 : Center(child: Text(AppLocale.labels.peerAction)),
149 0 : TextWrapper(AppLocale.labels.peerDevice),
150 0 : TextWrapper(AppLocale.labels.peerStatus),
151 0 : Center(child: Text(AppLocale.labels.peerAction)),
152 : ],
153 0 : ...List.generate(data.length, (index) {
154 0 : String status = switch (data[index].status) {
155 0 : true => AppLocale.labels.peerOnline,
156 0 : false => AppLocale.labels.peerClosed,
157 0 : _ => AppLocale.labels.peerOffline,
158 : };
159 0 : return <Widget>[
160 0 : ElevatedButton(
161 0 : onPressed: () => sync.del(data[index].id),
162 0 : child: Text(AppLocale.labels.peerDelete),
163 : ),
164 0 : TextWrapper(data[index].id),
165 0 : TextWrapper(status),
166 0 : data[index].status == true
167 0 : ? ElevatedButton(
168 0 : onPressed: () => sync.ping(data[index].id),
169 0 : child: Text(AppLocale.labels.peerPing),
170 : )
171 0 : : ElevatedButton(
172 0 : onPressed: () => sync.trace(data[index].id),
173 0 : child: Text(AppLocale.labels.peerConnectBtn),
174 : ),
175 : ];
176 : }),
177 0 : ...List.generate(request.length, (index) {
178 0 : return <Widget>[
179 0 : ElevatedButton(
180 0 : onPressed: () => setState(() => request.remove(request[index])),
181 0 : child: Text(AppLocale.labels.peerDelete),
182 : ),
183 0 : TextWrapper(request[index]),
184 0 : TextWrapper(AppLocale.labels.peerPending),
185 0 : ElevatedButton(
186 0 : onPressed: () => setState(() {
187 0 : sync.add(request[index]);
188 0 : request.remove(request[index]);
189 : }),
190 0 : child: Text(AppLocale.labels.peerAccept),
191 : ),
192 : ];
193 : }),
194 : ],
195 : ),
196 0 : if (sync.isActive()) ...[
197 : ThemeHelper.hIndent4x,
198 0 : Text(
199 0 : AppLocale.labels.peerOtherId,
200 0 : style: textTheme.bodyLarge,
201 : ),
202 0 : SimpleInput(controller: _controller),
203 : ThemeHelper.hIndent2x,
204 0 : SizedBox(
205 : width: double.infinity,
206 0 : child: FloatingActionButton(
207 : heroTag: 'sync_tab_add',
208 0 : onPressed: () => sync.add(_controller.text),
209 0 : tooltip: AppLocale.labels.peerConnect,
210 0 : child: Text(AppLocale.labels.peerConnect),
211 : ),
212 : ),
213 : ],
214 0 : ThemeHelper.formEndBox,
215 : ],
216 : ),
217 : ),
218 : );
219 : });
220 : });
221 : }
222 : }
|