Skip to content

Commit 0b222bd

Browse files
author
Dario Segura
committed
[STRAPI] Users must login to use the app
1 parent c8b6d6d commit 0b222bd

File tree

10 files changed

+418
-10
lines changed

10 files changed

+418
-10
lines changed

lib/main.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import 'dart:io';
22
import 'package:flutter/foundation.dart';
33
import 'package:flutter/widgets.dart';
44
import 'package:pheno_ui/pheno_ui.dart';
5-
import 'package:pheno_ui_tester/widgets/category_picker.dart';
5+
import 'package:pheno_ui_tester/widgets/login.dart';
66
import 'package:window_manager/window_manager.dart';
77

88
void main() async {
99
Strapi().server = 'https://api.develop.mindora.dev';
10+
1011
await initializePhenoUi();
1112
if (!kIsWeb && (Platform.isLinux || Platform.isWindows || Platform.isMacOS)) {
1213
WidgetsFlutterBinding.ensureInitialized();
@@ -23,7 +24,7 @@ class AppPhenoUI extends StatelessWidget {
2324
Widget build(BuildContext context) {
2425
return MiraiApp(
2526
title: 'Pheno UI',
26-
homeBuilder: (context) => const CategoryPicker(),
27+
homeBuilder: (context) => const Login(),
2728
);
2829
}
2930
}

lib/widgets/login.dart

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import 'package:flutter/cupertino.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:pheno_ui/interface/strapi.dart';
4+
import 'package:pheno_ui_tester/widgets/loading_screen.dart';
5+
import 'package:shared_preferences/shared_preferences.dart';
6+
7+
import 'category_picker.dart';
8+
9+
class Login extends StatefulWidget {
10+
const Login({super.key});
11+
12+
@override
13+
LoginState createState() => LoginState();
14+
}
15+
16+
class LoginState extends State<Login> {
17+
final TextEditingController _server = TextEditingController();
18+
final TextEditingController _user = TextEditingController();
19+
final TextEditingController _password = TextEditingController();
20+
late final SharedPreferences prefs;
21+
String? error;
22+
23+
bool _loaded = false;
24+
25+
final ButtonStyle _buttonStyle = ButtonStyle(
26+
minimumSize: MaterialStateProperty.all(const Size(200, 50)),
27+
);
28+
29+
final Widget _gap = const SizedBox(height: 20);
30+
31+
final TextStyle _textStyle = const TextStyle(
32+
color: Colors.white,
33+
fontSize: 12,
34+
fontWeight: FontWeight.normal,
35+
decoration: TextDecoration.none,
36+
);
37+
38+
final TextStyle _errorStyle = const TextStyle(
39+
color: Colors.orange,
40+
fontSize: 14,
41+
fontWeight: FontWeight.bold,
42+
decoration: TextDecoration.none,
43+
);
44+
45+
final InputDecoration _inputDecoration = const InputDecoration(
46+
enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.grey)),
47+
focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Colors.white)),
48+
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.white)),
49+
contentPadding: EdgeInsets.all(10),
50+
isDense: true,
51+
);
52+
53+
@override
54+
void initState() {
55+
super.initState();
56+
SharedPreferences.getInstance().then((prefs) async {
57+
this.prefs = prefs;
58+
_server.text = prefs.getString('strapi_server') ?? '';
59+
_user.text = prefs.getString('strapi_user') ?? '';
60+
String? jwt = prefs.getString('strapi_jwt');
61+
if (_server.text.isNotEmpty && jwt != null && jwt.isNotEmpty) {
62+
if (!await Strapi().loginJwt(_server.text, jwt)) {
63+
prefs.remove('strapi_jwt');
64+
} else {
65+
_user.text = Strapi().user!;
66+
prefs.setString('strapi_user', _user.text);
67+
}
68+
}
69+
70+
setState(() {
71+
_loaded = true;
72+
});
73+
});
74+
}
75+
76+
Widget _buildLogin(BuildContext context) {
77+
List<Widget> children = [
78+
Text('Server:', style: _textStyle),
79+
SizedBox(
80+
width: 300,
81+
// height: 100,
82+
child: TextField(
83+
decoration: _inputDecoration,
84+
controller: _server,
85+
style: _textStyle,
86+
),
87+
),
88+
89+
_gap,
90+
91+
Text('User:', style: _textStyle),
92+
SizedBox(
93+
width: 300,
94+
// height: 100,
95+
child: TextField(
96+
decoration: _inputDecoration,
97+
controller: _user,
98+
style: _textStyle,
99+
),
100+
),
101+
102+
_gap,
103+
104+
Text('Password:', style: _textStyle),
105+
SizedBox(
106+
width: 300,
107+
// height: 100,
108+
child: TextField(
109+
decoration: _inputDecoration,
110+
controller: _password,
111+
style: _textStyle,
112+
),
113+
),
114+
115+
_gap,
116+
117+
ElevatedButton(
118+
style: _buttonStyle,
119+
onPressed: () async {
120+
setState(() {
121+
_loaded = false;
122+
});
123+
try {
124+
String jwt = await Strapi().login(Uri.parse(_server.text), _user.text, _password.text);
125+
prefs.setString('strapi_server', _server.text);
126+
prefs.setString('strapi_user', _user.text);
127+
prefs.setString('strapi_jwt', jwt);
128+
} catch (e) {
129+
error = e.toString();
130+
}
131+
setState(() {
132+
_loaded = true;
133+
});
134+
},
135+
child: const Text('Login'),
136+
),
137+
];
138+
139+
if (error != null) {
140+
children.add(_gap);
141+
children.add(Text(error!, style: _errorStyle));
142+
}
143+
144+
return Material(
145+
color: Colors.transparent,
146+
child: Center(
147+
child: Column(
148+
mainAxisSize: MainAxisSize.min,
149+
children: children,
150+
),
151+
),
152+
);
153+
}
154+
155+
Widget _buildLogout(BuildContext context) {
156+
return Center(
157+
child: Column(
158+
mainAxisSize: MainAxisSize.min,
159+
children: [
160+
Text(
161+
'Logged in\n\nServer:\n${Strapi().server}\n\nUser:\n${Strapi().user}\n',
162+
style: _textStyle,
163+
),
164+
165+
_gap,
166+
167+
ElevatedButton(
168+
style: _buttonStyle,
169+
onPressed: () {
170+
setState(() {
171+
Strapi().logout();
172+
prefs.remove('strapi_jwt');
173+
});
174+
},
175+
child: const Text('Logout'),
176+
),
177+
178+
_gap,
179+
180+
ElevatedButton(
181+
style: _buttonStyle,
182+
onPressed: () {
183+
Navigator.of(context).push(PageRouteBuilder(
184+
settings: const RouteSettings(name: 'category_picker'),
185+
transitionDuration: Duration.zero,
186+
reverseTransitionDuration: Duration.zero,
187+
pageBuilder: (context, _, __) => const CategoryPicker(),
188+
));
189+
},
190+
child: const Text('Continue'),
191+
),
192+
],
193+
),
194+
);
195+
}
196+
197+
@override
198+
Widget build(BuildContext context) {
199+
if (!_loaded) {
200+
return loadingScreen();
201+
}
202+
return Container(
203+
color: Colors.blueGrey,
204+
child: Strapi().isLoggedIn ? _buildLogout(context) : _buildLogin(context),
205+
);
206+
}
207+
}

