1
0
forked from firka/firka

feat: account switching

This commit is contained in:
2025-09-11 12:47:43 +02:00
parent e44684049f
commit caeca5e050
7 changed files with 206 additions and 43 deletions

View File

@@ -327,7 +327,15 @@ class SettingsStore {
developerOptsEnabled, null, null, "Developer", false, never),
}),
always);
items["profile_settings"] = SettingsGroup(
0,
LinkedHashMap.of({
"back": SettingsBackHeader(0, l10n.s_your_account, always),
"e_kreta_accounts": SettingsHeaderSmall(0, l10n.s_acc_kreta, always),
"e_padding": SettingsPadding(0, 8, always),
"e_kreta_account_picker": SettingsKretenAccountPicker(0, always),
}),
never);
appIcons = {
"ace": l10n.ic_ace,
"ace_f": l10n.ic_ace_f,
@@ -676,6 +684,45 @@ class SettingsAppIconPreview implements SettingsItem {
Future<void> save(IsarCollection<AppSettingsModel> model) async {}
}
class SettingsKretenAccountPicker implements SettingsItem {
@override
Id key;
@override
FirkaIconType? iconType;
@override
Object? iconData;
@override
bool Function() visibilityProvider;
@override
Future<void> Function() postUpdate = () async {};
String title = "";
String icon = "";
int accountIndex = 0;
SettingsKretenAccountPicker(this.key, this.visibilityProvider);
@override
Future<void> load(IsarCollection<AppSettingsModel> model) async {
var v = await model.get(key);
if (v == null || v.valueIndex == null) {
accountIndex = 0;
} else {
accountIndex = v.valueIndex!;
}
}
@override
Future<void> save(IsarCollection<AppSettingsModel> model) async {
var v = AppSettingsModel();
v.id = key;
v.valueIndex = accountIndex;
await model.put(v);
initData.settingsUpdateNotifier.update();
}
}
class SettingsAppIconPicker implements SettingsItem {
@override
Id key;

View File

@@ -63,7 +63,7 @@ class AppInitialization {
final PackageInfo packageInfo;
final DeviceInfo devInfo;
late KretaClient client;
int tokenCount;
List<TokenModel> tokens;
bool hasWatchListener = false;
Uint8List? profilePicture;
SettingsStore settings;
@@ -74,7 +74,7 @@ class AppInitialization {
required this.isar,
required this.devInfo,
required this.packageInfo,
required this.tokenCount,
required this.tokens,
required this.settings,
required this.l10n,
});
@@ -154,13 +154,51 @@ void initTheme(AppInitialization data) {
}
}
Future<void> _initData(AppInitialization init) async {
await init.settings.load(init.isar.appSettingsModels);
initLang(init);
initTheme(init);
init.settings = SettingsStore(init.l10n);
await init.settings.load(init.isar.appSettingsModels);
var dispatcher = SchedulerBinding.instance.platformDispatcher;
dispatcher.onPlatformBrightnessChanged = () {
globalUpdate.update();
initTheme(init);
};
resetOldTimeTableCache(init.isar);
resetOldHomeworkCache(init.isar);
if (init.tokens.isNotEmpty) {
final i = (init.settings.group("profile_settings")["e_kreta_account_picker"]
as SettingsKretenAccountPicker)
.accountIndex;
init.client = KretaClient(
(await init.isar.tokenModels.where().findAll())[i], init.isar);
await WidgetCacheHelper.updateWidgetCache(appStyle, init.client);
}
final dataDir = await getApplicationDocumentsDirectory();
var pfpFile = File(p.join(dataDir.path, "profile.webp"));
if (await pfpFile.exists()) {
init.profilePicture = await pfpFile.readAsBytes();
}
}
Future<AppInitialization> initializeApp() async {
if (initDone) return initData;
if (initDone) {
await _initData(initData);
return initData;
}
final isar = await initDB();
final tokenCount = await isar.tokenModels.count();
final tokens = await isar.tokenModels.where().findAll();
if (kDebugMode) {
print('Token count: $tokenCount');
print('Token count: ${tokens.length}');
}
var devInfoFetched = false;
@@ -190,46 +228,17 @@ Future<AppInitialization> initializeApp() async {
isar: isar,
devInfo: devInfo,
packageInfo: await PackageInfo.fromPlatform(),
tokenCount: tokenCount,
tokens: tokens,
settings: SettingsStore(AppLocalizationsHu()),
l10n: AppLocalizationsHu(),
);
await _initData(init);
init.settingsUpdateNotifier.addListener(() {
debugPrint("Settings updated");
});
await init.settings.load(init.isar.appSettingsModels);
initLang(init);
initTheme(init);
init.settings = SettingsStore(init.l10n);
await init.settings.load(init.isar.appSettingsModels);
var dispatcher = SchedulerBinding.instance.platformDispatcher;
dispatcher.onPlatformBrightnessChanged = () {
globalUpdate.update();
initTheme(init);
};
resetOldTimeTableCache(isar);
resetOldHomeworkCache(isar);
// TODO: Account selection
if (tokenCount > 0) {
init.client =
KretaClient((await isar.tokenModels.where().findFirst())!, isar);
await WidgetCacheHelper.updateWidgetCache(appStyle, init.client);
}
final dataDir = await getApplicationDocumentsDirectory();
var pfpFile = File(p.join(dataDir.path, "profile.webp"));
if (await pfpFile.exists()) {
init.profilePicture = await pfpFile.readAsBytes();
}
return init;
}
@@ -310,7 +319,7 @@ class InitializationScreen extends StatelessWidget {
switch (msg["id"]) {
case "ping":
if (initData.tokenCount > 0) {
if (initData.tokens.isNotEmpty) {
debugPrint("[Phone -> Watch]: pong");
watch.sendMessage({"id": "pong"});
navigatorKey.currentState?.push(
@@ -324,7 +333,7 @@ class InitializationScreen extends StatelessWidget {
});
}
if (snapshot.data!.tokenCount == 0) {
if (snapshot.data!.tokens.isEmpty) {
screen = LoginScreen(
initData,
key: ValueKey('loginScreen'),

View File

@@ -91,6 +91,28 @@ void showExtrasBottomSheet(BuildContext context, AppInitialization data) {
],
right: [],
),
),
GestureDetector(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DefaultAssetBundle(
bundle: FirkaBundle(),
child: SettingsScreen(
data,
data.settings.items
.group("profile_settings")))));
},
child: FirkaCard(
left: [
Text(data.l10n.s_your_account,
style: appStyle.fonts.B_16R.apply(
color: appStyle.colors.textPrimary))
],
right: [],
),
)
],
),

