Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions lib/database/actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ import 'package:tattoo/database/database.dart';

/// Reusable database operations shared across repositories.
extension DatabaseActions on AppDatabase {
/// Drops and recreates all tables, fully resetting the database.
Future<void> deleteEverything() async {
await transaction(() async {
final m = Migrator(this);
final reversed = allSchemaEntities.toList().reversed;
for (final entity in reversed) {
await m.drop(entity);
}
await m.createAll();
});
}

/// Returns the ID of an existing semester row, or creates one if missing.
Future<int> getOrCreateSemester(int year, int term) async {
return (await into(semesters).insertReturning(
Expand Down
11 changes: 11 additions & 0 deletions lib/i18n/en-US.i18n.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ profile:
- order 1 asdfghjkl
- order 1 bowl of fried rice
- get kicked out by the staff
clearCache: Clear Cache
clearCookies: Clear Cookies
clearPreferences: Clear Preferences
clearUserData: Clear User Data
cleared: ${item} cleared
clearFailed: Failed to clear ${item}
items:
cache: Cache
cookies: Cookies
preferences: Preferences
userData: User data
enrollmentStatus:
learning: Enrolled
leaveOfAbsence: Leave of Absence
Expand Down
2 changes: 1 addition & 1 deletion lib/i18n/strings.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// To regenerate, run: `dart run slang`
///
/// Locales: 2
/// Strings: 154 (77 per locale)
/// Strings: 174 (87 per locale)

// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
Expand Down
30 changes: 30 additions & 0 deletions lib/i18n/strings_en_US.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ class _TranslationsProfileDangerZoneEnUs extends TranslationsProfileDangerZoneZh
'order 1 bowl of fried rice',
'get kicked out by the staff',
];
@override String get clearCache => 'Clear Cache';
@override String get clearCookies => 'Clear Cookies';
@override String get clearPreferences => 'Clear Preferences';
@override String get clearUserData => 'Clear User Data';
@override String cleared({required Object item}) => '${item} cleared';
@override String clearFailed({required Object item}) => 'Failed to clear ${item}';
@override late final _TranslationsProfileDangerZoneItemsEnUs items = _TranslationsProfileDangerZoneItemsEnUs._(_root);
}

// Path: intro.features.courseTable
Expand Down Expand Up @@ -298,6 +305,19 @@ class _TranslationsIntroFeaturesCampusLifeEnUs extends TranslationsIntroFeatures
@override String get description => 'Access campus life information, with more features coming soon.';
}

// Path: profile.dangerZone.items
class _TranslationsProfileDangerZoneItemsEnUs extends TranslationsProfileDangerZoneItemsZhTw {
_TranslationsProfileDangerZoneItemsEnUs._(TranslationsEnUs root) : this._root = root, super.internal(root);

final TranslationsEnUs _root; // ignore: unused_field

// Translations
@override String get cache => 'Cache';
@override String get cookies => 'Cookies';
@override String get preferences => 'Preferences';
@override String get userData => 'User data';
}

/// The flat map containing all translations for locale <en-US>.
/// Only for edge cases! For simple maps, use the map function of this library.
///
Expand Down Expand Up @@ -371,6 +391,16 @@ extension on TranslationsEnUs {
'profile.dangerZone.actions.4' => 'order 1 asdfghjkl',
'profile.dangerZone.actions.5' => 'order 1 bowl of fried rice',
'profile.dangerZone.actions.6' => 'get kicked out by the staff',
'profile.dangerZone.clearCache' => 'Clear Cache',
'profile.dangerZone.clearCookies' => 'Clear Cookies',
'profile.dangerZone.clearPreferences' => 'Clear Preferences',
'profile.dangerZone.clearUserData' => 'Clear User Data',
'profile.dangerZone.cleared' => ({required Object item}) => '${item} cleared',
'profile.dangerZone.clearFailed' => ({required Object item}) => 'Failed to clear ${item}',
'profile.dangerZone.items.cache' => 'Cache',
'profile.dangerZone.items.cookies' => 'Cookies',
'profile.dangerZone.items.preferences' => 'Preferences',
'profile.dangerZone.items.userData' => 'User data',
'enrollmentStatus.learning' => 'Enrolled',
'enrollmentStatus.leaveOfAbsence' => 'Leave of Absence',
'enrollmentStatus.droppedOut' => 'Withdrawn',
Expand Down
51 changes: 51 additions & 0 deletions lib/i18n/strings_zh_TW.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,26 @@ class TranslationsProfileDangerZoneZhTw {
'點1碗炒飯',
'跑進吧檯被店員拖出去',
];

/// zh-TW: '清除快取'
String get clearCache => '清除快取';

/// zh-TW: '清除Cookies'
String get clearCookies => '清除Cookies';

/// zh-TW: '清除偏好設定'
String get clearPreferences => '清除偏好設定';

/// zh-TW: '清除使用者資料'
String get clearUserData => '清除使用者資料';

/// zh-TW: '已清除${item}'
String cleared({required Object item}) => '已清除${item}';

/// zh-TW: '清除${item}失敗'
String clearFailed({required Object item}) => '清除${item}失敗';

late final TranslationsProfileDangerZoneItemsZhTw items = TranslationsProfileDangerZoneItemsZhTw.internal(_root);
}

// Path: intro.features.courseTable
Expand Down Expand Up @@ -441,6 +461,27 @@ class TranslationsIntroFeaturesCampusLifeZhTw {
String get description => '彙整其他校園生活資訊,更多功能敬請期待。';
}

// Path: profile.dangerZone.items
class TranslationsProfileDangerZoneItemsZhTw {
TranslationsProfileDangerZoneItemsZhTw.internal(this._root);

final Translations _root; // ignore: unused_field

// Translations

/// zh-TW: '快取'
String get cache => '快取';

/// zh-TW: 'Cookies'
String get cookies => 'Cookies';

/// zh-TW: '偏好設定'
String get preferences => '偏好設定';

/// zh-TW: '使用者資料'
String get userData => '使用者資料';
}

/// The flat map containing all translations for locale <zh-TW>.
/// Only for edge cases! For simple maps, use the map function of this library.
///
Expand Down Expand Up @@ -514,6 +555,16 @@ extension on Translations {
'profile.dangerZone.actions.4' => '點1份asdfghjkl',
'profile.dangerZone.actions.5' => '點1碗炒飯',
'profile.dangerZone.actions.6' => '跑進吧檯被店員拖出去',
'profile.dangerZone.clearCache' => '清除快取',
'profile.dangerZone.clearCookies' => '清除Cookies',
'profile.dangerZone.clearPreferences' => '清除偏好設定',
'profile.dangerZone.clearUserData' => '清除使用者資料',
'profile.dangerZone.cleared' => ({required Object item}) => '已清除${item}',
'profile.dangerZone.clearFailed' => ({required Object item}) => '清除${item}失敗',
'profile.dangerZone.items.cache' => '快取',
'profile.dangerZone.items.cookies' => 'Cookies',
'profile.dangerZone.items.preferences' => '偏好設定',
'profile.dangerZone.items.userData' => '使用者資料',
'enrollmentStatus.learning' => '在學',
'enrollmentStatus.leaveOfAbsence' => '休學',
'enrollmentStatus.droppedOut' => '退學',
Expand Down
11 changes: 11 additions & 0 deletions lib/i18n/zh-TW.i18n.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,17 @@ profile:
- 點1份asdfghjkl
- 點1碗炒飯
- 跑進吧檯被店員拖出去
clearCache: 清除快取
clearCookies: 清除Cookies
clearPreferences: 清除偏好設定
clearUserData: 清除使用者資料
cleared: 已清除${item}
clearFailed: 清除${item}失敗
items:
cache: 快取
cookies: Cookies
preferences: 偏好設定
userData: 使用者資料
enrollmentStatus:
learning: 在學
leaveOfAbsence: 休學
Expand Down
124 changes: 110 additions & 14 deletions lib/screens/main/profile/profile_danger_zone.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,97 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:path_provider/path_provider.dart';
import 'package:tattoo/components/option_entry_tile.dart';
import 'package:tattoo/components/section_header.dart';
import 'package:tattoo/database/database.dart';
import 'package:tattoo/i18n/strings.g.dart';
import 'package:tattoo/repositories/preferences_repository.dart';
import 'package:tattoo/screens/main/profile/profile_providers.dart';
import 'package:tattoo/utils/http.dart';
import 'package:tattoo/utils/shared_preferences.dart';

class ProfileDangerZone extends ConsumerWidget {
const ProfileDangerZone({super.key});

static const Color dangerColor = Colors.red;

Future<void> _clear(
BuildContext context,
String item,
Future<void> Function() action,
) async {
try {
await action();
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.profile.dangerZone.clearFailed(item: item))),
);
}
rethrow;
}
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(t.profile.dangerZone.cleared(item: item))),
);
}
}

