Skip to content

Commit ca8da96

Browse files
Disable auto trim for most settings fields. Added some advice and a TODO in README.md
1 parent 62da767 commit ca8da96

File tree

2 files changed

+112
-52
lines changed

2 files changed

+112
-52
lines changed

README.md

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
Manage your homework, with untis and discord support.
66

7-
You can take/import pictures and assign them to a homework. Sharing/Synchronization of homework is also possible.
7+
You can take/import pictures and assign them to a homework. Sharing/Synchronization of homework is
8+
also possible.
89

910
### Untis
1011

@@ -16,49 +17,65 @@ And secondly, for QOL, it can import the subjects you are going to have in the n
1617

1718
### Discord
1819

19-
You can use a combination of a bot and webhooks to achieve regulated homework ex-/import. The discord bot must have read message permissions for the channels, that could be used for homeworks. Furthermore, he should be member of only one guild.
20+
You can use a combination of a bot and webhooks to achieve regulated homework ex-/import. The
21+
discord bot must have read message permissions for the channels, that could be used for homeworks.
22+
Furthermore, he should be member of only one guild.
2023

2124
You can "export"(send) your homework easily, by:
25+
2226
1. Creating and assigning a subject
2327
2. Specifying a bot (token)
2428
3. Creating a discord relation, this must be assigned to the target channel to support import
2529
4. Specifying the webhook url for the given channel
2630
5. Clicking send on the homework
2731

32+
If the discord relations are configured, you can just click on the fetch icon in the upper right
33+
corner of the home screen. This may overwrite local changes, so be careful.
34+
35+
## TODO
2836

29-
If the discord relations are configured, you can just click on the fetch icon in the upper right corner of the home screen. This may overwrite local changes, so be careful.
37+
- [ ] Qrcode generator to share webhooks and bot token
3038

3139
## Getting Started / Installation
40+
3241
<details open>
3342
<summary><h3 style="display:inline-block">An online(ish) app is enough for me </h3></summary>
3443

