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:app_finance/pages/_interfaces/interface_page_inject.dart';
17 : import 'package:material_ui/material_ui.dart';
18 : import 'package:flutter/services.dart';
19 : import 'package:provider/provider.dart';
20 :
21 : class SyncTab extends StatefulWidget {
22 : final FnPageCallback callback;
23 :
24 0 : const SyncTab({
25 : super.key,
26 : required this.callback,
27 : });
28 :
29 0 : @override
30 0 : SyncTabState createState() => SyncTabState();
31 : }
32 :
33 : class SyncTabState extends State<SyncTab> {
34 : final _controller = TextEditingController();
35 : final _scroll = ScrollController();
36 : late AppSync sync;
37 : late AppData dataProvider;
38 : List<String> request = [];
39 : bool loading = false;
40 :
41 0 : @override
42 : void initState() {
43 0 : widget.callback((
44 0 : buildButton: (_, __) => ThemeHelper.emptyBox,
45 : buttonName: '',
46 0 : title: AppLocale.labels.syncHeadline,
47 : ));
48 0 : super.initState();
49 : }
50 :
51 0 : @override
52 : void dispose() {
53 0 : _controller.dispose();
54 0 : sync.unfollow(runtimeType);
55 0 : super.dispose();
56 : }
57 :
58 0 : void ping(Uint8List id) {
59 0 : final uuid = String.fromCharCodes(id);
60 0 : if (sync.getPeers().where((e) => e.id == uuid).isEmpty) {
61 0 : setState(() => request.add(uuid));
62 : }
63 0 : ScaffoldMessenger.of(context).showSnackBar(
64 0 : SnackBar(content: Text(AppLocale.labels.pongStatus(uuid))),
65 : );
66 : }
67 :
68 0 : void synchronize() {
69 0 : setState(() => loading = true);
70 0 : dataProvider.getList(AppDataType.goals).forEach((o) => sync.send(o.toStream()));
71 0 : dataProvider.getList(AppDataType.bills).forEach((o) => sync.send(o.toStream()));
72 0 : dataProvider.getList(AppDataType.invoice).forEach((o) => sync.send(o.toStream()));
73 0 : dataProvider.getList(AppDataType.budgets).forEach((o) => sync.send(o.toStream()));
74 0 : dataProvider.getList(AppDataType.accounts).forEach((o) => sync.send(o.toStream()));
75 0 : dataProvider.getList(AppDataType.currencies).forEach((o) => sync.send(o.toStream()));
76 0 : dataProvider.getList(AppDataType.payments).forEach((o) => sync.send(o.toStream()));
77 0 : setState(() => loading = false);
78 0 : ScaffoldMessenger.of(context).showSnackBar(
79 0 : SnackBar(content: Text(AppLocale.labels.peerSent)),
80 : );
81 : }
82 :
83 0 : @override
84 : Widget build(BuildContext context) {
85 0 : final textTheme = context.textTheme;
86 0 : final indent = ThemeHelper.getIndent();
87 0 : return Consumer<AppSync>(builder: (context, appSync, _) {
88 0 : sync = appSync..followBinary(runtimeType, ping);
89 0 : dataProvider = Provider.of<AppData>(context, listen: false);
90 0 : final data = sync.getPeers();
91 0 : return LayoutBuilder(builder: (context, constraints) {
92 0 : double width = ThemeHelper.getWidth(context, 4, constraints);
93 0 : if (ThemeHelper.isNavRight(context, constraints)) {
94 0 : width -= ThemeHelper.barHeight;
95 : }
96 0 : return SingleChildScrollView(
97 0 : controller: _scroll,
98 0 : child: Padding(
99 0 : padding: EdgeInsets.all(indent),
100 0 : child: Column(
101 0 : mainAxisAlignment: AppDesign.getAlignment<MainAxisAlignment>(),
102 0 : children: [
103 0 : RowWidget(
104 : maxWidth: width,
105 : alignment: MainAxisAlignment.spaceBetween,
106 : indent: indent,
107 : chunk: const [null, 85],
108 0 : children: [
109 0 : [
110 0 : Text(
111 0 : AppLocale.labels.peerId,
112 0 : style: textTheme.bodyLarge,
113 : ),
114 0 : Container(
115 0 : padding: EdgeInsets.all(indent),
116 0 : color: context.colorScheme.fieldBackground,
117 0 : child: SelectableText(
118 0 : sync.getUuid() ?? AppLocale.labels.pearDisabled,
119 0 : style: textTheme.bodyLarge,
120 0 : onTap: () {
121 0 : Clipboard.setData(ClipboardData(text: sync.getUuid() ?? ''));
122 0 : ScaffoldMessenger.of(context).showSnackBar(
123 0 : SnackBar(content: Text(AppLocale.labels.copiedToClipboard)),
124 : );
125 : },
126 : ),
127 : ),
128 : ],
129 0 : [
130 0 : Text(
131 0 : sync.isActive() ? AppLocale.labels.peerOnline : AppLocale.labels.peerClosed,
132 0 : style: textTheme.bodyLarge,
133 : ),
134 0 : Align(
135 : alignment: Alignment.topRight,
136 0 : child: Switch(
137 0 : value: sync.isActive(),
138 0 : onChanged: (value) => setState(() => value ? sync.enable() : sync.disable()),
139 : ),
140 : ),
141 : ],
142 : ],
143 : ),
144 : ThemeHelper.hIndent2x,
145 0 : if (sync.isActive())
146 0 : loading
147 0 : ? Row(
148 : crossAxisAlignment: CrossAxisAlignment.center,
149 0 : children: [
150 0 : Text(AppLocale.labels.pearLoading),
151 0 : LoadingWidget(isLoading: loading, size: const Size(48, 48)),
152 : ],
153 : )
154 0 : : SizedBox(
155 : width: double.infinity,
156 0 : child: FloatingActionButton(
157 : heroTag: 'sync_tab_sync',
158 0 : onPressed: synchronize,
159 0 : tooltip: AppLocale.labels.peerSync,
160 0 : child: Text(AppLocale.labels.peerSync),
161 : ),
162 : ),
163 0 : ThemeHelper.hIndent4x,
164 0 : TableWidget(
165 : width: width,
166 0 : shadowColor: context.colorScheme.onSurface.withValues(alpha: 0.1),
167 : chunk: const [80, null, 80, 90],
168 0 : data: [
169 0 : [
170 0 : Center(child: Text(AppLocale.labels.peerAction)),
171 0 : TextWrapper(AppLocale.labels.peerDevice),
172 0 : TextWrapper(AppLocale.labels.peerStatus),
173 0 : Center(child: Text(AppLocale.labels.peerAction)),
174 : ],
175 0 : ...List.generate(data.length, (index) {
176 0 : String status = switch (data[index].status) {
177 0 : true => AppLocale.labels.peerOnline,
178 0 : false => AppLocale.labels.peerClosed,
179 0 : _ => AppLocale.labels.peerOffline,
180 : };
181 0 : return <Widget>[
182 0 : ElevatedButton(
183 0 : onPressed: () => sync.del(data[index].id),
184 0 : child: Text(AppLocale.labels.peerDelete),
185 : ),
186 0 : TextWrapper(data[index].id),
187 0 : TextWrapper(status),
188 0 : data[index].status == true
189 0 : ? ElevatedButton(
190 0 : onPressed: () => sync.ping(data[index].id),
191 0 : child: Text(AppLocale.labels.peerPing),
192 : )
193 0 : : ElevatedButton(
194 0 : onPressed: () => sync.trace(data[index].id),
195 0 : child: Text(AppLocale.labels.peerConnectBtn),
196 : ),
197 : ];
198 : }),
199 0 : ...List.generate(request.length, (index) {
200 0 : return <Widget>[
201 0 : ElevatedButton(
202 0 : onPressed: () => setState(() => request.remove(request[index])),
203 0 : child: Text(AppLocale.labels.peerDelete),
204 : ),
205 0 : TextWrapper(request[index]),
206 0 : TextWrapper(AppLocale.labels.peerPending),
207 0 : ElevatedButton(
208 0 : onPressed: () => setState(() {
209 0 : sync.add(request[index]);
210 0 : request.remove(request[index]);
211 : }),
212 0 : child: Text(AppLocale.labels.peerAccept),
213 : ),
214 : ];
215 : }),
216 : ],
217 : ),
218 0 : if (sync.isActive()) ...[
219 : ThemeHelper.hIndent4x,
220 0 : Text(
221 0 : AppLocale.labels.peerOtherId,
222 0 : style: textTheme.bodyLarge,
223 : ),
224 0 : SimpleInput(controller: _controller),
225 : ThemeHelper.hIndent2x,
226 0 : SizedBox(
227 : width: double.infinity,
228 0 : child: FloatingActionButton(
229 : heroTag: 'sync_tab_add',
230 0 : onPressed: () => sync.add(_controller.text),
231 0 : tooltip: AppLocale.labels.peerConnect,
232 0 : child: Text(AppLocale.labels.peerConnect),
233 : ),
234 : ),
235 : ],
236 0 : ThemeHelper.formEndBox,
237 : ],
238 : ),
239 : ),
240 : );
241 : });
242 : });
243 : }
244 : }
|