lib/widgets/picker_state.dart

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ abstract class PickerWidget extends StatefulWidget {
99
const PickerWidget({super.key});
1010
Future<List<PhenoDataEntry>> Function() get getList;
1111
Widget Function(PhenoDataEntry, BuildContext, List<PhenoDataEntry>) get builder;
12+
Future<void> Function(PhenoDataEntry)? get delete => null;
1213
String get title;
1314
}
1415

1516
class PickerState<T extends PickerWidget> extends State<T> {
1617
List<PhenoDataEntry>? entries;
18+
bool _loading = false;
1719

1820
PickerState();
1921

@@ -32,13 +34,32 @@ class PickerState<T extends PickerWidget> extends State<T> {
3234

3335
@override
3436
Widget build(BuildContext context) {
35-
if (entries == null) {
37+
if (entries == null || _loading) {
3638
return loadingScreen();
3739
}
3840

3941
List<Widget> children = (entries as List).map((e) => ListTile(
40-
leading: const Icon(Icons.screenshot),
42+
leading: const Icon(
43+
Icons.screenshot,
44+
size: 20,
45+
),
46+
trailing: widget.delete == null? null :IconButton(
47+
onPressed: () async {
48+
setState(() => _loading = true);
49+
await widget.delete!(e);
50+
_loading = false;
51+
loadEntries();
52+
},
53+
icon: const Icon(
54+
Icons.delete,
55+
size: 16,
56+
),
57+
),
4158
title: Text(e.name),
59+
titleTextStyle: const TextStyle(
60+
color: Colors.black,
61+
fontSize: 14,
62+
),
4263
onTap: () => Navigator.push(context, PageRouteBuilder(
4364
settings: RouteSettings(name: '${ModalRoute.of(context)!.settings.name}${e.name}/'),
4465
transitionDuration: Duration.zero,

lib/widgets/screen_picker.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ class ScreenPicker extends PickerWidget {
1111
@override
1212
get builder => _builder;
1313

14+
@override
15+
get delete => _delete;
16+
1417
@override
1518
get title => 'Layout';
1619

@@ -26,6 +29,13 @@ class ScreenPicker extends PickerWidget {
2629
);
2730
}
2831

32+
Future<void> _delete(PhenoDataEntry entry) async {
33+
if (!Strapi().isLoggedIn) {
34+
throw Exception('You must be logged in to delete a screen');
35+
}
36+
await Strapi().deleteScreen(entry.id);
37+
}
38+
2939
Future<List<PhenoDataEntry>> _getList() {
3040
return Strapi().getScreenList(entry.id);
3141
}

macos/Flutter/GeneratedPluginRegistrant.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import Foundation
77

88
import path_provider_foundation
99
import screen_retriever
10+
import shared_preferences_foundation
1011
import window_manager
1112

1213
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
1314
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
1415
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
16+
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
1517
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
1618
}

0 commit comments

Comments
 (0)