35-
> <b>BE WARNED: If you use the app with a bot token on the web, you are sending your token to a CORS proxy that redirects it to discord. Someone (in this case Cloudflare) could sniff your token. Unfortunately a CORS proxy is needed on the web, because discord does not send adequate CORS.</b>
44+
> <b>BE WARNED: If you use the app with a bot token on the web, you are sending your token to a CORS
45+
> proxy that redirects it to discord. Someone (in this case Cloudflare) could sniff your token.
46+
> Unfortunately a CORS proxy is needed on the web, because discord does not send adequate CORS.</b>
3647
37-
If you took notice of this warning, you can just open the GitHub Pages app: [https://randommodderjdk.github.io/hw_manager_flutter](https://randommodderjdk.github.io/hw_manager_flutter)
48+
If you took notice of this warning, you can just open the GitHub Pages
49+
app: [https://randommodderjdk.github.io/hw_manager_flutter](https://randommodderjdk.github.io/hw_manager_flutter)
3850

3951
<details> <summary><h4 style="display:inline-block">iOS / iPadOS web app</h4></summary>
4052
You have two options to add the website (always needs internet connection):
4153

42-
> ##### 1. Use Safari's "Add to Homescreen":
43-
> Go to the website above and click on the share icon. There click on "Add to home screen", type in a name and submit.
54+
> ##### 1. Use Safari's "Add to Homescreen":
55+
> Go to the website above and click on the share icon. There click on "Add to home screen", type in
56+
> a name and submit.
4457
4558
> ##### 2. Use a profile (WebClip)
46-
> Go to [https://ivi.cx/](https://ivi.cx/) and enter the details you want. You can use for example the asset provided in this repository as an icon.
59+
> Go to [https://ivi.cx/](https://ivi.cx/) and enter the details you want. You can use for example
60+
> the asset provided in this repository as an icon.
4761
4862
</details>
4963

5064
<details> <summary><h4 style="display:inline-block">Android homescreen app</h4></summary>
5165
Use your browser to create a web app on android. Search for "Add Shortcut" or anything similar.
5266

53-
This is like the native app (except the security), especially when the browser caches it. Tested with Chromium-based browsers and firefox.
67+
This is like the native app (except the security), especially when the browser caches it. Tested
68+
with Chromium-based browsers and firefox.
5469

5570
</details>
5671
</details>
57-
<details open><summary><h3 style="display:inline-block">I prefer the native app</h3></summary>
72+
<details open><summary><h3 style="display:inline-block">I prefer the native app [recommended]</h3></summary>
5873

59-
**If you are on android** and go ahead to releases and download your arch.
74+
**If you are on android**, go ahead to releases and download your arch.
6075

61-
**If you are on ios**, download the ipa (native app) in the releases, if you know how to install it. This is not really signed. I would recommend Alt-/Troll-/Sidestore. When Apple allows apps to finally be installed like on android, then do it that way.
76+
**If you are on ios**, download the ipa (native app) in the releases, if you know how to install it.
77+
This is not really signed. I would recommend Alt-/Sidestore. When Apple allows apps to
78+
finally be installed like on android, then do it that way.
6279

6380
**If you'd like any other platform**:
6481
Clone this repo, (add your platform,), build HWM (and maybe contribute a GitHub action) yourself.

lib/routes/settings_route.dart

Lines changed: 83 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class SettingsRoute extends StatelessWidget {
6868
backgroundColor: Colors.transparent,
6969
boxShadow: const [],
7070
centralizeFirstChild: false,
71-
arrowWidget: const Icon(Icons.keyboard_arrow_up_rounded, size: 40.0),
71+
arrowWidget:
72+
const Icon(Icons.keyboard_arrow_up_rounded, size: 40.0),
7273
firstChild: Expanded(
7374
child: Align(
7475
alignment: Alignment.centerLeft,
@@ -86,6 +87,7 @@ class SettingsRoute extends StatelessWidget {
8687
hintText: context.locals.settingsUntisLoginServerHint,
8788
initValue: Preferences.getUntisServer(),
8889
onChanged: (s) => Preferences.saveUntisServer(s ?? ""),
90+
autoTrim: true,
8991
),
9092
const SizedBox(height: 10),
9193
SettingsTextFormField(
@@ -99,15 +101,17 @@ class SettingsRoute extends StatelessWidget {
99101
labelText: context.locals.settingsUntisLoginUsername,
100102
hintText: context.locals.settingsUntisLoginUsernameHint,
101103
initValue: Preferences.getUntisUsername(),
102-
onChanged: (s) => Preferences.saveUntisUsername(s ?? ""),
104+
onChanged: (s) =>
105+
Preferences.saveUntisUsername(s ?? ""),
103106
),
104107
const SizedBox(height: 10),
105108
SettingsTextFormField(
106109
passwordStyle: true,
107110
labelText: context.locals.settingsUntisLoginPassword,
108111
hintText: context.locals.settingsUntisLoginPasswordHint,
109112
initValue: Preferences.getUntisPassword(),
110-
onChanged: (s) => Preferences.saveUntisPassword(s ?? ""),
113+
onChanged: (s) =>
114+
Preferences.saveUntisPassword(s ?? ""),
111115
),
112116
],
113117
),
@@ -116,16 +120,20 @@ class SettingsRoute extends StatelessWidget {
116120
HWMSettingsTile(
117121
title: Text(context.locals.settingsUntisTestLoginTitle),
118122
leading: const Icon(Icons.task_alt_rounded),
119-
description: Text(context.locals.settingsUntisTestLoginDescription),
120-
onPressed: (context) async => UntisHelper().loginWithPreferences().then(
121-
(success) =>
122-
success ? untisConfirmation(locals.untisLoginSuccess) : untisError(locals.untisLoginFailure),
123-
),
123+
description:
124+
Text(context.locals.settingsUntisTestLoginDescription),
125+
onPressed: (context) async =>
126+
UntisHelper().loginWithPreferences().then(
127+
(success) => success
128+
? untisConfirmation(locals.untisLoginSuccess)
129+
: untisError(locals.untisLoginFailure),
130+
),
124131
),
125132
HWMSettingsTile(
126133
title: Text(context.locals.settingsUntisImportTitle),
127134
leading: const Icon(Icons.cloud_download_outlined),
128-
description: Text(context.locals.settingsUntisImportDescription),
135+
description:
136+
Text(context.locals.settingsUntisImportDescription),
129137
onPressed: (context) async {
130138
final List<UntisSubject> subjects;
131139
try {
@@ -171,36 +179,55 @@ class SettingsRoute extends StatelessWidget {
171179
HWMSettingsTile(
172180
title: SettingsTextFormField(
173181
labelText: context.locals.settingsDiscordRelationsBotToken,
174-
hintText: "OPdhu29hawopuhnUOUWE9awofhou.awdawdh34WDGGHH-Wawdsghj",
182+
hintText:
183+
"OPdhu29hawopuhnUOUWE9awofhou.awdawdh34WDGGHH-Wawdsghj",
175184
initValue: Preferences.getDiscordToken(),
176-
onChanged: (s) async => Preferences.saveDiscordToken(s ?? ""),
185+
onChanged: (s) => Preferences.saveDiscordToken(s ?? ""),
186+
autoTrim: true,
177187
onDeactivate: () async {
178188
final String token = await Preferences.getDiscordToken();
179189
if (DiscordHelper().tokenEqualsTo(token)) return;
180-
discordStatusUpdate(locals.settingsDiscordRelationsBotTryToken);
181-
final bool successfulLogin = await DiscordHelper().login(token);
182-
final bool successfulGuildRefresh = await DiscordHelper().refreshGuildCache();
183-
final bool successfulChannelRefresh = await DiscordHelper().refreshChannelCache();
184-
if (successfulLogin && successfulGuildRefresh && successfulChannelRefresh) {
185-
discordConfirmation(locals.settingsDiscordRelationsBotTryTokenSuccess);
190+
discordStatusUpdate(
191+
locals.settingsDiscordRelationsBotTryToken);
192+
final bool successfulLogin =
193+
await DiscordHelper().login(token);
194+
final bool successfulGuildRefresh =
195+
await DiscordHelper().refreshGuildCache();
196+
final bool successfulChannelRefresh =
197+
await DiscordHelper().refreshChannelCache();
198+
if (successfulLogin &&
199+
successfulGuildRefresh &&
200+
successfulChannelRefresh) {
201+
discordConfirmation(
202+
locals.settingsDiscordRelationsBotTryTokenSuccess);
186203
} else {
187-
discordError(locals.settingsDiscordRelationsBotTryTokenFailure);
204+
discordError(
205+
locals.settingsDiscordRelationsBotTryTokenFailure);
188206
}
189207
},
190208
onEnter: (s) {
191209
Preferences.getDiscordToken().then((token) async {
192210
if (!DiscordHelper().tokenEqualsTo(token)) {
193-
discordStatusUpdate(locals.settingsDiscordRelationsBotTryToken);
211+
discordStatusUpdate(
212+
locals.settingsDiscordRelationsBotTryToken);
194213
}
195-
if (!DiscordHelper().tokenEqualsTo(token) || DiscordHelper().tokenEqualsTo(null)) {
196-
final bool successfulLogin = await DiscordHelper().login(token);
197-
final bool successfulGuildRefresh = await DiscordHelper().refreshGuildCache();
198-
final bool successfulChannelRefresh = await DiscordHelper().refreshChannelCache();
199-
if (!(successfulLogin && successfulGuildRefresh && successfulChannelRefresh)) {
200-
discordError(locals.settingsDiscordRelationsBotTryTokenFailure);
214+
if (!DiscordHelper().tokenEqualsTo(token) ||
215+
DiscordHelper().tokenEqualsTo(null)) {
216+
final bool successfulLogin =
217+
await DiscordHelper().login(token);
218+
final bool successfulGuildRefresh =
219+
await DiscordHelper().refreshGuildCache();
220+
final bool successfulChannelRefresh =
221+
await DiscordHelper().refreshChannelCache();
222+
if (!(successfulLogin &&
223+
successfulGuildRefresh &&
224+
successfulChannelRefresh)) {
225+
discordError(locals
226+
.settingsDiscordRelationsBotTryTokenFailure);
201227
return null;
202228
}
203-
discordConfirmation(locals.settingsDiscordRelationsBotTryTokenSuccess);
229+
discordConfirmation(
230+
locals.settingsDiscordRelationsBotTryTokenSuccess);
204231
DiscordHelper().loggedInNotifier.notifyListeners();
205232
}
206233
});
@@ -247,18 +274,29 @@ class SettingsRoute extends StatelessWidget {
247274
onPressed: (context) async {
248275
final String token = await Preferences.getDiscordToken();
249276
if (!DiscordHelper().tokenEqualsTo(token)) {
250-
discordStatusUpdate(locals.settingsDiscordRelationsBotTryToken);
251-
final bool successfulLogin = await DiscordHelper().login(token);
252-
final bool successfulGuildRefresh = await DiscordHelper().refreshGuildCache();
253-
final bool successfulChannelRefresh = await DiscordHelper().refreshChannelCache();
254-
if (!(successfulLogin && successfulGuildRefresh && successfulChannelRefresh)) {
255-
discordError(locals.settingsDiscordRelationsBotTryTokenFailure);
277+
discordStatusUpdate(
278+
locals.settingsDiscordRelationsBotTryToken);
279+
final bool successfulLogin =
280+
await DiscordHelper().login(token);
281+
final bool successfulGuildRefresh =
282+
await DiscordHelper().refreshGuildCache();
283+
final bool successfulChannelRefresh =
284+
await DiscordHelper().refreshChannelCache();
285+
if (!(successfulLogin &&
286+
successfulGuildRefresh &&
287+
successfulChannelRefresh)) {
288+
discordError(
289+
locals.settingsDiscordRelationsBotTryTokenFailure);
256290
return;
257291
}
258292
}
259-
final List<DiscordRelation> relations = await DBHelper.retrieveDiscordRelations();
260-
final Iterable<GuildChannel>? channels = (await DiscordHelper().channels)?.where(
261-
(c) => !relations.map((r) => r.channelID).contains(c.id.value.toString()),
293+
final List<DiscordRelation> relations =
294+
await DBHelper.retrieveDiscordRelations();
295+
final Iterable<GuildChannel>? channels =
296+
(await DiscordHelper().channels)?.where(
297+
(c) => !relations
298+
.map((r) => r.channelID)
299+
.contains(c.id.value.toString()),
262300
);
263301
if (channels == null) {
264302
discordError(locals.settingsDiscordRelationsImportFailure);
@@ -271,11 +309,13 @@ class SettingsRoute extends StatelessWidget {
271309
channelID: c.id.value.toString(),
272310
channelName: c.name,
273311
);
274-
if (kDebugMode) print("Added channel ${c.name} ${c.id.value}");
312+
if (kDebugMode)
313+
print("Added channel ${c.name} ${c.id.value}");
275314
DBHelper.insertDiscordRelation(dr);
276315
count++;
277316
}
278-
discordConfirmation(locals.settingsDiscordRelationsImportSuccess(count));
317+
discordConfirmation(
318+
locals.settingsDiscordRelationsImportSuccess(count));
279319
},
280320
leading: const Icon(Icons.cloud_download_outlined),
281321
),
@@ -312,7 +352,7 @@ class SettingsTextFormField extends StatefulWidget {
312352
required this.labelText,
313353
required this.hintText,
314354
this.passwordStyle = false,
315-
this.autoTrim = true,
355+
this.autoTrim = false,
316356
this.initValue,
317357
this.onChanged,
318358
this.onEnter,
@@ -353,12 +393,15 @@ class _SettingsTextFormFieldState extends State<SettingsTextFormField> {
353393
).applyDefaults(Theme.of(context).inputDecorationTheme),
354394
onChanged: (s) {
355395
if (widget.autoTrim) {
356-
if (s.trim() != s) setState(() => _controller.text = s.trim()); // Remove whitespace immediately
396+
if (s.trim() != s)
397+
setState(() =>
398+
_controller.text = s.trim()); // Remove whitespace immediately
357399
s = s.trim();
358400
}
359401
widget.onChanged?.call(s);
360402
},
361-
onFieldSubmitted: (s) => widget.onEnter?.call(widget.autoTrim ? s.trim() : s),
403+
onFieldSubmitted: (s) =>
404+
widget.onEnter?.call(widget.autoTrim ? s.trim() : s),
362405
//onTapOutside: onFinishEditing, // disabled because it break the focus system
363406
);
364407
}

0 commit comments

Comments
 (0)