Merge pull request #49 from refilc/dev

Dev
This commit is contained in:
Márton Kiss 2023-09-17 14:15:27 +02:00 committed by GitHub
commit 19691a1dc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 449 additions and 148 deletions

1
.gitignore vendored
View File

@ -49,3 +49,4 @@ key.properties
.flutter-plugins* .flutter-plugins*
filcnaplo/ios/Flutter/flutter_export_environment 4.sh filcnaplo/ios/Flutter/flutter_export_environment 4.sh
filcnaplo/ios/Flutter/Generated 4.xcconfig

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
@ -6,6 +7,7 @@ import 'package:filcnaplo/models/config.dart';
import 'package:filcnaplo/models/news.dart'; import 'package:filcnaplo/models/news.dart';
import 'package:filcnaplo/models/release.dart'; import 'package:filcnaplo/models/release.dart';
import 'package:filcnaplo/models/settings.dart'; import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/models/shared_theme.dart';
import 'package:filcnaplo/models/supporter.dart'; import 'package:filcnaplo/models/supporter.dart';
import 'package:filcnaplo_kreta_api/models/school.dart'; import 'package:filcnaplo_kreta_api/models/school.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -13,15 +15,18 @@ import 'package:http/http.dart' as http;
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
class FilcAPI { class FilcAPI {
// API base
static const baseUrl = "https://api.refilc.hu";
// Public API // Public API
static const schoolList = "https://api.refilc.hu/v1/public/school-list"; static const schoolList = "$baseUrl/v1/public/school-list";
static const news = "https://api.refilc.hu/v1/public/news"; static const news = "$baseUrl/v1/public/news";
static const supporters = "https://api.refilc.hu/v1/public/supporters"; static const supporters = "$baseUrl/v1/public/supporters";
// Private API // Private API
static const ads = "https://api.refilc.hu/v1/private/ads"; static const ads = "$baseUrl/v1/private/ads";
static const config = "https://api.refilc.hu/v1/private/config"; static const config = "$baseUrl/v1/private/config";
static const reportApi = "https://api.refilc.hu/v1/private/crash-report"; static const reportApi = "$baseUrl/v1/private/crash-report";
static const premiumApi = "https://api.filcnaplo.hu/premium/activate"; static const premiumApi = "https://api.filcnaplo.hu/premium/activate";
// static const premiumScopesApi = "https://api.filcnaplo.hu/premium/scopes"; // static const premiumScopesApi = "https://api.filcnaplo.hu/premium/scopes";
@ -29,6 +34,12 @@ class FilcAPI {
static const repo = "refilc/naplo"; static const repo = "refilc/naplo";
static const releases = "https://api.github.com/repos/$repo/releases"; 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<bool> checkConnectivity() async => static Future<bool> checkConnectivity() async =>
(await Connectivity().checkConnectivity()) != ConnectivityResult.none; (await Connectivity().checkConnectivity()) != ConnectivityResult.none;
@ -183,6 +194,46 @@ class FilcAPI {
log("ERROR: FilcAPI.sendReport: $error $stacktrace"); log("ERROR: FilcAPI.sendReport: $error $stacktrace");
} }
} }
// sharing
static Future<void> 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<Map?> 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 { class ErrorReport {

View File

@ -14,6 +14,7 @@ import 'package:filcnaplo/theme/theme.dart';
import 'package:filcnaplo_kreta_api/client/client.dart'; import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart'; import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_premium/providers/goal_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/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -99,49 +100,78 @@ class App extends StatelessWidget {
Provider<KretaClient>(create: (_) => kreta), Provider<KretaClient>(create: (_) => kreta),
Provider<DatabaseProvider>(create: (context) => database), Provider<DatabaseProvider>(create: (context) => database),
ChangeNotifierProvider<ThemeModeObserver>( ChangeNotifierProvider<ThemeModeObserver>(
create: (context) => create: (context) => ThemeModeObserver(
ThemeModeObserver(initialTheme: settings.theme)), initialTheme: settings.theme,
),
),
ChangeNotifierProvider<NewsProvider>( ChangeNotifierProvider<NewsProvider>(
create: (context) => NewsProvider(context: context)), create: (context) => NewsProvider(context: context),
),
ChangeNotifierProvider<UpdateProvider>( ChangeNotifierProvider<UpdateProvider>(
create: (context) => UpdateProvider(context: context)), create: (context) => UpdateProvider(context: context),
),
ChangeNotifierProvider<AdProvider>( ChangeNotifierProvider<AdProvider>(
create: (context) => AdProvider(context: context)), create: (context) => AdProvider(context: context),
),
// user data (kreten) providers // user data (kreten) providers
ChangeNotifierProvider<GradeProvider>( ChangeNotifierProvider<GradeProvider>(
create: (_) => GradeProvider( create: (_) => GradeProvider(
settings: settings, settings: settings,
user: user, user: user,
database: database, database: database,
kreta: kreta)), kreta: kreta,
),
),
ChangeNotifierProvider<TimetableProvider>(create: (_) => timetable), ChangeNotifierProvider<TimetableProvider>(create: (_) => timetable),
ChangeNotifierProvider<ExamProvider>( ChangeNotifierProvider<ExamProvider>(
create: (context) => ExamProvider(context: context)), create: (context) => ExamProvider(context: context),
),
ChangeNotifierProvider<HomeworkProvider>( ChangeNotifierProvider<HomeworkProvider>(
create: (context) => create: (context) => HomeworkProvider(
HomeworkProvider(context: context, database: database)), context: context,
database: database,
),
),
ChangeNotifierProvider<MessageProvider>( ChangeNotifierProvider<MessageProvider>(
create: (context) => MessageProvider(context: context)), create: (context) => MessageProvider(context: context),
),
ChangeNotifierProvider<NoteProvider>( ChangeNotifierProvider<NoteProvider>(
create: (context) => NoteProvider(context: context)), create: (context) => NoteProvider(context: context),
),
ChangeNotifierProvider<EventProvider>( ChangeNotifierProvider<EventProvider>(
create: (context) => EventProvider(context: context)), create: (context) => EventProvider(context: context),
),
ChangeNotifierProvider<AbsenceProvider>( ChangeNotifierProvider<AbsenceProvider>(
create: (context) => AbsenceProvider(context: context)), create: (context) => AbsenceProvider(context: context),
),
// other providers // other providers
ChangeNotifierProvider<GradeCalculatorProvider>( ChangeNotifierProvider<GradeCalculatorProvider>(
create: (_) => GradeCalculatorProvider( create: (_) => GradeCalculatorProvider(
settings: settings, settings: settings,
user: user, user: user,
database: database, database: database,
kreta: kreta)), kreta: kreta,
),
),
ChangeNotifierProvider<LiveCardProvider>( ChangeNotifierProvider<LiveCardProvider>(
create: (context) => create: (context) => LiveCardProvider(
LiveCardProvider(timetable: timetable, settings: settings)), timetable: timetable,
settings: settings,
),
),
ChangeNotifierProvider<GoalProvider>( ChangeNotifierProvider<GoalProvider>(
create: (context) => GoalProvider(database: database, user: user)), create: (context) => GoalProvider(
database: database,
user: user,
),
),
ChangeNotifierProvider<ShareProvider>(
create: (context) => ShareProvider(
user: user,
),
),
], ],
child: Consumer<ThemeModeObserver>( child: Consumer<ThemeModeObserver>(
builder: (context, themeMode, child) { builder: (context, themeMode, child) {

View File

@ -3,18 +3,22 @@ import 'dart:io';
class Config { class Config {
String _userAgent; String _userAgent;
Map? json; 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; Config({required String userAgent, this.json}) : _userAgent = userAgent;
factory Config.fromJson(Map json) { factory Config.fromJson(Map json) {
return Config( return Config(
userAgent: json["user_agent"] ?? "hu.refilc.naplo/\$0/\$1/\$2", userAgent: json["user_agent"] ?? "hu.ekreta.student/\$0/\$1/\$2",
json: json, 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 { static String get platform {
if (Platform.isAndroid) { if (Platform.isAndroid) {

View File

@ -313,7 +313,7 @@ class SettingsProvider extends ChangeNotifier {
bellDelay: 0, bellDelay: 0,
gradeOpeningFun: true, gradeOpeningFun: true,
iconPack: IconPack.cupertino, iconPack: IconPack.cupertino,
customAccentColor: const Color(0xff20AC9B), customAccentColor: const Color(0xff3D7BF4),
customBackgroundColor: const Color(0xff000000), customBackgroundColor: const Color(0xff000000),
customHighlightColor: const Color(0xff222222), customHighlightColor: const Color(0xff222222),
premiumScopes: [PremiumScopes.all], premiumScopes: [PremiumScopes.all],

View File

@ -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']),
);
}
}

View File

@ -4,7 +4,13 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart';
enum LoginInputStyle { username, password, school } enum LoginInputStyle { username, password, school }
class LoginInput extends StatefulWidget { 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 Function()? onClear;
final LoginInputStyle style; final LoginInputStyle style;
@ -59,8 +65,10 @@ class _LoginInputState extends State<LoginInput> {
borderRadius: BorderRadius.circular(12.0), borderRadius: BorderRadius.circular(12.0),
borderSide: const BorderSide(width: 0, color: Colors.transparent), borderSide: const BorderSide(width: 0, color: Colors.transparent),
), ),
suffixIconConstraints: const BoxConstraints(maxHeight: 42.0, maxWidth: 48.0), suffixIconConstraints:
suffixIcon: widget.style == LoginInputStyle.password || widget.style == LoginInputStyle.school const BoxConstraints(maxHeight: 42.0, maxWidth: 48.0),
suffixIcon: widget.style == LoginInputStyle.password ||
widget.style == LoginInputStyle.school
? ClipOval( ? ClipOval(
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,

View File

@ -46,6 +46,7 @@ dependencies:
rounded_expansion_tile: rounded_expansion_tile:
git: git:
url: https://github.com/kimaah/rounded_expansion_tile.git url: https://github.com/kimaah/rounded_expansion_tile.git
go_router: ^10.1.2
dev_dependencies: dev_dependencies:
flutter_lints: ^1.0.0 flutter_lints: ^1.0.0

View File

@ -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<void> shareTheme({required SharedTheme theme}) async {
// }
Future<SharedTheme> shareCurrentTheme(BuildContext context,
{bool isPublic = false, bool shareNick = true}) async {
final SettingsProvider settings =
Provider.of<SettingsProvider>(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<SharedTheme?> 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;
}
}

View File

@ -9,6 +9,9 @@
library hsv_picker; 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/block_picker.dart';
import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/palette.dart'; import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/palette.dart';
import 'package:filcnaplo_premium/ui/mobile/flutter_colorpicker/utils.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/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:provider/provider.dart';
class FilcColorPicker extends StatefulWidget { class FilcColorPicker extends StatefulWidget {
const FilcColorPicker({ const FilcColorPicker({
@ -48,6 +52,7 @@ class FilcColorPicker extends StatefulWidget {
this.hexInputController, this.hexInputController,
this.colorHistory, this.colorHistory,
this.onHistoryChanged, this.onHistoryChanged,
required this.onThemeIdProvided,
}) : super(key: key); }) : super(key: key);
final CustomColorMode colorMode; final CustomColorMode colorMode;
@ -70,12 +75,17 @@ class FilcColorPicker extends StatefulWidget {
final TextEditingController? hexInputController; final TextEditingController? hexInputController;
final List<Color>? colorHistory; final List<Color>? colorHistory;
final ValueChanged<List<Color>>? onHistoryChanged; final ValueChanged<List<Color>>? onHistoryChanged;
final void Function(SharedTheme theme) onThemeIdProvided;
@override @override
_FilcColorPickerState createState() => _FilcColorPickerState(); _FilcColorPickerState createState() => _FilcColorPickerState();
} }
class _FilcColorPickerState extends State<FilcColorPicker> { class _FilcColorPickerState extends State<FilcColorPicker> {
final idController = TextEditingController();
late final ShareProvider shareProvider;
HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0); HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0);
List<Color> colorHistory = []; List<Color> colorHistory = [];
bool isAdvancedView = false; bool isAdvancedView = false;
@ -98,6 +108,7 @@ class _FilcColorPickerState extends State<FilcColorPicker> {
if (widget.colorHistory != null && widget.onHistoryChanged != null) { if (widget.colorHistory != null && widget.onHistoryChanged != null) {
colorHistory = widget.colorHistory ?? []; colorHistory = widget.colorHistory ?? [];
} }
shareProvider = Provider.of<ShareProvider>(context, listen: false);
super.initState(); super.initState();
} }
@ -182,7 +193,8 @@ class _FilcColorPickerState extends State<FilcColorPicker> {
widget.portraitOnly) { widget.portraitOnly) {
return Column( return Column(
children: [ children: [
if (widget.colorMode != CustomColorMode.theme) if (widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
child: Column( child: Column(
@ -215,7 +227,9 @@ class _FilcColorPickerState extends State<FilcColorPicker> {
], ],
), ),
), ),
if (isAdvancedView && widget.colorMode != CustomColorMode.theme) if (isAdvancedView &&
widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Padding( Padding(
padding: const EdgeInsets.only(bottom: 6.0), padding: const EdgeInsets.only(bottom: 6.0),
child: ColorPickerInput( child: ColorPickerInput(
@ -231,57 +245,105 @@ class _FilcColorPickerState extends State<FilcColorPicker> {
embeddedText: false, embeddedText: false,
), ),
), ),
SizedBox( if (widget.colorMode == CustomColorMode.enterId)
height: 70 * (widget.colorMode == CustomColorMode.theme ? 2 : 1), Padding(
child: BlockPicker( padding:
pickerColor: Colors.red, const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
layoutBuilder: (context, colors, child) { child: Column(
return GridView.count( children: [
shrinkWrap: true, TextField(
crossAxisCount: autocorrect: false,
widget.colorMode == CustomColorMode.theme ? 2 : 1, autofocus: true,
scrollDirection: Axis.horizontal, onEditingComplete: () async {
crossAxisSpacing: 15, SharedTheme? theme = await shareProvider.getThemeById(
physics: const BouncingScrollPhysics(), context,
mainAxisSpacing: 15, id: idController.text.replaceAll(' ', ''),
padding: const EdgeInsets.symmetric( );
horizontal: 12.0, vertical: 8.0),
children: List.generate( if (theme != null) {
colors.toSet().length + widget.onThemeIdProvided(theme);
(widget.colorMode == CustomColorMode.theme ? 1 : 0), } else {
(index) { ScaffoldMessenger.of(context).showSnackBar(
if (widget.colorMode == CustomColorMode.theme) { CustomSnackBar(
if (index == 0) { content: Text("theme_not_found".i18n,
return GestureDetector( style: const TextStyle(color: Colors.white)),
onTap: () => widget.onColorChangeEnd( backgroundColor: AppColors.of(context).red,
Colors.transparent, context: context,
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--; },
} controller: idController,
return GestureDetector( decoration: InputDecoration(
onTap: () => widget.onColorChangeEnd(colors[index]), hintText: 'theme_id'.i18n,
child: ColorIndicator(HSVColor.fromColor(colors[index]), ),
currentHsvColor: currentHsvColor, ),
width: 30, // MaterialActionButton(
height: 30), // child: Row(
); // mainAxisSize: MainAxisSize.min,
}), // children: [
); // Text('check_id'.i18n),
}, // ],
onColorChanged: (c) => {}, // ),
// backgroundColor: AppColors.of(context).filc,
// onPressed: () {},
// ),
],
),
), ),
), if (widget.colorMode != CustomColorMode.enterId)
if (widget.colorMode != CustomColorMode.theme) 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( Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(

View File

@ -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<PremiumShareTheme> createState() => _PremiumShareThemeState();
}
class _PremiumShareThemeState extends State<PremiumShareTheme>
with TickerProviderStateMixin {
late final SettingsProvider settingsProvider;
@override
void initState() {
super.initState();
settingsProvider = Provider.of<SettingsProvider>(context, listen: false);
}
@override
Widget build(BuildContext context) {
return const Scaffold();
}
}

View File

@ -1,4 +1,5 @@
import 'package:filcnaplo/models/settings.dart'; 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/accent.dart';
import 'package:filcnaplo/theme/colors/colors.dart'; import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:filcnaplo/theme/observer.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_mobile_ui/common/widgets/homework/homework_tile.dart';
import 'package:filcnaplo_premium/models/premium_scopes.dart'; import 'package:filcnaplo_premium/models/premium_scopes.dart';
import 'package:filcnaplo_premium/providers/premium_provider.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/flutter_colorpicker/colorpicker.dart';
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart'; import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'theme.i18n.dart'; import 'theme.i18n.dart';
import 'package:share_plus/share_plus.dart';
class PremiumCustomAccentColorSetting extends StatefulWidget { class PremiumCustomAccentColorSetting extends StatefulWidget {
const PremiumCustomAccentColorSetting({Key? key}) : super(key: key); const PremiumCustomAccentColorSetting({Key? key}) : super(key: key);
@ -28,12 +32,20 @@ class PremiumCustomAccentColorSetting extends StatefulWidget {
_PremiumCustomAccentColorSettingState(); _PremiumCustomAccentColorSettingState();
} }
enum CustomColorMode { theme, saved, accent, background, highlight } enum CustomColorMode {
theme,
saved,
accent,
background,
highlight,
enterId,
}
class _PremiumCustomAccentColorSettingState class _PremiumCustomAccentColorSettingState
extends State<PremiumCustomAccentColorSetting> extends State<PremiumCustomAccentColorSetting>
with TickerProviderStateMixin { with TickerProviderStateMixin {
late final SettingsProvider settings; late final SettingsProvider settings;
late final ShareProvider shareProvider;
bool colorSelection = false; bool colorSelection = false;
bool customColorMenu = false; bool customColorMenu = false;
CustomColorMode colorMode = CustomColorMode.theme; CustomColorMode colorMode = CustomColorMode.theme;
@ -95,9 +107,10 @@ class _PremiumCustomAccentColorSettingState
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_colorsTabController = TabController(length: 4, vsync: this); _colorsTabController = TabController(length: 5, vsync: this);
_testTabController = TabController(length: 4, vsync: this); _testTabController = TabController(length: 4, vsync: this);
settings = Provider.of<SettingsProvider>(context, listen: false); settings = Provider.of<SettingsProvider>(context, listen: false);
shareProvider = Provider.of<ShareProvider>(context, listen: false);
_openAnimController = AnimationController( _openAnimController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 750)); vsync: this, duration: const Duration(milliseconds: 750));
@ -132,10 +145,14 @@ class _PremiumCustomAccentColorSettingState
return settings.customHighlightColor; return settings.customHighlightColor;
case CustomColorMode.accent: case CustomColorMode.accent:
return settings.customAccentColor; 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) { if (colorMode != CustomColorMode.theme) {
settings.update(accentColor: AccentColor.custom, store: store); settings.update(accentColor: AccentColor.custom, store: store);
} }
@ -168,6 +185,11 @@ class _PremiumCustomAccentColorSettingState
case CustomColorMode.accent: case CustomColorMode.accent:
settings.update(customAccentColor: v, store: store); settings.update(customAccentColor: v, store: store);
break; 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( appBar: AppBar(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor, surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
leading: BackButton(color: AppColors.of(context).text), 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( title: Text(
"theme_prev".i18n, "theme_prev".i18n,
style: TextStyle(color: AppColors.of(context).text), style: TextStyle(color: AppColors.of(context).text),
@ -588,6 +638,9 @@ class _PremiumCustomAccentColorSettingState
tab: Tab( tab: Tab(
text: "colorpicker_presets" text: "colorpicker_presets"
.i18n)), .i18n)),
ColorTab(
color: unknownColor,
tab: Tab(text: "enter_id".i18n)),
/*ColorTab( /*ColorTab(
color: color:
settings.customAccentColor ?? settings.customAccentColor ??
@ -639,25 +692,31 @@ class _PremiumCustomAccentColorSettingState
CustomColorMode.theme; CustomColorMode.theme;
}); });
break; break;
case 1:
setState(() {
colorMode =
CustomColorMode.enterId;
});
break;
/*case 1: /*case 1:
setState(() { setState(() {
colorMode = colorMode =
CustomColorMode.saved; CustomColorMode.saved;
}); });
break;*/ break;*/
case 1: case 2:
setState(() { setState(() {
colorMode = colorMode =
CustomColorMode.background; CustomColorMode.background;
}); });
break; break;
case 2: case 3:
setState(() { setState(() {
colorMode = colorMode =
CustomColorMode.highlight; CustomColorMode.highlight;
}); });
break; break;
case 3: case 4:
setState(() { setState(() {
colorMode = colorMode =
CustomColorMode.accent; CustomColorMode.accent;
@ -721,6 +780,19 @@ class _PremiumCustomAccentColorSettingState
}); });
setTheme(settings.theme, true); setTheme(settings.theme, true);
}, },
onThemeIdProvided: (theme) {
setState(() {
updateCustomColor(
null,
true,
accent: theme.accentColor,
background:
theme.backgroundColor,
panels: theme.panelsColor,
);
});
setTheme(settings.theme, true);
},
), ),
), ),
), ),

View File

@ -11,6 +11,9 @@ extension SettingsLocalization on String {
"colorpicker_accent": "Accent", "colorpicker_accent": "Accent",
"need_sub": "You need Kupak subscription to use modify this.", "need_sub": "You need Kupak subscription to use modify this.",
"advanced": "Advanced", "advanced": "Advanced",
"enter_id": "Enter ID",
"theme_id": "Theme ID...",
"theme_not_found": "Theme not found!",
}, },
"hu_hu": { "hu_hu": {
"theme_prev": "Előnézet", "theme_prev": "Előnézet",
@ -20,6 +23,9 @@ extension SettingsLocalization on String {
"colorpicker_accent": "Színtónus", "colorpicker_accent": "Színtónus",
"need_sub": "A módosításhoz Kupak szintű támogatás szükséges.", "need_sub": "A módosításhoz Kupak szintű támogatás szükséges.",
"advanced": "Haladó", "advanced": "Haladó",
"enter_id": "ID megadása",
"theme_id": "Téma azonosító...",
"theme_not_found": "A téma nem található!",
}, },
"de_de": { "de_de": {
"theme_prev": "Vorschau", "theme_prev": "Vorschau",
@ -30,6 +36,9 @@ extension SettingsLocalization on String {
"need_sub": "need_sub":
"Sie benötigen ein Kupak-Abonnement, um diese Funktion zu ändern.", "Sie benötigen ein Kupak-Abonnement, um diese Funktion zu ändern.",
"advanced": "Fortschrittlich", "advanced": "Fortschrittlich",
"enter_id": "Geben Sie die ID ein",
"theme_id": "Themen-ID...",
"theme_not_found": "Thema nicht gefunden!",
}, },
}; };