Add files via upload
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
import 'package:filcnaplo/theme/colors/colors.dart';
|
||||
import 'package:filcnaplo_premium/providers/premium_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_feather_icons/flutter_feather_icons.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ActivationDashboard extends StatefulWidget {
|
||||
const ActivationDashboard({super.key});
|
||||
|
||||
@override
|
||||
State<ActivationDashboard> createState() => _ActivationDashboardState();
|
||||
}
|
||||
|
||||
class _ActivationDashboardState extends State<ActivationDashboard> {
|
||||
bool manualActivationLoading = false;
|
||||
|
||||
Future<void> onManualActivation() async {
|
||||
final data = await Clipboard.getData("text/plain");
|
||||
if (data == null || data.text == null || data.text == "") {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
manualActivationLoading = true;
|
||||
});
|
||||
final result = await context.read<PremiumProvider>().auth.finishAuth(data.text!);
|
||||
setState(() {
|
||||
manualActivationLoading = false;
|
||||
});
|
||||
|
||||
if (!result && mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
|
||||
content: Text(
|
||||
"Sikertelen aktiválás. Kérlek próbáld újra később!",
|
||||
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||
),
|
||||
backgroundColor: Colors.red,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Spacer(),
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
"assets/images/github.svg",
|
||||
height: 64.0,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32.0),
|
||||
const Text(
|
||||
"Jelentkezz be a GitHub felületén és adj hozzáférést a Filcnek, hogy aktiváld a Premiumot.",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 18.0),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: const [
|
||||
Icon(FeatherIcons.alertTriangle, size: 20.0, color: Colors.orange),
|
||||
SizedBox(width: 12.0),
|
||||
Text(
|
||||
"Figyelem!",
|
||||
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6.0),
|
||||
const Text(
|
||||
"Csak akkor érzékeli a Filc a támogatói státuszod, ha nem állítod privátra!",
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: const [
|
||||
Icon(FeatherIcons.alertTriangle, size: 20.0, color: Colors.orange),
|
||||
SizedBox(width: 12.0),
|
||||
Text(
|
||||
"Figyelem!",
|
||||
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6.0),
|
||||
const Text(
|
||||
"Ha friss támogató vagy, 5-10 percbe telhet az aktiválás. Kérlek gyere vissza később, és próbáld újra!",
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
Card(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14.0)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"Ha bejelentkezés után nem lép vissza az alkalmazásba automatikusan, aktiváld a támogatásod manuálisan",
|
||||
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 6.0),
|
||||
Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: onManualActivation,
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStatePropertyAll(Theme.of(context).colorScheme.secondary),
|
||||
overlayColor: MaterialStatePropertyAll(Theme.of(context).colorScheme.secondary.withOpacity(.1)),
|
||||
),
|
||||
icon: manualActivationLoading
|
||||
? const SizedBox(
|
||||
child: CircularProgressIndicator(),
|
||||
height: 16.0,
|
||||
width: 16.0,
|
||||
)
|
||||
: const Icon(FeatherIcons.key, size: 20.0),
|
||||
label: const Padding(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
"Aktiválás tokennel",
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 24.0),
|
||||
child: Center(
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStatePropertyAll(AppColors.of(context).text),
|
||||
overlayColor: MaterialStatePropertyAll(AppColors.of(context).text.withOpacity(.1)),
|
||||
),
|
||||
icon: const Icon(FeatherIcons.arrowLeft, size: 20.0),
|
||||
label: const Text(
|
||||
"Vissza",
|
||||
style: TextStyle(fontSize: 16.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:filcnaplo_premium/providers/premium_provider.dart';
|
||||
import 'package:filcnaplo_premium/ui/mobile/premium/activation_view/activation_dashboard.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lottie/lottie.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class PremiumActivationView extends StatefulWidget {
|
||||
const PremiumActivationView({super.key});
|
||||
|
||||
@override
|
||||
State<PremiumActivationView> createState() => _PremiumActivationViewState();
|
||||
}
|
||||
|
||||
class _PremiumActivationViewState extends State<PremiumActivationView> with SingleTickerProviderStateMixin {
|
||||
late AnimationController animation;
|
||||
bool activated = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
context.read<PremiumProvider>().auth.initAuth();
|
||||
|
||||
animation = AnimationController(vsync: this, duration: const Duration(seconds: 2));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
animation.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final premium = context.watch<PremiumProvider>();
|
||||
|
||||
if (premium.hasPremium && !activated) {
|
||||
activated = true;
|
||||
animation.forward();
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
Future.delayed(const Duration(seconds: 2)).then((value) {
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
body: PageTransitionSwitcher(
|
||||
transitionBuilder: (child, primaryAnimation, secondaryAnimation) => SharedAxisTransition(
|
||||
animation: primaryAnimation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
transitionType: SharedAxisTransitionType.horizontal,
|
||||
fillColor: Colors.transparent,
|
||||
child: child,
|
||||
),
|
||||
child: premium.hasPremium
|
||||
? Center(
|
||||
child: SizedBox(
|
||||
width: 400,
|
||||
child: Lottie.network("https://assets2.lottiefiles.com/packages/lf20_wkebwzpz.json", controller: animation),
|
||||
),
|
||||
)
|
||||
: const SafeArea(child: ActivationDashboard()),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
66
filcnaplo_premium/lib/ui/mobile/premium/premium_inline.dart
Normal file
66
filcnaplo_premium/lib/ui/mobile/premium/premium_inline.dart
Normal file
@@ -0,0 +1,66 @@
|
||||
import 'package:filcnaplo_premium/ui/mobile/premium/upsell.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum PremiumInlineFeature { nickname, theme, widget, goal, stats }
|
||||
|
||||
const Map<PremiumInlineFeature, String> _featureAssets = {
|
||||
PremiumInlineFeature.nickname: "assets/images/premium_nickname_inline_showcase.png",
|
||||
PremiumInlineFeature.theme: "assets/images/premium_theme_inline_showcase.png",
|
||||
PremiumInlineFeature.widget: "assets/images/premium_widget_inline_showcase.png",
|
||||
PremiumInlineFeature.goal: "assets/images/premium_goal_inline_showcase.png",
|
||||
PremiumInlineFeature.stats: "assets/images/premium_stats_inline_showcase.png",
|
||||
};
|
||||
|
||||
const Map<PremiumInlineFeature, PremiumFeature> _featuresInline = {
|
||||
PremiumInlineFeature.nickname: PremiumFeature.profile,
|
||||
PremiumInlineFeature.theme: PremiumFeature.customcolors,
|
||||
PremiumInlineFeature.widget: PremiumFeature.widget,
|
||||
PremiumInlineFeature.goal: PremiumFeature.goalplanner,
|
||||
PremiumInlineFeature.stats: PremiumFeature.gradestats,
|
||||
};
|
||||
|
||||
class PremiumInline extends StatelessWidget {
|
||||
const PremiumInline({super.key, required this.features});
|
||||
|
||||
final List<PremiumInlineFeature> features;
|
||||
|
||||
String _getAsset() {
|
||||
for (int i = 0; i < features.length; i++) {
|
||||
if (DateTime.now().day % features.length == i) {
|
||||
return _featureAssets[features[i]]!;
|
||||
}
|
||||
}
|
||||
|
||||
return _featureAssets[features[0]]!;
|
||||
}
|
||||
|
||||
PremiumFeature _getFeature() {
|
||||
for (int i = 0; i < features.length; i++) {
|
||||
if (DateTime.now().day % features.length == i) {
|
||||
return _featuresInline[features[i]]!;
|
||||
}
|
||||
}
|
||||
|
||||
return _featuresInline[features[0]]!;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Image.asset(_getAsset()),
|
||||
Positioned.fill(
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(16.0),
|
||||
onTap: () {
|
||||
PremiumLockedFeatureUpsell.show(context: context, feature: _getFeature());
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
164
filcnaplo_premium/lib/ui/mobile/premium/upsell.dart
Normal file
164
filcnaplo_premium/lib/ui/mobile/premium/upsell.dart
Normal file
@@ -0,0 +1,164 @@
|
||||
import 'package:filcnaplo/icons/filc_icons.dart';
|
||||
import 'package:filcnaplo_mobile_ui/premium/premium_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum PremiumFeature {
|
||||
gradestats,
|
||||
customcolors,
|
||||
profile,
|
||||
iconpack,
|
||||
subjectrename,
|
||||
weeklytimetable,
|
||||
goalplanner,
|
||||
widget,
|
||||
}
|
||||
|
||||
enum PremiumFeatureLevel { kupak, tinta }
|
||||
|
||||
const Map<PremiumFeature, PremiumFeatureLevel> _featureLevels = {
|
||||
PremiumFeature.gradestats: PremiumFeatureLevel.kupak,
|
||||
PremiumFeature.customcolors: PremiumFeatureLevel.kupak,
|
||||
PremiumFeature.profile: PremiumFeatureLevel.kupak,
|
||||
PremiumFeature.iconpack: PremiumFeatureLevel.kupak,
|
||||
PremiumFeature.subjectrename: PremiumFeatureLevel.kupak,
|
||||
PremiumFeature.weeklytimetable: PremiumFeatureLevel.tinta,
|
||||
PremiumFeature.goalplanner: PremiumFeatureLevel.tinta,
|
||||
PremiumFeature.widget: PremiumFeatureLevel.tinta,
|
||||
};
|
||||
|
||||
const Map<PremiumFeature, String> _featureAssets = {
|
||||
PremiumFeature.gradestats: "assets/images/premium_stats_showcase.png",
|
||||
PremiumFeature.customcolors: "assets/images/premium_theme_showcase.png",
|
||||
PremiumFeature.profile: "assets/images/premium_nickname_showcase.png",
|
||||
PremiumFeature.weeklytimetable: "assets/images/premium_timetable_showcase.png",
|
||||
PremiumFeature.goalplanner: "assets/images/premium_goal_showcase.png",
|
||||
PremiumFeature.widget: "assets/images/premium_widget_showcase.png",
|
||||
};
|
||||
|
||||
const Map<PremiumFeature, String> _featureTitles = {
|
||||
PremiumFeature.gradestats: "Találtál egy prémium funkciót.",
|
||||
PremiumFeature.customcolors: "Több személyre szabás kell?",
|
||||
PremiumFeature.profile: "Nem tetszik a neved?",
|
||||
PremiumFeature.iconpack: "Jobban tetszettek a régi ikonok?",
|
||||
PremiumFeature.subjectrename: "Sokáig tart elolvasni, hogy \"Földrajz természettudomány\"?",
|
||||
PremiumFeature.weeklytimetable: "Szeretnéd egyszerre az egész hetet látni?",
|
||||
PremiumFeature.goalplanner: "Kövesd a céljaidat, sok-sok statisztikával.",
|
||||
PremiumFeature.widget: "Órák a kezdőképernyőd kényelméből.",
|
||||
};
|
||||
|
||||
const Map<PremiumFeature, String> _featureDescriptions = {
|
||||
PremiumFeature.gradestats: "Támogass Kupak szinten, hogy több statisztikát láthass. ",
|
||||
PremiumFeature.customcolors: "Támogass Kupak szinten, és szabd személyre az elemek, a háttér, és a panelek színeit.",
|
||||
PremiumFeature.profile: "Kupak szinten változtathatod a nevedet, sőt, akár a profilképedet is.",
|
||||
PremiumFeature.iconpack: "Támogass Kupak szinten, hogy ikon témát választhass.",
|
||||
PremiumFeature.subjectrename: "Támogass Kupak szinten, hogy átnevezhesd Föcire.",
|
||||
PremiumFeature.weeklytimetable: "Támogass Tinta szinten a heti órarend funkcióért.",
|
||||
PremiumFeature.goalplanner: "A célkövetéshez támogass Tinta szinten.",
|
||||
PremiumFeature.widget: "Támogass Tinta szinten, és helyezz egy widgetet a kezdőképernyődre.",
|
||||
};
|
||||
|
||||
class PremiumLockedFeatureUpsell extends StatelessWidget {
|
||||
const PremiumLockedFeatureUpsell({super.key, required this.feature});
|
||||
|
||||
static void show({required BuildContext context, required PremiumFeature feature}) =>
|
||||
showDialog(context: context, builder: (context) => PremiumLockedFeatureUpsell(feature: feature));
|
||||
|
||||
final PremiumFeature feature;
|
||||
|
||||
IconData _getIcon() => _featureLevels[feature] == PremiumFeatureLevel.kupak ? FilcIcons.kupak : FilcIcons.tinta;
|
||||
Color _getColor(BuildContext context) => _featureLevels[feature] == PremiumFeatureLevel.kupak
|
||||
? const Color(0xffC8A708)
|
||||
: Theme.of(context).brightness == Brightness.light
|
||||
? const Color(0xff691A9B)
|
||||
: const Color(0xffA66FC8);
|
||||
String? _getAsset() => _featureAssets[feature];
|
||||
String _getTitle() => _featureTitles[feature]!;
|
||||
String _getDescription() => _featureDescriptions[feature]!;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Color color = _getColor(context);
|
||||
|
||||
return Dialog(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Title Bar
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Icon(_getIcon()),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: const Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Image showcase
|
||||
if (_getAsset() != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Image.asset(_getAsset()!),
|
||||
),
|
||||
|
||||
// Dialog title
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12.0),
|
||||
child: Text(
|
||||
_getTitle(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 20.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Dialog description
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
_getDescription(),
|
||||
style: const TextStyle(
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// CTA button
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(color.withOpacity(.25)),
|
||||
foregroundColor: MaterialStatePropertyAll(color),
|
||||
overlayColor: MaterialStatePropertyAll(color.withOpacity(.1))),
|
||||
onPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).push(MaterialPageRoute(builder: (context) {
|
||||
return const PremiumScreen();
|
||||
}));
|
||||
},
|
||||
child: const Text(
|
||||
"Vigyél oda!",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user