diff --git a/.gitignore b/.gitignore index 7f38ad2..36ba336 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ key.properties .flutter-plugins* filcnaplo/ios/Flutter/flutter_export_environment 4.sh +filcnaplo/ios/Flutter/Generated 4.xcconfig diff --git a/filcnaplo/ios/Flutter/Generated 2.xcconfig b/filcnaplo/ios/Flutter/Generated 2.xcconfig deleted file mode 100644 index 194436b..0000000 --- a/filcnaplo/ios/Flutter/Generated 2.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=/Users/kima/src/flutter -FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo -COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_TARGET=lib/main.dart -FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=4.2.0 -FLUTTER_BUILD_NUMBER=220 -EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 -EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 -DART_OBFUSCATION=false -TRACK_WIDGET_CREATION=true -TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/filcnaplo/ios/Flutter/Generated 3.xcconfig b/filcnaplo/ios/Flutter/Generated 3.xcconfig deleted file mode 100644 index 194436b..0000000 --- a/filcnaplo/ios/Flutter/Generated 3.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=/Users/kima/src/flutter -FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo -COCOAPODS_PARALLEL_CODE_SIGN=true -FLUTTER_TARGET=lib/main.dart -FLUTTER_BUILD_DIR=build -FLUTTER_BUILD_NAME=4.2.0 -FLUTTER_BUILD_NUMBER=220 -EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 -EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 -DART_OBFUSCATION=false -TRACK_WIDGET_CREATION=true -TREE_SHAKE_ICONS=false -PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/filcnaplo/ios/Flutter/flutter_export_environment 2.sh b/filcnaplo/ios/Flutter/flutter_export_environment 2.sh deleted file mode 100755 index 276363d..0000000 --- a/filcnaplo/ios/Flutter/flutter_export_environment 2.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/kima/src/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib/main.dart" -export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=4.2.0" -export "FLUTTER_BUILD_NUMBER=220" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=true" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/filcnaplo/ios/Flutter/flutter_export_environment 3.sh b/filcnaplo/ios/Flutter/flutter_export_environment 3.sh deleted file mode 100755 index 276363d..0000000 --- a/filcnaplo/ios/Flutter/flutter_export_environment 3.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=/Users/kima/src/flutter" -export "FLUTTER_APPLICATION_PATH=/Users/kima/Documents/refilc/app/naplo/filcnaplo" -export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib/main.dart" -export "FLUTTER_BUILD_DIR=build" -export "FLUTTER_BUILD_NAME=4.2.0" -export "FLUTTER_BUILD_NUMBER=220" -export "DART_OBFUSCATION=false" -export "TRACK_WIDGET_CREATION=true" -export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/filcnaplo/lib/api/client.dart b/filcnaplo/lib/api/client.dart index 6de525e..bf290be 100644 --- a/filcnaplo/lib/api/client.dart +++ b/filcnaplo/lib/api/client.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:developer'; @@ -6,6 +7,7 @@ import 'package:filcnaplo/models/config.dart'; import 'package:filcnaplo/models/news.dart'; import 'package:filcnaplo/models/release.dart'; import 'package:filcnaplo/models/settings.dart'; +import 'package:filcnaplo/models/shared_theme.dart'; import 'package:filcnaplo/models/supporter.dart'; import 'package:filcnaplo_kreta_api/models/school.dart'; import 'package:flutter/foundation.dart'; @@ -13,15 +15,18 @@ import 'package:http/http.dart' as http; import 'package:connectivity_plus/connectivity_plus.dart'; class FilcAPI { + // API base + static const baseUrl = "https://api.refilc.hu"; + // Public API - static const schoolList = "https://api.refilc.hu/v1/public/school-list"; - static const news = "https://api.refilc.hu/v1/public/news"; - static const supporters = "https://api.refilc.hu/v1/public/supporters"; + static const schoolList = "$baseUrl/v1/public/school-list"; + static const news = "$baseUrl/v1/public/news"; + static const supporters = "$baseUrl/v1/public/supporters"; // Private API - static const ads = "https://api.refilc.hu/v1/private/ads"; - static const config = "https://api.refilc.hu/v1/private/config"; - static const reportApi = "https://api.refilc.hu/v1/private/crash-report"; + static const ads = "$baseUrl/v1/private/ads"; + static const config = "$baseUrl/v1/private/config"; + static const reportApi = "$baseUrl/v1/private/crash-report"; static const premiumApi = "https://api.filcnaplo.hu/premium/activate"; // static const premiumScopesApi = "https://api.filcnaplo.hu/premium/scopes"; @@ -29,6 +34,12 @@ class FilcAPI { static const repo = "refilc/naplo"; static const releases = "https://api.github.com/repos/$repo/releases"; + // Share API + static const themeShare = "$baseUrl/v2/shared/theme/add"; + static const themeGet = "$baseUrl/v2/shared/theme/get"; + static const allThemes = "$themeGet/all"; + static const themeByID = "$themeGet/"; + static Future checkConnectivity() async => (await Connectivity().checkConnectivity()) != ConnectivityResult.none; @@ -183,6 +194,46 @@ class FilcAPI { log("ERROR: FilcAPI.sendReport: $error $stacktrace"); } } + + // sharing + static Future addSharedTheme(SharedTheme theme) async { + try { + theme.json.remove('json'); + theme.json['is_public'] = theme.isPublic.toString(); + theme.json['background_color'] = theme.backgroundColor.value.toString(); + theme.json['panels_color'] = theme.panelsColor.value.toString(); + theme.json['accent_color'] = theme.accentColor.value.toString(); + + http.Response res = await http.post( + Uri.parse(themeShare), + body: theme.json, + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + ); + + if (res.statusCode != 201) { + throw "HTTP ${res.statusCode}: ${res.body}"; + } + + log('Shared theme successfully with ID: ${theme.id}'); + } on Exception catch (error, stacktrace) { + log("ERROR: FilcAPI.addSharedTheme: $error $stacktrace"); + } + } + + static Future getSharedTheme(String id) async { + try { + http.Response res = await http.get(Uri.parse(themeByID + id)); + + if (res.statusCode == 200) { + return (jsonDecode(res.body) as Map); + } else { + throw "HTTP ${res.statusCode}: ${res.body}"; + } + } on Exception catch (error, stacktrace) { + log("ERROR: FilcAPI.addSharedTheme: $error $stacktrace"); + } + return null; + } } class ErrorReport { diff --git a/filcnaplo/lib/app.dart b/filcnaplo/lib/app.dart index 717b3b1..32ecb6d 100644 --- a/filcnaplo/lib/app.dart +++ b/filcnaplo/lib/app.dart @@ -14,6 +14,7 @@ import 'package:filcnaplo/theme/theme.dart'; import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; import 'package:filcnaplo_premium/providers/goal_provider.dart'; +import 'package:filcnaplo_premium/providers/share_provider.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -99,49 +100,78 @@ class App extends StatelessWidget { Provider(create: (_) => kreta), Provider(create: (context) => database), ChangeNotifierProvider( - create: (context) => - ThemeModeObserver(initialTheme: settings.theme)), + create: (context) => ThemeModeObserver( + initialTheme: settings.theme, + ), + ), ChangeNotifierProvider( - create: (context) => NewsProvider(context: context)), + create: (context) => NewsProvider(context: context), + ), ChangeNotifierProvider( - create: (context) => UpdateProvider(context: context)), + create: (context) => UpdateProvider(context: context), + ), ChangeNotifierProvider( - create: (context) => AdProvider(context: context)), + create: (context) => AdProvider(context: context), + ), // user data (kreten) providers ChangeNotifierProvider( - create: (_) => GradeProvider( - settings: settings, - user: user, - database: database, - kreta: kreta)), + create: (_) => GradeProvider( + settings: settings, + user: user, + database: database, + kreta: kreta, + ), + ), ChangeNotifierProvider(create: (_) => timetable), ChangeNotifierProvider( - create: (context) => ExamProvider(context: context)), + create: (context) => ExamProvider(context: context), + ), ChangeNotifierProvider( - create: (context) => - HomeworkProvider(context: context, database: database)), + create: (context) => HomeworkProvider( + context: context, + database: database, + ), + ), ChangeNotifierProvider( - create: (context) => MessageProvider(context: context)), + create: (context) => MessageProvider(context: context), + ), ChangeNotifierProvider( - create: (context) => NoteProvider(context: context)), + create: (context) => NoteProvider(context: context), + ), ChangeNotifierProvider( - create: (context) => EventProvider(context: context)), + create: (context) => EventProvider(context: context), + ), ChangeNotifierProvider( - create: (context) => AbsenceProvider(context: context)), + create: (context) => AbsenceProvider(context: context), + ), // other providers ChangeNotifierProvider( - create: (_) => GradeCalculatorProvider( - settings: settings, - user: user, - database: database, - kreta: kreta)), + create: (_) => GradeCalculatorProvider( + settings: settings, + user: user, + database: database, + kreta: kreta, + ), + ), ChangeNotifierProvider( - create: (context) => - LiveCardProvider(timetable: timetable, settings: settings)), + create: (context) => LiveCardProvider( + timetable: timetable, + settings: settings, + ), + ), ChangeNotifierProvider( - create: (context) => GoalProvider(database: database, user: user)), + create: (context) => GoalProvider( + database: database, + user: user, + ), + ), + ChangeNotifierProvider( + create: (context) => ShareProvider( + user: user, + ), + ), ], child: Consumer( builder: (context, themeMode, child) { diff --git a/filcnaplo/lib/models/config.dart b/filcnaplo/lib/models/config.dart index 510ce22..1cc2557 100644 --- a/filcnaplo/lib/models/config.dart +++ b/filcnaplo/lib/models/config.dart @@ -3,18 +3,22 @@ import 'dart:io'; class Config { String _userAgent; Map? json; - static const String _version = String.fromEnvironment("APPVER", defaultValue: "2.2.0"); + static const String _version = + String.fromEnvironment("APPVER", defaultValue: "3.0.4"); Config({required String userAgent, this.json}) : _userAgent = userAgent; factory Config.fromJson(Map json) { return Config( - userAgent: json["user_agent"] ?? "hu.refilc.naplo/\$0/\$1/\$2", + userAgent: json["user_agent"] ?? "hu.ekreta.student/\$0/\$1/\$2", json: json, ); } - String get userAgent => _userAgent.replaceAll("\$0", _version).replaceAll("\$1", platform).replaceAll("\$2", "0"); + String get userAgent => _userAgent + .replaceAll("\$0", _version) + .replaceAll("\$1", platform) + .replaceAll("\$2", "0"); static String get platform { if (Platform.isAndroid) { diff --git a/filcnaplo/lib/models/settings.dart b/filcnaplo/lib/models/settings.dart index 9f59887..3e42b26 100644 --- a/filcnaplo/lib/models/settings.dart +++ b/filcnaplo/lib/models/settings.dart @@ -313,7 +313,7 @@ class SettingsProvider extends ChangeNotifier { bellDelay: 0, gradeOpeningFun: true, iconPack: IconPack.cupertino, - customAccentColor: const Color(0xff20AC9B), + customAccentColor: const Color(0xff3D7BF4), customBackgroundColor: const Color(0xff000000), customHighlightColor: const Color(0xff222222), premiumScopes: [PremiumScopes.all], diff --git a/filcnaplo/lib/models/shared_theme.dart b/filcnaplo/lib/models/shared_theme.dart new file mode 100644 index 0000000..5c3486c --- /dev/null +++ b/filcnaplo/lib/models/shared_theme.dart @@ -0,0 +1,33 @@ +import 'dart:ui'; + +class SharedTheme { + Map json; + String id; + bool isPublic; + String nickname; + Color backgroundColor; + Color panelsColor; + Color accentColor; + + SharedTheme({ + required this.json, + required this.id, + this.isPublic = false, + this.nickname = 'Anonymous', + required this.backgroundColor, + required this.panelsColor, + required this.accentColor, + }); + + factory SharedTheme.fromJson(Map json) { + return SharedTheme( + json: json, + id: json['public_id'], + isPublic: json['is_public'] ?? false, + nickname: json['nickname'] ?? 'Anonymous', + backgroundColor: Color(json['background_color']), + panelsColor: Color(json['panels_color']), + accentColor: Color(json['accent_color']), + ); + } +} diff --git a/filcnaplo_mobile_ui/lib/screens/login/login_input.dart b/filcnaplo_mobile_ui/lib/screens/login/login_input.dart index 08fc8eb..68088a3 100755 --- a/filcnaplo_mobile_ui/lib/screens/login/login_input.dart +++ b/filcnaplo_mobile_ui/lib/screens/login/login_input.dart @@ -4,7 +4,13 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart'; enum LoginInputStyle { username, password, school } class LoginInput extends StatefulWidget { - const LoginInput({Key? key, required this.style, this.controller, this.focusNode, this.onClear}) : super(key: key); + const LoginInput( + {Key? key, + required this.style, + this.controller, + this.focusNode, + this.onClear}) + : super(key: key); final Function()? onClear; final LoginInputStyle style; @@ -59,8 +65,10 @@ class _LoginInputState extends State { borderRadius: BorderRadius.circular(12.0), borderSide: const BorderSide(width: 0, color: Colors.transparent), ), - suffixIconConstraints: const BoxConstraints(maxHeight: 42.0, maxWidth: 48.0), - suffixIcon: widget.style == LoginInputStyle.password || widget.style == LoginInputStyle.school + suffixIconConstraints: + const BoxConstraints(maxHeight: 42.0, maxWidth: 48.0), + suffixIcon: widget.style == LoginInputStyle.password || + widget.style == LoginInputStyle.school ? ClipOval( child: Material( type: MaterialType.transparency, diff --git a/filcnaplo_mobile_ui/pubspec.yaml b/filcnaplo_mobile_ui/pubspec.yaml index 498f473..fdfc57d 100644 --- a/filcnaplo_mobile_ui/pubspec.yaml +++ b/filcnaplo_mobile_ui/pubspec.yaml @@ -46,6 +46,7 @@ dependencies: rounded_expansion_tile: git: url: https://github.com/kimaah/rounded_expansion_tile.git + go_router: ^10.1.2 dev_dependencies: flutter_lints: ^1.0.0 diff --git a/filcnaplo_premium/lib/providers/share_provider.dart b/filcnaplo_premium/lib/providers/share_provider.dart new file mode 100644 index 0000000..8407e43 --- /dev/null +++ b/filcnaplo_premium/lib/providers/share_provider.dart @@ -0,0 +1,58 @@ +import 'package:filcnaplo/api/client.dart'; +import 'package:filcnaplo/api/providers/user_provider.dart'; +import 'package:filcnaplo/models/settings.dart'; +import 'package:filcnaplo/models/shared_theme.dart'; +// import 'package:filcnaplo/models/shared_theme.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; +import 'package:uuid/uuid.dart'; + +class ShareProvider extends ChangeNotifier { + final UserProvider _user; + + ShareProvider({ + required UserProvider user, + }) : _user = user; + + // Future shareTheme({required SharedTheme theme}) async { + + // } + Future shareCurrentTheme(BuildContext context, + {bool isPublic = false, bool shareNick = true}) async { + final SettingsProvider settings = + Provider.of(context, listen: false); + + Map themeJson = { + 'public_id': const Uuid().v4(), + 'is_public': isPublic, + 'nickname': shareNick ? _user.nickname : 'Anonymous', + 'background_color': (settings.customBackgroundColor ?? + SettingsProvider.defaultSettings().customBackgroundColor) + ?.value, + 'panels_color': (settings.customHighlightColor ?? + SettingsProvider.defaultSettings().customHighlightColor) + ?.value, + 'accent_color': (settings.customAccentColor ?? + SettingsProvider.defaultSettings().customAccentColor) + ?.value ?? + const Color(0xFF3D7BF4).value, + }; + + SharedTheme theme = SharedTheme.fromJson(themeJson); + FilcAPI.addSharedTheme(theme); + + return theme; + } + + Future getThemeById(BuildContext context, + {required String id}) async { + Map? themeJson = await FilcAPI.getSharedTheme(id); + + if (themeJson != null) { + SharedTheme theme = SharedTheme.fromJson(themeJson); + return theme; + } + + return null; + } +} diff --git a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colorpicker.dart b/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colorpicker.dart index ba3838e..d10c6ca 100644 --- a/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colorpicker.dart +++ b/filcnaplo_premium/lib/ui/mobile/flutter_colorpicker/colorpicker.dart @@ -9,6 +9,9 @@ library hsv_picker; +import 'package:filcnaplo/models/shared_theme.dart'; +import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart'; +import 'package:filcnaplo_premium/providers/share_provider.dart'; import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/block_picker.dart'; import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/palette.dart'; import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/utils.dart'; @@ -17,6 +20,7 @@ import 'package:filcnaplo_premium/ui/mobile/settings/theme.i18n.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; +import 'package:provider/provider.dart'; class FilcColorPicker extends StatefulWidget { const FilcColorPicker({ @@ -48,6 +52,7 @@ class FilcColorPicker extends StatefulWidget { this.hexInputController, this.colorHistory, this.onHistoryChanged, + required this.onThemeIdProvided, }) : super(key: key); final CustomColorMode colorMode; @@ -70,12 +75,17 @@ class FilcColorPicker extends StatefulWidget { final TextEditingController? hexInputController; final List? colorHistory; final ValueChanged>? onHistoryChanged; + final void Function(SharedTheme theme) onThemeIdProvided; @override _FilcColorPickerState createState() => _FilcColorPickerState(); } class _FilcColorPickerState extends State { + final idController = TextEditingController(); + + late final ShareProvider shareProvider; + HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0); List colorHistory = []; bool isAdvancedView = false; @@ -98,6 +108,7 @@ class _FilcColorPickerState extends State { if (widget.colorHistory != null && widget.onHistoryChanged != null) { colorHistory = widget.colorHistory ?? []; } + shareProvider = Provider.of(context, listen: false); super.initState(); } @@ -182,7 +193,8 @@ class _FilcColorPickerState extends State { widget.portraitOnly) { return Column( children: [ - if (widget.colorMode != CustomColorMode.theme) + if (widget.colorMode != CustomColorMode.theme && + widget.colorMode != CustomColorMode.enterId) Padding( padding: const EdgeInsets.only(top: 8.0), child: Column( @@ -215,7 +227,9 @@ class _FilcColorPickerState extends State { ], ), ), - if (isAdvancedView && widget.colorMode != CustomColorMode.theme) + if (isAdvancedView && + widget.colorMode != CustomColorMode.theme && + widget.colorMode != CustomColorMode.enterId) Padding( padding: const EdgeInsets.only(bottom: 6.0), child: ColorPickerInput( @@ -231,57 +245,105 @@ class _FilcColorPickerState extends State { embeddedText: false, ), ), - SizedBox( - height: 70 * (widget.colorMode == CustomColorMode.theme ? 2 : 1), - child: BlockPicker( - pickerColor: Colors.red, - layoutBuilder: (context, colors, child) { - return GridView.count( - shrinkWrap: true, - crossAxisCount: - widget.colorMode == CustomColorMode.theme ? 2 : 1, - scrollDirection: Axis.horizontal, - crossAxisSpacing: 15, - physics: const BouncingScrollPhysics(), - mainAxisSpacing: 15, - padding: const EdgeInsets.symmetric( - horizontal: 12.0, vertical: 8.0), - children: List.generate( - colors.toSet().length + - (widget.colorMode == CustomColorMode.theme ? 1 : 0), - (index) { - if (widget.colorMode == CustomColorMode.theme) { - if (index == 0) { - return GestureDetector( - onTap: () => widget.onColorChangeEnd( - Colors.transparent, - adaptive: true), - child: ColorIndicator( - HSVColor.fromColor( - const Color.fromARGB(255, 255, 238, 177)), - icon: CupertinoIcons.wand_stars, - currentHsvColor: currentHsvColor, - width: 30, - height: 30, - adaptive: true), + if (widget.colorMode == CustomColorMode.enterId) + Padding( + padding: + const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0), + child: Column( + children: [ + TextField( + autocorrect: false, + autofocus: true, + onEditingComplete: () async { + SharedTheme? theme = await shareProvider.getThemeById( + context, + id: idController.text.replaceAll(' ', ''), + ); + + if (theme != null) { + widget.onThemeIdProvided(theme); + } else { + ScaffoldMessenger.of(context).showSnackBar( + CustomSnackBar( + content: Text("theme_not_found".i18n, + style: const TextStyle(color: Colors.white)), + backgroundColor: AppColors.of(context).red, + context: context, + ), ); } - index--; - } - return GestureDetector( - onTap: () => widget.onColorChangeEnd(colors[index]), - child: ColorIndicator(HSVColor.fromColor(colors[index]), - currentHsvColor: currentHsvColor, - width: 30, - height: 30), - ); - }), - ); - }, - onColorChanged: (c) => {}, + }, + controller: idController, + decoration: InputDecoration( + hintText: 'theme_id'.i18n, + ), + ), + // MaterialActionButton( + // child: Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text('check_id'.i18n), + // ], + // ), + // backgroundColor: AppColors.of(context).filc, + // onPressed: () {}, + // ), + ], + ), ), - ), - if (widget.colorMode != CustomColorMode.theme) + if (widget.colorMode != CustomColorMode.enterId) + SizedBox( + height: 70 * (widget.colorMode == CustomColorMode.theme ? 2 : 1), + child: BlockPicker( + pickerColor: Colors.red, + layoutBuilder: (context, colors, child) { + return GridView.count( + shrinkWrap: true, + crossAxisCount: + widget.colorMode == CustomColorMode.theme ? 2 : 1, + scrollDirection: Axis.horizontal, + crossAxisSpacing: 15, + physics: const BouncingScrollPhysics(), + mainAxisSpacing: 15, + padding: const EdgeInsets.symmetric( + horizontal: 12.0, vertical: 8.0), + children: List.generate( + colors.toSet().length + + (widget.colorMode == CustomColorMode.theme ? 1 : 0), + (index) { + if (widget.colorMode == CustomColorMode.theme) { + if (index == 0) { + return GestureDetector( + onTap: () => widget.onColorChangeEnd( + Colors.transparent, + adaptive: true), + child: ColorIndicator( + HSVColor.fromColor( + const Color.fromARGB(255, 255, 238, 177)), + icon: CupertinoIcons.wand_stars, + currentHsvColor: currentHsvColor, + width: 30, + height: 30, + adaptive: true), + ); + } + index--; + } + return GestureDetector( + onTap: () => widget.onColorChangeEnd(colors[index]), + child: ColorIndicator(HSVColor.fromColor(colors[index]), + currentHsvColor: currentHsvColor, + width: 30, + height: 30), + ); + }), + ); + }, + onColorChanged: (c) => {}, + ), + ), + if (widget.colorMode != CustomColorMode.theme && + widget.colorMode != CustomColorMode.enterId) Material( color: Colors.transparent, child: InkWell( diff --git a/filcnaplo_premium/lib/ui/mobile/settings/share_theme.dart b/filcnaplo_premium/lib/ui/mobile/settings/share_theme.dart new file mode 100644 index 0000000..cbbe917 --- /dev/null +++ b/filcnaplo_premium/lib/ui/mobile/settings/share_theme.dart @@ -0,0 +1,26 @@ +import 'package:filcnaplo/models/settings.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class PremiumShareTheme extends StatefulWidget { + const PremiumShareTheme({Key? key}) : super(key: key); + + @override + State createState() => _PremiumShareThemeState(); +} + +class _PremiumShareThemeState extends State + with TickerProviderStateMixin { + late final SettingsProvider settingsProvider; + + @override + void initState() { + super.initState(); + settingsProvider = Provider.of(context, listen: false); + } + + @override + Widget build(BuildContext context) { + return const Scaffold(); + } +} diff --git a/filcnaplo_premium/lib/ui/mobile/settings/theme.dart b/filcnaplo_premium/lib/ui/mobile/settings/theme.dart index 8aaa65f..33cff7a 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/theme.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/theme.dart @@ -1,4 +1,5 @@ import 'package:filcnaplo/models/settings.dart'; +import 'package:filcnaplo/models/shared_theme.dart'; import 'package:filcnaplo/theme/colors/accent.dart'; import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/theme/observer.dart'; @@ -13,12 +14,15 @@ import 'package:filcnaplo_mobile_ui/common/widgets/grade/new_grades.dart'; import 'package:filcnaplo_mobile_ui/common/widgets/homework/homework_tile.dart'; import 'package:filcnaplo_premium/models/premium_scopes.dart'; import 'package:filcnaplo_premium/providers/premium_provider.dart'; +import 'package:filcnaplo_premium/providers/share_provider.dart'; import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/colorpicker.dart'; import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_feather_icons/flutter_feather_icons.dart'; import 'package:provider/provider.dart'; import 'theme.i18n.dart'; +import 'package:share_plus/share_plus.dart'; class PremiumCustomAccentColorSetting extends StatefulWidget { const PremiumCustomAccentColorSetting({Key? key}) : super(key: key); @@ -28,12 +32,20 @@ class PremiumCustomAccentColorSetting extends StatefulWidget { _PremiumCustomAccentColorSettingState(); } -enum CustomColorMode { theme, saved, accent, background, highlight } +enum CustomColorMode { + theme, + saved, + accent, + background, + highlight, + enterId, +} class _PremiumCustomAccentColorSettingState extends State with TickerProviderStateMixin { late final SettingsProvider settings; + late final ShareProvider shareProvider; bool colorSelection = false; bool customColorMenu = false; CustomColorMode colorMode = CustomColorMode.theme; @@ -95,9 +107,10 @@ class _PremiumCustomAccentColorSettingState @override void initState() { super.initState(); - _colorsTabController = TabController(length: 4, vsync: this); + _colorsTabController = TabController(length: 5, vsync: this); _testTabController = TabController(length: 4, vsync: this); settings = Provider.of(context, listen: false); + shareProvider = Provider.of(context, listen: false); _openAnimController = AnimationController( vsync: this, duration: const Duration(milliseconds: 750)); @@ -132,10 +145,14 @@ class _PremiumCustomAccentColorSettingState return settings.customHighlightColor; case CustomColorMode.accent: return settings.customAccentColor; + case CustomColorMode.enterId: + // do nothing here lol + break; } } - void updateCustomColor(dynamic v, bool store) { + void updateCustomColor(dynamic v, bool store, + {Color? accent, Color? background, Color? panels}) { if (colorMode != CustomColorMode.theme) { settings.update(accentColor: AccentColor.custom, store: store); } @@ -168,6 +185,11 @@ class _PremiumCustomAccentColorSettingState case CustomColorMode.accent: settings.update(customAccentColor: v, store: store); break; + case CustomColorMode.enterId: + settings.update(customBackgroundColor: background, store: store); + settings.update(customHighlightColor: panels, store: store); + settings.update(customAccentColor: accent, store: store); + break; } } @@ -230,6 +252,34 @@ class _PremiumCustomAccentColorSettingState appBar: AppBar( surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, leading: BackButton(color: AppColors.of(context).text), + actions: [ + Padding( + padding: const EdgeInsets.only(right: 10.0), + child: IconButton( + focusColor: Colors.transparent, + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + onPressed: () async { + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar( + // duration: Duration(milliseconds: 1000), + // content: Text( + // "Hamarosan...", + // ), + // ), + // ); + SharedTheme theme = + await shareProvider.shareCurrentTheme(context); + Share.share(theme.id, + subject: 'reFilc Téma / reFilc Theme'); + }, + icon: const Icon( + FeatherIcons.share2, + size: 22.0, + ), + ), + ), + ], title: Text( "theme_prev".i18n, style: TextStyle(color: AppColors.of(context).text), @@ -588,6 +638,9 @@ class _PremiumCustomAccentColorSettingState tab: Tab( text: "colorpicker_presets" .i18n)), + ColorTab( + color: unknownColor, + tab: Tab(text: "enter_id".i18n)), /*ColorTab( color: settings.customAccentColor ?? @@ -639,25 +692,31 @@ class _PremiumCustomAccentColorSettingState CustomColorMode.theme; }); break; + case 1: + setState(() { + colorMode = + CustomColorMode.enterId; + }); + break; /*case 1: setState(() { colorMode = CustomColorMode.saved; }); break;*/ - case 1: + case 2: setState(() { colorMode = CustomColorMode.background; }); break; - case 2: + case 3: setState(() { colorMode = CustomColorMode.highlight; }); break; - case 3: + case 4: setState(() { colorMode = CustomColorMode.accent; @@ -721,6 +780,19 @@ class _PremiumCustomAccentColorSettingState }); setTheme(settings.theme, true); }, + onThemeIdProvided: (theme) { + setState(() { + updateCustomColor( + null, + true, + accent: theme.accentColor, + background: + theme.backgroundColor, + panels: theme.panelsColor, + ); + }); + setTheme(settings.theme, true); + }, ), ), ), diff --git a/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart b/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart index 3ed502e..40ac863 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/theme.i18n.dart @@ -11,6 +11,9 @@ extension SettingsLocalization on String { "colorpicker_accent": "Accent", "need_sub": "You need Kupak subscription to use modify this.", "advanced": "Advanced", + "enter_id": "Enter ID", + "theme_id": "Theme ID...", + "theme_not_found": "Theme not found!", }, "hu_hu": { "theme_prev": "Előnézet", @@ -20,6 +23,9 @@ extension SettingsLocalization on String { "colorpicker_accent": "Színtónus", "need_sub": "A módosításhoz Kupak szintű támogatás szükséges.", "advanced": "Haladó", + "enter_id": "ID megadása", + "theme_id": "Téma azonosító...", + "theme_not_found": "A téma nem található!", }, "de_de": { "theme_prev": "Vorschau", @@ -30,6 +36,9 @@ extension SettingsLocalization on String { "need_sub": "Sie benötigen ein Kupak-Abonnement, um diese Funktion zu ändern.", "advanced": "Fortschrittlich", + "enter_id": "Geben Sie die ID ein", + "theme_id": "Themen-ID...", + "theme_not_found": "Thema nicht gefunden!", }, };