void _goAction(String action) {
if (action == t.profile.dangerZone.actions.last) {
SystemNavigator.pop();
} else {
throw Exception(t.profile.dangerZone.fireMessage);
}
}

void _triggerNonFlutterCrash() {
Future.delayed(Duration.zero, () {
throw Exception(t.profile.dangerZone.nonFlutterCrashException);
});
}

Future<void> _clearCache(BuildContext context) => _clear(
context,
t.profile.dangerZone.items.cache,
() async {
final cacheDir = await getApplicationCacheDirectory();
if (await cacheDir.exists()) {
await for (final entity in cacheDir.list()) {
await entity.delete(recursive: true);
}
}
},
);

Future<void> _clearCookies(BuildContext context) => _clear(
context,
t.profile.dangerZone.items.cookies,
() async {
await cookieJar.deleteAll();
},
);

Future<void> _clearPreferences(BuildContext context, WidgetRef ref) => _clear(
context,
t.profile.dangerZone.items.preferences,
() async {
await ref.read(sharedPreferencesProvider).clear();
},
);

Future<void> _clearUserData(BuildContext context, WidgetRef ref) => _clear(
context,
t.profile.dangerZone.items.userData,
() async {
await ref.read(databaseProvider).deleteEverything();
await cookieJar.deleteAll();
await const FlutterSecureStorage().deleteAll();
},
);

