diff --git a/assets/translations/en.json b/assets/translations/en.json index 98263701..15de115c 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -17,7 +17,8 @@ "unselect_all": "Unselect All", "no_info": "No information", "all_selected": "All Selected", - "num_selected": " Selected" + "num_selected": " Selected", + "close": "Close" }, "semester": { "spring": "Spring", @@ -121,7 +122,11 @@ "major": "Major", "my_review": "My Reviews", "liked_review": "Liked Reviews", - "logout": "Logout" + "logout": "Logout", + "delete_account": "Delete Account", + "ask_delete_account": "Are you sure you want to delete your account?", + "account_deleted": "Account Deleted", + "deleted_account": "This is the deleted account." }, "settings": { "send_error_log": "Send error log", diff --git a/assets/translations/ko.json b/assets/translations/ko.json index d209d355..b17b4968 100644 --- a/assets/translations/ko.json +++ b/assets/translations/ko.json @@ -17,7 +17,8 @@ "unselect_all": "모두 해제", "no_info": "정보 없음", "all_selected": "전체 선택됨", - "num_selected": "개 선택됨" + "num_selected": "개 선택됨", + "close": "닫기" }, "semester": { "spring": "봄", @@ -121,7 +122,11 @@ "major": "전공", "my_review": "내가 들은 과목", "liked_review": "좋아요한 후기", - "logout": "로그아웃" + "logout": "로그아웃", + "delete_account": "계정 삭제", + "ask_delete_account": "계정을 정말 삭제하시겠습니까?", + "account_deleted": "계정 삭제됨", + "deleted_account": "삭제된 계정입니다." }, "settings": { "send_error_log": "오류 로그 전송", diff --git a/lib/pages/login_page.dart b/lib/pages/login_page.dart index 74901ca8..975c94b3 100644 --- a/lib/pages/login_page.dart +++ b/lib/pages/login_page.dart @@ -1,9 +1,14 @@ import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:otlplus/constants/color.dart'; +import 'package:otlplus/utils/responsive_button.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/url.dart'; import 'package:otlplus/providers/auth_model.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:webview_flutter/webview_flutter.dart'; class LoginPage extends StatefulWidget { @@ -16,6 +21,36 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { bool _isVisible = true; + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback( + (_) async { + if (!((await SharedPreferences.getInstance()).getBool('hasAccount') ?? + true)) { + await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('user.account_deleted'.tr()), + content: Text('user.deleted_account'.tr()), + actions: [ + IconTextButton( + padding: EdgeInsets.all(12), + text: 'common.close'.tr(), + color: OTLColor.pinksMain, + onTap: () { + SystemNavigator.pop(); + }, + ), + ], + ), + ); + } + }, + ); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/pages/timetable_page.dart b/lib/pages/timetable_page.dart index 26fa50b0..5d646f33 100644 --- a/lib/pages/timetable_page.dart +++ b/lib/pages/timetable_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:otlplus/utils/build_page_route.dart'; import 'package:otlplus/providers/lecture_search_model.dart'; import 'package:otlplus/utils/responsive_button.dart'; +import 'package:otlplus/widgets/delete_dialog.dart'; import 'package:otlplus/widgets/lecture_search.dart'; import 'package:otlplus/widgets/map_view.dart'; import 'package:provider/provider.dart'; @@ -254,8 +255,15 @@ class _TimetablePageState extends State { barrierDismissible: true, barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, - pageBuilder: (context, _, __) => - _buildDeleteDialog(context, timetableModel.selectedIndex), + pageBuilder: (context, _, __) => DeleteDialog( + text: 'timetable.ask_delete_tab'.tr(args: [ + 'timetable.tab' + .tr(args: [timetableModel.selectedIndex.toString()]) + ]), + onDelete: () { + context.read().deleteTimetable(); + }, + ), ); }, onExportTap: (type) { @@ -265,76 +273,4 @@ class _TimetablePageState extends State { }, ); } - - Widget _buildDeleteDialog(BuildContext context, int i) { - return Center( - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: Material( - child: IntrinsicWidth( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(16, 19, 16, 20), - alignment: Alignment.center, - color: Colors.white, - child: Text( - 'timetable.ask_delete_tab'.tr(args: [ - 'timetable.tab'.tr(args: [i.toString()]) - ]), - style: TextStyle( - fontSize: 12, - ), - ), - ), - Row( - children: [ - Expanded( - child: GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - height: 40, - alignment: Alignment.center, - color: OTLColor.grayE, - child: Text( - 'common.cancel'.tr(), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w700, - ), - ), - ), - ), - ), - Expanded( - child: GestureDetector( - onTap: () { - context.read().deleteTimetable(); - Navigator.pop(context); - }, - child: Container( - height: 40, - alignment: Alignment.center, - color: OTLColor.pinksMain, - child: Text( - 'common.delete'.tr(), - style: TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.w700, - ), - ), - ), - ), - ), - ], - ) - ], - ), - ), - ), - ), - ); - } } diff --git a/lib/pages/user_page.dart b/lib/pages/user_page.dart index 119f097a..6febee39 100644 --- a/lib/pages/user_page.dart +++ b/lib/pages/user_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:otlplus/constants/text_styles.dart'; @@ -5,6 +7,7 @@ import 'package:otlplus/providers/auth_model.dart'; import 'package:otlplus/utils/build_app_bar.dart'; import 'package:otlplus/utils/build_page_route.dart'; import 'package:otlplus/utils/responsive_button.dart'; +import 'package:otlplus/widgets/delete_dialog.dart'; import 'package:provider/provider.dart'; import 'package:otlplus/constants/color.dart'; import 'package:otlplus/providers/info_model.dart'; @@ -57,22 +60,37 @@ class UserPage extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 16.0), child: _buildDivider(), ), - Align( - alignment: Alignment.centerLeft, - child: IconTextButton( - icon: 'assets/icons/logout.svg', - onTap: () { - context.read().logout(); - context.read().logout(); - Navigator.pop(context); + _buildAccount( + 'assets/icons/logout.svg', + () { + context.read().logout(); + context.read().logout(); + Navigator.pop(context); + }, + 'user.logout'.tr(), + ), + if (Platform.isIOS) + _buildAccount( + Icons.highlight_off, + () async { + showGeneralDialog( + context: context, + barrierColor: Colors.black.withOpacity(0.2), + barrierDismissible: true, + barrierLabel: MaterialLocalizations.of(context) + .modalBarrierDismissLabel, + pageBuilder: (context, _, __) => DeleteDialog( + text: 'user.ask_delete_account'.tr(), + onDelete: () { + context.read().logout(); + context.read().deleteAccount(); + Navigator.pop(context); + }, + ), + ); }, - text: 'user.logout'.tr(), - color: OTLColor.pinksMain, - textStyle: bodyBold, - spaceBetween: 8.0, - padding: EdgeInsets.symmetric(horizontal: 16.0), + 'user.delete_account'.tr(), ), - ), ], ), ), @@ -150,4 +168,19 @@ class UserPage extends StatelessWidget { onTap: onTap, ); } + + Widget _buildAccount(dynamic icon, void Function()? onTap, String? text) { + return Align( + alignment: Alignment.centerLeft, + child: IconTextButton( + icon: icon, + onTap: onTap, + text: text, + color: OTLColor.pinksMain, + textStyle: bodyBold, + spaceBetween: 8.0, + padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 6.0), + ), + ); + } } diff --git a/lib/providers/info_model.dart b/lib/providers/info_model.dart index 417fa749..711acc56 100644 --- a/lib/providers/info_model.dart +++ b/lib/providers/info_model.dart @@ -4,6 +4,7 @@ import 'package:otlplus/dio_provider.dart'; import 'package:otlplus/extensions/semester.dart'; import 'package:otlplus/models/semester.dart'; import 'package:otlplus/models/user.dart'; +import 'package:shared_preferences/shared_preferences.dart'; const USED_SCHEDULE_FIELDS = [ "beginning", @@ -77,16 +78,15 @@ class InfoModel extends ChangeNotifier { } Future getInfo() async { - // try { - _semesters = await getSemesters(); - _years = _semesters.map((semester) => semester.year).toSet(); - _user = await getUser(); - _currentSchedule = getCurrentSchedule(); - _hasData = true; + if ((await SharedPreferences.getInstance()).getBool('hasAccount') ?? + true) { + _semesters = await getSemesters(); + _years = _semesters.map((semester) => semester.year).toSet(); + _user = await getUser(); + _currentSchedule = getCurrentSchedule(); + _hasData = true; + } notifyListeners(); - // } catch (exception) { - // print(exception); - // } } Future> getSemesters() async { @@ -120,4 +120,11 @@ class InfoModel extends ChangeNotifier { return schedules.firstWhere((e) => e!["time"].isAfter(now), orElse: () => null); } + + Future deleteAccount() async { + final pref = await SharedPreferences.getInstance(); + pref.setBool('hasAccount', false); + _hasData = false; + notifyListeners(); + } } diff --git a/lib/widgets/delete_dialog.dart b/lib/widgets/delete_dialog.dart new file mode 100644 index 00000000..b5fd85db --- /dev/null +++ b/lib/widgets/delete_dialog.dart @@ -0,0 +1,83 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:otlplus/constants/color.dart'; + +class DeleteDialog extends StatelessWidget { + const DeleteDialog({Key? key, required this.text, this.onDelete}) + : super(key: key); + final String text; + final void Function()? onDelete; + + @override + Widget build(BuildContext context) { + return Center( + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Material( + child: IntrinsicWidth( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.fromLTRB(16, 19, 16, 20), + alignment: Alignment.center, + color: Colors.white, + child: Text( + text, + style: TextStyle( + fontSize: 12, + ), + ), + ), + Row( + children: [ + _buildButton( + () => Navigator.pop(context), + text: 'common.cancel'.tr(), + buttonColor: OTLColor.grayE, + ), + _buildButton( + () { + if (onDelete != null) onDelete!(); + Navigator.pop(context); + }, + text: 'common.delete'.tr(), + buttonColor: OTLColor.pinksMain, + textColor: Colors.white, + ), + ], + ) + ], + ), + ), + ), + ), + ); + } + + Widget _buildButton( + void Function()? onTap, { + required String text, + required Color buttonColor, + Color? textColor, + }) { + return Expanded( + child: GestureDetector( + onTap: onTap, + child: Container( + height: 40, + alignment: Alignment.center, + color: buttonColor, + child: Text( + text, + style: TextStyle( + color: textColor, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ), + ), + ); + } +}