diff --git a/filcnaplo/ios/Flutter/Generated 5.xcconfig b/filcnaplo/ios/Flutter/Generated 5.xcconfig new file mode 100644 index 0000000..06ae94b --- /dev/null +++ b/filcnaplo/ios/Flutter/Generated 5.xcconfig @@ -0,0 +1,14 @@ +// 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.2 +FLUTTER_BUILD_NUMBER=222 +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 6.sh b/filcnaplo/ios/Flutter/flutter_export_environment 6.sh new file mode 100755 index 0000000..3d52119 --- /dev/null +++ b/filcnaplo/ios/Flutter/flutter_export_environment 6.sh @@ -0,0 +1,13 @@ +#!/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.2" +export "FLUTTER_BUILD_NUMBER=222" +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 187e29f..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'; @@ -35,6 +36,9 @@ class FilcAPI { // 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; @@ -194,15 +198,41 @@ class FilcAPI { // sharing static Future addSharedTheme(SharedTheme theme) async { try { - http.Response res = - await http.post(Uri.parse(themeShare), body: theme.json); + 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(); - if (res.statusCode != 200) { + 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; } } diff --git a/filcnaplo/lib/models/shared_theme.dart b/filcnaplo/lib/models/shared_theme.dart index 2e2c042..5c3486c 100644 --- a/filcnaplo/lib/models/shared_theme.dart +++ b/filcnaplo/lib/models/shared_theme.dart @@ -25,9 +25,9 @@ class SharedTheme { id: json['public_id'], isPublic: json['is_public'] ?? false, nickname: json['nickname'] ?? 'Anonymous', - backgroundColor: json['background_color'], - panelsColor: json['panels_color'], - accentColor: json['accent_color'], + 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 index b652cea..2ba8c3f 100644 --- a/filcnaplo_premium/lib/providers/share_provider.dart +++ b/filcnaplo_premium/lib/providers/share_provider.dart @@ -19,7 +19,8 @@ class ShareProvider extends ChangeNotifier { // } Future shareCurrentTheme(BuildContext context, {bool isPublic = false, bool shareNick = true}) async { - final SettingsProvider settings = Provider.of(context); + final SettingsProvider settings = + Provider.of(context, listen: false); Map themeJson = { 'public_id': const Uuid().v4(), @@ -41,4 +42,16 @@ class ShareProvider extends ChangeNotifier { 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/theme.dart b/filcnaplo_premium/lib/ui/mobile/settings/theme.dart index d8ba210..33cff7a 100644 --- a/filcnaplo_premium/lib/ui/mobile/settings/theme.dart +++ b/filcnaplo_premium/lib/ui/mobile/settings/theme.dart @@ -22,6 +22,7 @@ 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); @@ -31,7 +32,14 @@ class PremiumCustomAccentColorSetting extends StatefulWidget { _PremiumCustomAccentColorSettingState(); } -enum CustomColorMode { theme, saved, accent, background, highlight } +enum CustomColorMode { + theme, + saved, + accent, + background, + highlight, + enterId, +} class _PremiumCustomAccentColorSettingState extends State @@ -99,7 +107,7 @@ 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); @@ -137,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); } @@ -173,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; } } @@ -253,7 +270,8 @@ class _PremiumCustomAccentColorSettingState // ); SharedTheme theme = await shareProvider.shareCurrentTheme(context); - print(theme.id); + Share.share(theme.id, + subject: 'reFilc Téma / reFilc Theme'); }, icon: const Icon( FeatherIcons.share2, @@ -620,6 +638,9 @@ class _PremiumCustomAccentColorSettingState tab: Tab( text: "colorpicker_presets" .i18n)), + ColorTab( + color: unknownColor, + tab: Tab(text: "enter_id".i18n)), /*ColorTab( color: settings.customAccentColor ?? @@ -671,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; @@ -753,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!", }, };