mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: downgrade/upgrade dialogs
This commit is contained in:
parent
89f6056592
commit
08fd136f39
@ -27,7 +27,7 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
|||||||
|
|
||||||
on<SettingsPlanEvent>((event, emit) async {
|
on<SettingsPlanEvent>((event, emit) async {
|
||||||
await event.when(
|
await event.when(
|
||||||
started: () async {
|
started: (withShowSuccessful) async {
|
||||||
emit(const SettingsPlanState.loading());
|
emit(const SettingsPlanState.loading());
|
||||||
|
|
||||||
final snapshots = await Future.wait([
|
final snapshots = await Future.wait([
|
||||||
@ -88,8 +88,19 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
|||||||
workspaceUsage: usageResult,
|
workspaceUsage: usageResult,
|
||||||
subscription: subscription,
|
subscription: subscription,
|
||||||
billingPortal: billingPortal,
|
billingPortal: billingPortal,
|
||||||
|
showSuccessDialog: withShowSuccessful,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (withShowSuccessful) {
|
||||||
|
emit(
|
||||||
|
SettingsPlanState.ready(
|
||||||
|
workspaceUsage: usageResult,
|
||||||
|
subscription: subscription,
|
||||||
|
billingPortal: billingPortal,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
addSubscription: (plan) async {
|
addSubscription: (plan) async {
|
||||||
final result = await UserBackendService.createSubscription(
|
final result = await UserBackendService.createSubscription(
|
||||||
@ -104,6 +115,7 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
|||||||
},
|
},
|
||||||
cancelSubscription: () async {
|
cancelSubscription: () async {
|
||||||
await UserBackendService.cancelSubscription(workspaceId);
|
await UserBackendService.cancelSubscription(workspaceId);
|
||||||
|
add(const SettingsPlanEvent.started());
|
||||||
},
|
},
|
||||||
paymentSuccessful: () {
|
paymentSuccessful: () {
|
||||||
final readyState = state.mapOrNull(ready: (state) => state);
|
final readyState = state.mapOrNull(ready: (state) => state);
|
||||||
@ -111,8 +123,7 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(readyState.copyWith(showSuccessDialog: true));
|
add(const SettingsPlanEvent.started(withShowSuccessful: true));
|
||||||
emit(readyState.copyWith(showSuccessDialog: false));
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -135,7 +146,9 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
|||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class SettingsPlanEvent with _$SettingsPlanEvent {
|
class SettingsPlanEvent with _$SettingsPlanEvent {
|
||||||
const factory SettingsPlanEvent.started() = _Started;
|
const factory SettingsPlanEvent.started({
|
||||||
|
@Default(false) bool withShowSuccessful,
|
||||||
|
}) = _Started;
|
||||||
const factory SettingsPlanEvent.addSubscription(SubscriptionPlanPB plan) =
|
const factory SettingsPlanEvent.addSubscription(SubscriptionPlanPB plan) =
|
||||||
_AddSubscription;
|
_AddSubscription;
|
||||||
const factory SettingsPlanEvent.cancelSubscription() = _CancelSubscription;
|
const factory SettingsPlanEvent.cancelSubscription() = _CancelSubscription;
|
||||||
|
@ -8,7 +8,7 @@ import 'package:appflowy/workspace/presentation/settings/pages/settings_plan_com
|
|||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/single_setting_action.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/single_setting_action.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/workspace.pbenum.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -63,8 +63,9 @@ class SettingsBillingView extends StatelessWidget {
|
|||||||
onPressed: () => _openPricingDialog(
|
onPressed: () => _openPricingDialog(
|
||||||
context,
|
context,
|
||||||
workspaceId,
|
workspaceId,
|
||||||
state.subscription.subscriptionPlan,
|
state.subscription,
|
||||||
),
|
),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
label: state.subscription.label,
|
label: state.subscription.label,
|
||||||
buttonLabel: LocaleKeys
|
buttonLabel: LocaleKeys
|
||||||
.settings_billingPage_plan_planButtonLabel
|
.settings_billingPage_plan_planButtonLabel
|
||||||
@ -77,6 +78,7 @@ class SettingsBillingView extends StatelessWidget {
|
|||||||
label: LocaleKeys
|
label: LocaleKeys
|
||||||
.settings_billingPage_plan_billingPeriod
|
.settings_billingPage_plan_billingPeriod
|
||||||
.tr(),
|
.tr(),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
buttonLabel: LocaleKeys
|
buttonLabel: LocaleKeys
|
||||||
.settings_billingPage_plan_periodButtonLabel
|
.settings_billingPage_plan_periodButtonLabel
|
||||||
.tr(),
|
.tr(),
|
||||||
@ -95,6 +97,7 @@ class SettingsBillingView extends StatelessWidget {
|
|||||||
label: LocaleKeys
|
label: LocaleKeys
|
||||||
.settings_billingPage_paymentDetails_methodLabel
|
.settings_billingPage_paymentDetails_methodLabel
|
||||||
.tr(),
|
.tr(),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
buttonLabel: LocaleKeys
|
buttonLabel: LocaleKeys
|
||||||
.settings_billingPage_paymentDetails_methodButtonLabel
|
.settings_billingPage_paymentDetails_methodButtonLabel
|
||||||
.tr(),
|
.tr(),
|
||||||
@ -113,7 +116,7 @@ class SettingsBillingView extends StatelessWidget {
|
|||||||
void _openPricingDialog(
|
void _openPricingDialog(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
String workspaceId,
|
String workspaceId,
|
||||||
SubscriptionPlanPB plan,
|
WorkspaceSubscriptionPB subscription,
|
||||||
) =>
|
) =>
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -122,7 +125,7 @@ class SettingsBillingView extends StatelessWidget {
|
|||||||
..add(const SettingsPlanEvent.started()),
|
..add(const SettingsPlanEvent.started()),
|
||||||
child: SettingsPlanComparisonDialog(
|
child: SettingsPlanComparisonDialog(
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
currentPlan: plan,
|
subscription: subscription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/plan/settings_plan_bloc.dart';
|
import 'package:appflowy/workspace/application/settings/plan/settings_plan_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/settings/plan/workspace_subscription_ext.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_alert_dialog.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
@ -14,11 +17,11 @@ class SettingsPlanComparisonDialog extends StatefulWidget {
|
|||||||
const SettingsPlanComparisonDialog({
|
const SettingsPlanComparisonDialog({
|
||||||
super.key,
|
super.key,
|
||||||
required this.workspaceId,
|
required this.workspaceId,
|
||||||
required this.currentPlan,
|
required this.subscription,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String workspaceId;
|
final String workspaceId;
|
||||||
final SubscriptionPlanPB currentPlan;
|
final WorkspaceSubscriptionPB subscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SettingsPlanComparisonDialog> createState() =>
|
State<SettingsPlanComparisonDialog> createState() =>
|
||||||
@ -30,6 +33,8 @@ class _SettingsPlanComparisonDialogState
|
|||||||
final horizontalController = ScrollController();
|
final horizontalController = ScrollController();
|
||||||
final verticalController = ScrollController();
|
final verticalController = ScrollController();
|
||||||
|
|
||||||
|
late WorkspaceSubscriptionPB currentSubscription = widget.subscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
horizontalController.dispose();
|
horizontalController.dispose();
|
||||||
@ -39,147 +44,208 @@ class _SettingsPlanComparisonDialogState
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SettingsPlanBloc, SettingsPlanState>(
|
return BlocListener<SettingsPlanBloc, SettingsPlanState>(
|
||||||
builder: (context, state) {
|
listener: (context, state) {
|
||||||
return FlowyDialog(
|
final readyState = state.mapOrNull(ready: (state) => state);
|
||||||
constraints: const BoxConstraints(maxWidth: 784, minWidth: 674),
|
|
||||||
child: Column(
|
if (readyState == null) {
|
||||||
children: [
|
return;
|
||||||
Padding(
|
}
|
||||||
padding: const EdgeInsets.only(top: 24, left: 24, right: 24),
|
|
||||||
child: Row(
|
if (readyState.showSuccessDialog) {
|
||||||
mainAxisSize: MainAxisSize.min,
|
SettingsAlertDialog(
|
||||||
children: [
|
icon: Center(
|
||||||
FlowyText.semibold(
|
child: SizedBox(
|
||||||
LocaleKeys.settings_comparePlanDialog_title.tr(),
|
height: 90,
|
||||||
fontSize: 24,
|
width: 90,
|
||||||
),
|
child: FlowySvg(
|
||||||
const Spacer(),
|
FlowySvgs.check_circle_s,
|
||||||
GestureDetector(
|
color: AFThemeExtension.of(context).success,
|
||||||
onTap: Navigator.of(context).pop,
|
|
||||||
child: MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.click,
|
|
||||||
child: FlowySvg(
|
|
||||||
FlowySvgs.m_close_m,
|
|
||||||
size: const Size.square(20),
|
|
||||||
color: Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Flexible(
|
),
|
||||||
child: SingleChildScrollView(
|
title:
|
||||||
controller: horizontalController,
|
LocaleKeys.settings_comparePlanDialog_paymentSuccess_title.tr(
|
||||||
scrollDirection: Axis.horizontal,
|
args: [readyState.subscription.label],
|
||||||
child: SingleChildScrollView(
|
),
|
||||||
controller: verticalController,
|
subtitle: LocaleKeys
|
||||||
padding:
|
.settings_comparePlanDialog_paymentSuccess_description
|
||||||
const EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
.tr(
|
||||||
child: Column(
|
args: [readyState.subscription.label],
|
||||||
mainAxisSize: MainAxisSize.min,
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
hideCancelButton: true,
|
||||||
children: [
|
confirm: Navigator.of(context).pop,
|
||||||
Row(
|
confirmLabel: LocaleKeys.button_close.tr(),
|
||||||
mainAxisSize: MainAxisSize.min,
|
).show(context);
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
}
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: 250,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const VSpace(22),
|
|
||||||
SizedBox(
|
|
||||||
height: 100,
|
|
||||||
child: FlowyText.semibold(
|
|
||||||
LocaleKeys
|
|
||||||
.settings_comparePlanDialog_planFeatures
|
|
||||||
.tr(),
|
|
||||||
fontSize: 24,
|
|
||||||
maxLines: 2,
|
|
||||||
color: const Color(0xFF5C3699),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 64),
|
|
||||||
const SizedBox(height: 56),
|
|
||||||
..._planLabels.map(
|
|
||||||
(e) => _ComparisonCell(
|
|
||||||
label: e.label,
|
|
||||||
tooltip: e.tooltip,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_PlanTable(
|
|
||||||
title: LocaleKeys
|
|
||||||
.settings_comparePlanDialog_freePlan_title
|
|
||||||
.tr(),
|
|
||||||
description: LocaleKeys
|
|
||||||
.settings_comparePlanDialog_freePlan_description
|
|
||||||
.tr(),
|
|
||||||
price: LocaleKeys
|
|
||||||
.settings_comparePlanDialog_freePlan_price
|
|
||||||
.tr(),
|
|
||||||
priceInfo: LocaleKeys
|
|
||||||
.settings_comparePlanDialog_freePlan_priceInfo
|
|
||||||
.tr(),
|
|
||||||
cells: _freeLabels,
|
|
||||||
isCurrent:
|
|
||||||
widget.currentPlan == SubscriptionPlanPB.None,
|
|
||||||
canDowngrade:
|
|
||||||
widget.currentPlan != SubscriptionPlanPB.None,
|
|
||||||
onSelected: () async {
|
|
||||||
if (widget.currentPlan ==
|
|
||||||
SubscriptionPlanPB.None) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.read<SettingsPlanBloc>().add(
|
setState(() {
|
||||||
const SettingsPlanEvent
|
currentSubscription = readyState.subscription;
|
||||||
.cancelSubscription(),
|
});
|
||||||
);
|
},
|
||||||
},
|
child: FlowyDialog(
|
||||||
),
|
constraints: const BoxConstraints(maxWidth: 784, minWidth: 674),
|
||||||
_PlanTable(
|
child: Column(
|
||||||
title: LocaleKeys
|
children: [
|
||||||
.settings_comparePlanDialog_proPlan_title
|
Padding(
|
||||||
.tr(),
|
padding: const EdgeInsets.only(top: 24, left: 24, right: 24),
|
||||||
description: LocaleKeys
|
child: Row(
|
||||||
.settings_comparePlanDialog_proPlan_description
|
mainAxisSize: MainAxisSize.min,
|
||||||
.tr(),
|
children: [
|
||||||
price: LocaleKeys
|
FlowyText.semibold(
|
||||||
.settings_comparePlanDialog_proPlan_price
|
LocaleKeys.settings_comparePlanDialog_title.tr(),
|
||||||
.tr(),
|
fontSize: 24,
|
||||||
priceInfo: LocaleKeys
|
),
|
||||||
.settings_comparePlanDialog_proPlan_priceInfo
|
const Spacer(),
|
||||||
.tr(),
|
GestureDetector(
|
||||||
cells: _proLabels,
|
onTap: Navigator.of(context).pop,
|
||||||
isCurrent:
|
child: MouseRegion(
|
||||||
widget.currentPlan == SubscriptionPlanPB.Pro,
|
cursor: SystemMouseCursors.click,
|
||||||
canUpgrade:
|
child: FlowySvg(
|
||||||
widget.currentPlan == SubscriptionPlanPB.None,
|
FlowySvgs.m_close_m,
|
||||||
onSelected: () =>
|
size: const Size.square(20),
|
||||||
context.read<SettingsPlanBloc>().add(
|
color: Theme.of(context).colorScheme.outline,
|
||||||
const SettingsPlanEvent.addSubscription(
|
),
|
||||||
SubscriptionPlanPB.Pro,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: horizontalController,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: verticalController,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 250,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const VSpace(22),
|
||||||
|
SizedBox(
|
||||||
|
height: 100,
|
||||||
|
child: FlowyText.semibold(
|
||||||
|
LocaleKeys
|
||||||
|
.settings_comparePlanDialog_planFeatures
|
||||||
|
.tr(),
|
||||||
|
fontSize: 24,
|
||||||
|
maxLines: 2,
|
||||||
|
color: const Color(0xFF5C3699),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 64),
|
||||||
|
const SizedBox(height: 56),
|
||||||
|
..._planLabels.map(
|
||||||
|
(e) => _ComparisonCell(
|
||||||
|
label: e.label,
|
||||||
|
tooltip: e.tooltip,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_PlanTable(
|
||||||
|
title: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_freePlan_title
|
||||||
|
.tr(),
|
||||||
|
description: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_freePlan_description
|
||||||
|
.tr(),
|
||||||
|
price: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_freePlan_price
|
||||||
|
.tr(),
|
||||||
|
priceInfo: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_freePlan_priceInfo
|
||||||
|
.tr(),
|
||||||
|
cells: _freeLabels,
|
||||||
|
isCurrent: currentSubscription.subscriptionPlan ==
|
||||||
|
SubscriptionPlanPB.None,
|
||||||
|
canDowngrade:
|
||||||
|
currentSubscription.subscriptionPlan !=
|
||||||
|
SubscriptionPlanPB.None,
|
||||||
|
onSelected: () async {
|
||||||
|
if (currentSubscription.subscriptionPlan ==
|
||||||
|
SubscriptionPlanPB.None ||
|
||||||
|
currentSubscription.hasCanceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.read<SettingsPlanBloc>().add(
|
||||||
|
const SettingsPlanEvent
|
||||||
|
.cancelSubscription(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await SettingsAlertDialog(
|
||||||
|
icon: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 90,
|
||||||
|
width: 90,
|
||||||
|
child: FlowySvg(
|
||||||
|
FlowySvgs.check_circle_s,
|
||||||
|
color:
|
||||||
|
AFThemeExtension.of(context).success,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_downgradeSuccess_title
|
||||||
|
.tr(args: [currentSubscription.label]),
|
||||||
|
subtitle: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_downgradeSuccess_description
|
||||||
|
.tr(),
|
||||||
|
hideCancelButton: true,
|
||||||
|
confirm: Navigator.of(context).pop,
|
||||||
|
confirmLabel: LocaleKeys.button_close.tr(),
|
||||||
|
).show(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_PlanTable(
|
||||||
|
title: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_proPlan_title
|
||||||
|
.tr(),
|
||||||
|
description: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_proPlan_description
|
||||||
|
.tr(),
|
||||||
|
price: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_proPlan_price
|
||||||
|
.tr(),
|
||||||
|
priceInfo: LocaleKeys
|
||||||
|
.settings_comparePlanDialog_proPlan_priceInfo
|
||||||
|
.tr(),
|
||||||
|
cells: _proLabels,
|
||||||
|
isCurrent: currentSubscription.subscriptionPlan ==
|
||||||
|
SubscriptionPlanPB.Pro,
|
||||||
|
canUpgrade: currentSubscription.subscriptionPlan ==
|
||||||
|
SubscriptionPlanPB.None,
|
||||||
|
onSelected: () =>
|
||||||
|
context.read<SettingsPlanBloc>().add(
|
||||||
|
const SettingsPlanEvent.addSubscription(
|
||||||
|
SubscriptionPlanPB.Pro,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ class SettingsPlanView extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
_PlanUsageSummary(
|
_PlanUsageSummary(
|
||||||
usage: state.workspaceUsage,
|
usage: state.workspaceUsage,
|
||||||
currentPlan: state.subscription.subscriptionPlan,
|
subscription: state.subscription,
|
||||||
),
|
),
|
||||||
_CurrentPlanBox(subscription: state.subscription),
|
_CurrentPlanBox(subscription: state.subscription),
|
||||||
],
|
],
|
||||||
@ -117,7 +117,7 @@ class _CurrentPlanBox extends StatelessWidget {
|
|||||||
onPressed: () => _openPricingDialog(
|
onPressed: () => _openPricingDialog(
|
||||||
context,
|
context,
|
||||||
context.read<SettingsPlanBloc>().workspaceId,
|
context.read<SettingsPlanBloc>().workspaceId,
|
||||||
subscription.subscriptionPlan,
|
subscription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (subscription.hasCanceled) ...[
|
if (subscription.hasCanceled) ...[
|
||||||
@ -225,7 +225,7 @@ class _CurrentPlanBox extends StatelessWidget {
|
|||||||
void _openPricingDialog(
|
void _openPricingDialog(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
String workspaceId,
|
String workspaceId,
|
||||||
SubscriptionPlanPB plan,
|
WorkspaceSubscriptionPB subscription,
|
||||||
) =>
|
) =>
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -233,7 +233,7 @@ class _CurrentPlanBox extends StatelessWidget {
|
|||||||
value: context.read<SettingsPlanBloc>(),
|
value: context.read<SettingsPlanBloc>(),
|
||||||
child: SettingsPlanComparisonDialog(
|
child: SettingsPlanComparisonDialog(
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
currentPlan: plan,
|
subscription: subscription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -274,10 +274,10 @@ class _ProConItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PlanUsageSummary extends StatelessWidget {
|
class _PlanUsageSummary extends StatelessWidget {
|
||||||
const _PlanUsageSummary({required this.usage, required this.currentPlan});
|
const _PlanUsageSummary({required this.usage, required this.subscription});
|
||||||
|
|
||||||
final WorkspaceUsagePB usage;
|
final WorkspaceUsagePB usage;
|
||||||
final SubscriptionPlanPB currentPlan;
|
final WorkspaceSubscriptionPB subscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -329,18 +329,18 @@ class _PlanUsageSummary extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_ToggleMore(
|
_ToggleMore(
|
||||||
value: currentPlan == SubscriptionPlanPB.Pro,
|
value: subscription.subscriptionPlan == SubscriptionPlanPB.Pro,
|
||||||
label:
|
label:
|
||||||
LocaleKeys.settings_planPage_planUsage_memberProToggle.tr(),
|
LocaleKeys.settings_planPage_planUsage_memberProToggle.tr(),
|
||||||
currentPlan: currentPlan,
|
subscription: subscription,
|
||||||
badgeLabel: LocaleKeys.settings_planPage_planUsage_proBadge.tr(),
|
badgeLabel: LocaleKeys.settings_planPage_planUsage_proBadge.tr(),
|
||||||
),
|
),
|
||||||
const VSpace(8),
|
const VSpace(8),
|
||||||
_ToggleMore(
|
_ToggleMore(
|
||||||
value: currentPlan == SubscriptionPlanPB.Pro,
|
value: subscription.subscriptionPlan == SubscriptionPlanPB.Pro,
|
||||||
label:
|
label:
|
||||||
LocaleKeys.settings_planPage_planUsage_guestCollabToggle.tr(),
|
LocaleKeys.settings_planPage_planUsage_guestCollabToggle.tr(),
|
||||||
currentPlan: currentPlan,
|
subscription: subscription,
|
||||||
badgeLabel: LocaleKeys.settings_planPage_planUsage_proBadge.tr(),
|
badgeLabel: LocaleKeys.settings_planPage_planUsage_proBadge.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -381,13 +381,13 @@ class _ToggleMore extends StatefulWidget {
|
|||||||
const _ToggleMore({
|
const _ToggleMore({
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.currentPlan,
|
required this.subscription,
|
||||||
this.badgeLabel,
|
this.badgeLabel,
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool value;
|
final bool value;
|
||||||
final String label;
|
final String label;
|
||||||
final SubscriptionPlanPB currentPlan;
|
final WorkspaceSubscriptionPB subscription;
|
||||||
final String? badgeLabel;
|
final String? badgeLabel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -422,7 +422,7 @@ class _ToggleMoreState extends State<_ToggleMore> {
|
|||||||
value: context.read<SettingsPlanBloc>(),
|
value: context.read<SettingsPlanBloc>(),
|
||||||
child: SettingsPlanComparisonDialog(
|
child: SettingsPlanComparisonDialog(
|
||||||
workspaceId: context.read<SettingsPlanBloc>().workspaceId,
|
workspaceId: context.read<SettingsPlanBloc>().workspaceId,
|
||||||
currentPlan: widget.currentPlan,
|
subscription: widget.subscription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((_) {
|
).then((_) {
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||||
@ -40,8 +43,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
||||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
||||||
@ -10,6 +11,7 @@ import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
|||||||
class SettingsAlertDialog extends StatefulWidget {
|
class SettingsAlertDialog extends StatefulWidget {
|
||||||
const SettingsAlertDialog({
|
const SettingsAlertDialog({
|
||||||
super.key,
|
super.key,
|
||||||
|
this.icon,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
this.children,
|
this.children,
|
||||||
@ -21,6 +23,7 @@ class SettingsAlertDialog extends StatefulWidget {
|
|||||||
this.implyLeading = false,
|
this.implyLeading = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final Widget? icon;
|
||||||
final String title;
|
final String title;
|
||||||
final String? subtitle;
|
final String? subtitle;
|
||||||
final List<Widget>? children;
|
final List<Widget>? children;
|
||||||
@ -86,6 +89,10 @@ class _SettingsAlertDialogState extends State<SettingsAlertDialog> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if (widget.icon != null) ...[
|
||||||
|
widget.icon!,
|
||||||
|
const VSpace(16),
|
||||||
|
],
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -168,6 +175,7 @@ class _Actions extends StatelessWidget {
|
|||||||
fontColor: AFThemeExtension.of(context).textColor,
|
fontColor: AFThemeExtension.of(context).textColor,
|
||||||
fillColor: Colors.transparent,
|
fillColor: Colors.transparent,
|
||||||
hoverColor: Colors.transparent,
|
hoverColor: Colors.transparent,
|
||||||
|
radius: Corners.s12Border,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
cancel?.call();
|
cancel?.call();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
@ -187,6 +195,7 @@ class _Actions extends StatelessWidget {
|
|||||||
horizontal: 24,
|
horizontal: 24,
|
||||||
vertical: 12,
|
vertical: 12,
|
||||||
),
|
),
|
||||||
|
radius: Corners.s12Border,
|
||||||
fontColor: isDangerous ? Colors.white : null,
|
fontColor: isDangerous ? Colors.white : null,
|
||||||
fontHoverColor: Colors.white,
|
fontHoverColor: Colors.white,
|
||||||
fillColor: isDangerous
|
fillColor: isDangerous
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
@ -65,6 +66,7 @@ class SingleSettingAction extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 7),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 7),
|
||||||
fillColor:
|
fillColor:
|
||||||
isDangerous ? null : Theme.of(context).colorScheme.primary,
|
isDangerous ? null : Theme.of(context).colorScheme.primary,
|
||||||
|
radius: Corners.s12Border,
|
||||||
hoverColor: isDangerous ? null : const Color(0xFF005483),
|
hoverColor: isDangerous ? null : const Color(0xFF005483),
|
||||||
fontColor: isDangerous ? Theme.of(context).colorScheme.error : null,
|
fontColor: isDangerous ? Theme.of(context).colorScheme.error : null,
|
||||||
fontHoverColor: Colors.white,
|
fontHoverColor: Colors.white,
|
||||||
|
3
frontend/resources/flowy_icons/16x/check_circle.svg
Normal file
3
frontend/resources/flowy_icons/16x/check_circle.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.88 11.68L12.52 6.04L11.4 4.92L6.88 9.44L4.6 7.16L3.48 8.28L6.88 11.68ZM8 16C6.89333 16 5.85333 15.79 4.88 15.37C3.90667 14.95 3.06 14.38 2.34 13.66C1.62 12.94 1.05 12.0933 0.63 11.12C0.21 10.1467 0 9.10667 0 8C0 6.89333 0.21 5.85333 0.63 4.88C1.05 3.90667 1.62 3.06 2.34 2.34C3.06 1.62 3.90667 1.05 4.88 0.63C5.85333 0.21 6.89333 0 8 0C9.10667 0 10.1467 0.21 11.12 0.63C12.0933 1.05 12.94 1.62 13.66 2.34C14.38 3.06 14.95 3.90667 15.37 4.88C15.79 5.85333 16 6.89333 16 8C16 9.10667 15.79 10.1467 15.37 11.12C14.95 12.0933 14.38 12.94 13.66 13.66C12.94 14.38 12.0933 14.95 11.12 15.37C10.1467 15.79 9.10667 16 8 16Z" fill="#66CF80"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 748 B |
@ -330,7 +330,8 @@
|
|||||||
"signInGoogle": "Sign in with Google",
|
"signInGoogle": "Sign in with Google",
|
||||||
"signInGithub": "Sign in with Github",
|
"signInGithub": "Sign in with Github",
|
||||||
"signInDiscord": "Sign in with Discord",
|
"signInDiscord": "Sign in with Discord",
|
||||||
"more": "More"
|
"more": "More",
|
||||||
|
"close": "Close"
|
||||||
},
|
},
|
||||||
"label": {
|
"label": {
|
||||||
"welcome": "Welcome!",
|
"welcome": "Welcome!",
|
||||||
@ -618,6 +619,14 @@
|
|||||||
"itemSix": "yes",
|
"itemSix": "yes",
|
||||||
"itemSeven": "yes",
|
"itemSeven": "yes",
|
||||||
"itemEight": "10,000 monthly"
|
"itemEight": "10,000 monthly"
|
||||||
|
},
|
||||||
|
"paymentSuccess": {
|
||||||
|
"title": "You are now on the {} plan!",
|
||||||
|
"description": "Your payment has been successfully processed and your plan is upgraded to AppFlowy {}. You can view your plan details on the Plan page"
|
||||||
|
},
|
||||||
|
"downgradeSuccess": {
|
||||||
|
"title": "You have canceled the {} plan!",
|
||||||
|
"description": "Your plan has been canceled, until the end of the billing cycle you will retain your previous plan benefits."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
|
Loading…
Reference in New Issue
Block a user