View File

@@ -214,7 +214,7 @@ class _DebugScreen extends FirkaState<DebugScreen> {
await isar.tokenModels.clear();
});
widget.data.tokenCount = 0;
widget.data.tokens = List.empty(growable: true);
Navigator.push(
context,

View File

@@ -1,5 +1,6 @@
import 'dart:collection';
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import 'package:firka/helpers/db/models/app_settings_model.dart';
import 'package:firka/helpers/image_preloader.dart';
import 'package:firka/helpers/ui/firka_button.dart';
@@ -14,6 +15,7 @@ import 'package:majesticons_flutter/majesticons_flutter.dart';
import '../../../../helpers/firka_bundle.dart';
import '../../../../helpers/firka_state.dart';
import '../../../../helpers/settings/setting.dart';
import '../../widgets/login_webview.dart';
class SettingsScreen extends StatefulWidget {
final AppInitialization data;
@@ -494,6 +496,88 @@ class _SettingsScreenState extends FirkaState<SettingsScreen> {
continue;
}
if (item is SettingsKretenAccountPicker) {
for (var i = 0; i < widget.data.tokens.length; i++) {
final token = widget.data.tokens[i];
final jwt = JWT.decode(token.idToken!);
widgets.add(GestureDetector(
child: SizedBox(
height: 52,
child: FirkaCard(
left: [
Text(
jwt.payload["name"],
style: appStyle.fonts.B_16R
.apply(color: appStyle.colors.textPrimary),
),
SizedBox(width: 8),
Text(
jwt.payload["role"],
style: appStyle.fonts.B_16R
.apply(color: appStyle.colors.textTertiary),
)
],
right: [
i != item.accountIndex
? SizedBox()
: Checkbox(
value: true,
fillColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
return appStyle.colors.secondary;
}),
onChanged: (_) async {
setState(() {
// item.activeIndex = i;
});
await widget.data.isar.writeTxn(() async {
await item
.save(widget.data.isar.appSettingsModels);
});
debugPrint('Settings saved');
})
],
),
),
onTap: () async {
if (i != item.accountIndex) {
await widget.data.isar.writeTxn(() async {
item.accountIndex = i;
await item.save(widget.data.isar.appSettingsModels);
});
runApp(InitializationScreen());
}
},
));
widgets.add(SizedBox(height: 8));
}
widgets.add(GestureDetector(
child: FirkaCard(left: [
Text(
widget.data.l10n.s_acc_add,
style: appStyle.fonts.B_16R
.apply(color: appStyle.colors.textPrimary),
)
]),
onTap: () {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return LoginWebviewWidget(widget.data,
username: widget.data.client.model.studentId.toString(),
schoolId: widget.data.client.model.iss!);
},
);
},
));
continue;
}
}
return widgets;

View File

@@ -1,6 +1,7 @@
import 'package:firka/main.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../helpers/api/client/kreta_client.dart';
@@ -65,7 +66,7 @@ class _LoginWebviewWidgetState extends FirkaState<LoginWebviewWidget> {
});
widget.data.client = KretaClient(tokenModel, isar);
widget.data.tokenCount = await isar.tokenModels.count();
widget.data.tokens = await isar.tokenModels.where().findAll();
if (!mounted) return NavigationDecision.prevent;