@override
Widget build(BuildContext context, WidgetRef ref) {
final prefs = ref.watch(preferencesRepositoryProvider);
Expand All @@ -35,26 +115,42 @@ class ProfileDangerZone extends ConsumerWidget {
title: t.profile.dangerZone.goAction(action: action),
color: dangerColor,
borderColor: dangerColor,
onTap: () {
if (action == t.profile.dangerZone.actions.last) {
SystemNavigator.pop();
} else {
throw Exception(t.profile.dangerZone.fireMessage);
}
},
onTap: () => _goAction(action),
),
OptionEntryTile.icon(
icon: Icons.bug_report_outlined,
title: t.profile.dangerZone.nonFlutterCrash,
color: dangerColor,
borderColor: dangerColor,
onTap: () {
Future.delayed(Duration.zero, () {
throw Exception(
t.profile.dangerZone.nonFlutterCrashException,
);
});
},
onTap: _triggerNonFlutterCrash,
),
OptionEntryTile.icon(
icon: Icons.cached_outlined,
title: t.profile.dangerZone.clearCache,
color: dangerColor,
borderColor: dangerColor,
onTap: () => _clearCache(context),
),
OptionEntryTile.icon(
icon: Icons.cookie_outlined,
title: t.profile.dangerZone.clearCookies,
color: dangerColor,
borderColor: dangerColor,
onTap: () => _clearCookies(context),
),
OptionEntryTile.icon(
icon: Icons.settings_backup_restore_outlined,
title: t.profile.dangerZone.clearPreferences,
color: dangerColor,
borderColor: dangerColor,
onTap: () => _clearPreferences(context, ref),
),
OptionEntryTile.icon(
icon: Icons.delete_forever_outlined,
title: t.profile.dangerZone.clearUserData,
color: dangerColor,
borderColor: dangerColor,
onTap: () => _clearUserData(context, ref),
),
],
);
Expand Down