feat(shopinbit): desktop branding, setup dialog, and dialog UX
What changed, and why it matters
This commit is a routine user-interface update for the ShopinBit feature in Stack Wallet. It changes branding text, adds a first-time setup dialog for desktop users, improves dialog scrolling and navigation, and renames 'My tickets' to 'My requests'. There is no indication of any security fix or vulnerability being addressed.
No security action required. Treat as a normal feature/UX commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies 14 Dart files under lib/pages/shopinbit/ and lib/pages_desktop_specific/. Changes are cosmetic and UX-focused: string renames (‘ShopInBit’ → ‘ShopinBit’, ‘My tickets’ → ‘My requests’), addition of a desktop setup dialog that generates/persists a customer key and display name, wrapping dialog content in SingleChildScrollView, adding back buttons and barrierDismissible: false to wizard dialogs, and unifying the guidelines checkbox behavior across desktop and mobile. No cryptographic, networking, permission, or input-validation logic changes with security implications are visible.
Changed components
lib/pages/shopinbit/shopinbit_car_fee_view.dartlib/pages/shopinbit/shopinbit_car_research_payment_view.dartlib/pages/shopinbit/shopinbit_offer_view.dartlib/pages/shopinbit/shopinbit_order_created.dartlib/pages/shopinbit/shopinbit_payment_view.dartlib/pages/shopinbit/shopinbit_shipping_view.dartlib/pages/shopinbit/shopinbit_step_1.dartlib/pages/shopinbit/shopinbit_step_2.dartlib/pages/shopinbit/shopinbit_step_3.dartlib/pages/shopinbit/shopinbit_step_4.dartlib/pages_desktop_specific/more_view/sub_widgets/desktop_shopinbit_view.dartlib/pages_desktop_specific/settings/settings_menu.dartlib/pages_desktop_specific/settings/settings_menu/shopinbit_settings.dartInspect captured patch +309 / −81
diff --git a/lib/pages/more_view/services_view.dart b/lib/pages/more_view/services_view.dart
index ee33ceb..fa4131f 100644
--- a/lib/pages/more_view/services_view.dart
+++ b/lib/pages/more_view/services_view.dart
@@ -314,8 +314,8 @@ class _ServicesViewState extends State<ServicesView> {
.length;
return SecondaryButton(
label: count > 0
- ? "My tickets ($count)"
- : "My tickets",
+ ? "My requests ($count)"
+ : "My requests",
onPressed: () async {
await Navigator.of(
context,
diff --git a/lib/pages/shopinbit/shopinbit_car_fee_view.dart b/lib/pages/shopinbit/shopinbit_car_fee_view.dart
index 9b34a91..8db1d9a 100644
--- a/lib/pages/shopinbit/shopinbit_car_fee_view.dart
+++ b/lib/pages/shopinbit/shopinbit_car_fee_view.dart
@@ -689,7 +689,8 @@ class _ShopInBitCarFeeViewState extends State<ShopInBitCarFeeView> {
isDesktop: isDesktop,
),
],
- const Spacer(),
+ if (!isDesktop) const Spacer(),
+ if (isDesktop) const SizedBox(height: 24),
PrimaryButton(
label: "Pay research fee",
enabled: _canContinue && !_submitting,
@@ -703,7 +704,7 @@ class _ShopInBitCarFeeViewState extends State<ShopInBitCarFeeView> {
if (isDesktop) {
return DesktopDialog(
maxWidth: 580,
- maxHeight: 650,
+ maxHeight: 750,
child: Column(
children: [
Row(
@@ -712,7 +713,7 @@ class _ShopInBitCarFeeViewState extends State<ShopInBitCarFeeView> {
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
- "ShopInBit",
+ "ShopinBit",
style: STextStyles.desktopH3(context),
),
),
@@ -725,7 +726,9 @@ class _ShopInBitCarFeeViewState extends State<ShopInBitCarFeeView> {
horizontal: 32,
vertical: 16,
),
- child: content,
+ child: SingleChildScrollView(
+ child: content,
+ ),
),
),
],
diff --git a/lib/pages/shopinbit/shopinbit_car_research_payment_view.dart b/lib/pages/shopinbit/shopinbit_car_research_payment_view.dart
index e7e70f1..b55ad44 100644
--- a/lib/pages/shopinbit/shopinbit_car_research_payment_view.dart
+++ b/lib/pages/shopinbit/shopinbit_car_research_payment_view.dart
@@ -679,7 +679,7 @@ class _ShopInBitCarResearchPaymentViewState
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
- "ShopInBit",
+ "ShopinBit",
style: STextStyles.desktopH3(context),
),
),
diff --git a/lib/pages/shopinbit/shopinbit_offer_view.dart b/lib/pages/shopinbit/shopinbit_offer_view.dart
index 873acd5..ace2f3d 100644
--- a/lib/pages/shopinbit/shopinbit_offer_view.dart
+++ b/lib/pages/shopinbit/shopinbit_offer_view.dart
@@ -174,7 +174,7 @@ class _ShopInBitOfferViewState extends State<ShopInBitOfferView> {
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
- "ShopInBit",
+ "ShopinBit",
style: STextStyles.desktopH3(context),
),
),
diff --git a/lib/pages/shopinbit/shopinbit_order_created.dart b/lib/pages/shopinbit/shopinbit_order_created.dart
index bdadf80..d07752c 100644
--- a/lib/pages/shopinbit/shopinbit_order_created.dart
+++ b/lib/pages/shopinbit/shopinbit_order_created.dart
@@ -150,7 +150,7 @@ class ShopInBitOrderCreated extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
- "ShopInBit",
+ "ShopinBit",
style: STextStyles.desktopH3(context),
),
),
diff --git a/lib/pages/shopinbit/shopinbit_payment_view.dart b/lib/pages/shopinbit/shopinbit_payment_view.dart
index e06acb4..1a51cbe 100644
--- a/lib/pages/shopinbit/shopinbit_payment_view.dart
+++ b/lib/pages/shopinbit/shopinbit_payment_view.dart
@@ -786,7 +786,7 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
- "ShopInBit",
+ "ShopinBit",
style: STextStyles.desktopH3(context),
),
),
diff --git a/lib/pages/shopinbit/shopinbit_shipping_view.dart b/lib/pages/shopinbit/shopinbit_shipping_view.dart
index 8f24645..6bb0f6a 100644
--- a/lib/pages/shopinbit/shopinbit_shipping_view.dart
+++ b/lib/pages/shopinbit/shopinbit_shipping_view.dart
@@ -697,7 +697,7 @@ class _ShopInBitShippingViewState extends State<ShopInBitShippingView> {
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
- "ShopInBit",
+ "ShopinBit",
style: STextStyles.desktopH3(context),
),
),
diff --git a/lib/pages/shopinbit/shopinbit_step_1.dart b/lib/pages/shopinbit/shopinbit_step_1.dart
index 21f3f13..6e6a097 100644
--- a/lib/pages/shopinbit/shopinbit_step_1.dart
+++ b/lib/pages/shopinbit/shopinbit_step_1.dart
@@ -55,6 +55,7 @@ class _ShopInBitStep1State extends State<ShopInBitStep1> {
Navigator.of(context, rootNavigator: true).pop();
showDialog<void>(
context: context,
+ barrierDismissible: false,
builder: (_) => ShopInBitStep2(model: widget.model),
);
} else {
@@ -146,7 +147,7 @@ class _ShopInBitStep1State extends State<ShopInBitStep1> {
Padding(
padding: const EdgeInsets.only(left: 32),
child: Text(
- "ShopInBit",
+ "ShopinBit",
style: STextStyles.desktopH3(context),
),
),
diff --git a/lib/pages/shopinbit/shopinbit_step_2.dart b/lib/pages/shopinbit/shopinbit_step_2.dart
index bed8a50..5dfef58 100644
--- a/lib/pages/shopinbit/shopinbit_step_2.dart
+++ b/lib/pages/shopinbit/shopinbit_step_2.dart
@@ -13,6 +13,7 @@ import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
import '../exchange_view/sub_widgets/step_row.dart';
+import 'shopinbit_step_1.dart';
import 'shopinbit_step_3.dart';
import 'shopinbit_step_4.dart';
@@ -41,6 +42,11 @@ class _ShopInBitStep2State extends State<ShopInBitStep2> {
void _popBack() {
if (Util.isDesktop) {
Navigator.of(context, rootNavigator: true).pop();
+ showDialog<void>(
+ context: context,
+ barrierDismissible: false,
+ builder: (_) => ShopInBitStep1(model: widget.model),
+ );
} else {
Navigator.of(context).pop();
}
@@ -56,9 +62,8 @@ class _ShopInBitStep2State extends State<ShopInBitStep2> {
Navigator.of(context, rootNavigator: true).pop();
showDialog<void>(
context: context,
- builder: (_) => skipGuidelines
- ? ShopInBitStep4(model: widget.model)
- : ShopInBitStep3(model: widget.model),
+ barrierDismissible: false,
+ builder: (_) => ShopInBitStep3(model: widget.model),
);
} else {
if (skipGuidelines) {
@@ -92,7 +97,7 @@ class _ShopInBitStep2State extends State<ShopInBitStep2> {
borderRadius: BorderRadius.circular(isDesktop ? 16 : 12),
border: Border.all(
color: isSelected
- ? Theme.of(context).extension<StackColors>()!.accentColorBlue
+ ? Theme.of(context).extension<StackColors>()!.textDark
: Theme.of(context).extension<StackColors>()!.background,
width: 2,
),
@@ -108,7 +113,7 @@ class _ShopInBitStep2State extends State<ShopInBitStep2> {
shape: BoxShape.circle,
color: Theme.of(context)
.extension<StackColors>()!
- .accentColorBlue
+ .textDark
.withOpacity(0.1),
),
alignment: Alignment.center,
@@ -118,7 +123,7 @@ class _ShopInBitStep2State extends State<ShopInBitStep2> {
height: isDesktop ? 24 : 20,
color: Theme.of(context)
.extension<StackColors>()!
- .accentColorBlue,
+ .textDark,
),
),
SizedBox(width: isDesktop ? 16 : 12),
@@ -151,7 +156,7 @@ class _ShopInBitStep2State extends State<ShopInBitStep2> {
Icons.check_circle,
color: Theme.of(
context,
- ).extension<StackColors>()!.accentColorBlue,
+ ).extension<StackColors>()!.textDark,
size: isDesktop ? 24 : 20,
),
],
@@ -223,18 +228,24 @@ class _ShopInBitStep2State extends State<ShopInBitStep2> {
if (isDesktop) {
return DesktopDialog(
maxWidth: 580,
- maxHeight: 580,
+ maxHeight: 700,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "ShopInBit",
- style: STextStyles.desktopH3(context),
- ),
+ Row(
+ children: [
+ AppBarBackButton(
+ isCompact: true,
+ iconSize: 23,
+ onPressed: _popBack,
+ ),
+ Text(
+ "ShopinBit",
+ style: STextStyles.desktopH3(context),
+ ),
+ ],
),
const DesktopDialogCloseButton(),
],
diff --git a/lib/pages/shopinbit/shopinbit_step_3.dart b/lib/pages/shopinbit/shopinbit_step_3.dart
index 2f03093..4438d50 100644
--- a/lib/pages/shopinbit/shopinbit_step_3.dart
+++ b/lib/pages/shopinbit/shopinbit_step_3.dart
@@ -12,6 +12,7 @@ import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
import '../../widgets/rounded_white_container.dart';
import '../exchange_view/sub_widgets/step_row.dart';
+import 'shopinbit_step_2.dart';
import 'shopinbit_step_4.dart';
class ShopInBitStep3 extends StatefulWidget {
@@ -73,6 +74,19 @@ class _ShopInBitStep3State extends State<ShopInBitStep3> {
}
}
+ void _popBack() {
+ if (Util.isDesktop) {
+ Navigator.of(context, rootNavigator: true).pop();
+ showDialog<void>(
+ context: context,
+ barrierDismissible: false,
+ builder: (_) => ShopInBitStep2(model: widget.model),
+ );
+ } else {
+ Navigator.of(context).pop();
+ }
+ }
+
void _continue() {
widget.model.guidelinesAccepted = true;
// Persist acceptance.
@@ -81,6 +95,7 @@ class _ShopInBitStep3State extends State<ShopInBitStep3> {
Navigator.of(context, rootNavigator: true).pop();
showDialog<void>(
context: context,
+ barrierDismissible: false,
builder: (_) => ShopInBitStep4(model: widget.model),
);
} else {
@@ -130,13 +145,14 @@ class _ShopInBitStep3State extends State<ShopInBitStep3> {
),
),
),
- if (!isDesktop)
- CheckboxListTile(
+ CheckboxListTile(
value: _agreed,
onChanged: (v) => setState(() => _agreed = v ?? false),
title: Text(
"I have read and agree to the Service Guidelines",
- style: STextStyles.itemSubtitle12(context),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.itemSubtitle12(context),
),
controlAffinity: ListTileControlAffinity.leading,
contentPadding: EdgeInsets.zero,
@@ -146,8 +162,8 @@ class _ShopInBitStep3State extends State<ShopInBitStep3> {
SizedBox(height: isDesktop ? 24 : 16),
PrimaryButton(
label: "Next",
- enabled: isDesktop || _agreed,
- onPressed: (isDesktop || _agreed) ? _continue : null,
+ enabled: _agreed,
+ onPressed: _agreed ? _continue : null,
),
],
);
@@ -161,12 +177,18 @@ class _ShopInBitStep3State extends State<ShopInBitStep3> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "ShopInBit",
- style: STextStyles.desktopH3(context),
- ),
+ Row(
+ children: [
+ AppBarBackButton(
+ isCompact: true,
+ iconSize: 23,
+ onPressed: _popBack,
+ ),
+ Text(
+ "ShopinBit",
+ style: STextStyles.desktopH3(context),
+ ),
+ ],
),
const DesktopDialogCloseButton(),
],
diff --git a/lib/pages/shopinbit/shopinbit_step_4.dart b/lib/pages/shopinbit/shopinbit_step_4.dart
index a066330..60dbf0b 100644
--- a/lib/pages/shopinbit/shopinbit_step_4.dart
+++ b/lib/pages/shopinbit/shopinbit_step_4.dart
@@ -26,6 +26,7 @@ import '../../widgets/desktop/secondary_button.dart';
import '../../widgets/rounded_white_container.dart';
import '../../widgets/stack_text_field.dart';
import '../exchange_view/sub_widgets/step_row.dart';
+import 'shopinbit_step_3.dart';
import 'shopinbit_car_fee_view.dart';
import 'shopinbit_order_created.dart';
@@ -297,6 +298,11 @@ class _ShopInBitStep4State extends State<ShopInBitStep4> {
void _popBack() {
if (Util.isDesktop) {
Navigator.of(context, rootNavigator: true).pop();
+ showDialog<void>(
+ context: context,
+ barrierDismissible: false,
+ builder: (_) => ShopInBitStep3(model: widget.model),
+ );
} else {
Navigator.of(context).pop();
}
@@ -1361,18 +1367,24 @@ class _ShopInBitStep4State extends State<ShopInBitStep4> {
if (isDesktop) {
return DesktopDialog(
maxWidth: 580,
- maxHeight: 560,
+ maxHeight: 750,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "ShopInBit",
- style: STextStyles.desktopH3(context),
- ),
+ Row(
+ children: [
+ AppBarBackButton(
+ isCompact: true,
+ iconSize: 23,
+ onPressed: _popBack,
+ ),
+ Text(
+ "ShopinBit",
+ style: STextStyles.desktopH3(context),
+ ),
+ ],
),
const DesktopDialogCloseButton(),
],
@@ -1383,7 +1395,7 @@ class _ShopInBitStep4State extends State<ShopInBitStep4> {
horizontal: 32,
vertical: 16,
),
- child: content,
+ child: SingleChildScrollView(child: content),
),
),
],
diff --git a/lib/pages_desktop_specific/more_view/sub_widgets/desktop_shopinbit_view.dart b/lib/pages_desktop_specific/more_view/sub_widgets/desktop_shopinbit_view.dart
index 5ee6bf4..6ee58b5 100644
--- a/lib/pages_desktop_specific/more_view/sub_widgets/desktop_shopinbit_view.dart
+++ b/lib/pages_desktop_specific/more_view/sub_widgets/desktop_shopinbit_view.dart
@@ -1,5 +1,6 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -7,12 +8,16 @@ import 'package:url_launcher/url_launcher.dart';
import '../../../app_config.dart';
import '../../../db/isar/main_db.dart';
import '../../../models/shopinbit/shopinbit_order_model.dart';
+import '../../../notifications/show_flush_bar.dart';
import '../../../pages/shopinbit/shopinbit_step_1.dart';
import '../../../pages/shopinbit/shopinbit_tickets_view.dart';
import '../../../providers/desktop/current_desktop_menu_item.dart';
+import '../../../services/shopinbit/shopinbit_service.dart';
+import '../../../themes/stack_colors.dart';
import '../../../utilities/assets.dart';
import '../../../utilities/text_styles.dart';
import '../../../widgets/desktop/desktop_dialog.dart';
+import '../../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../../widgets/desktop/primary_button.dart';
import '../../../widgets/desktop/secondary_button.dart';
import '../../../widgets/rounded_white_container.dart';
@@ -81,10 +86,31 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
return shouldContinue ?? false;
}
- void _showShopDialog(BuildContext context) {
+ void _showShopDialog(BuildContext context) async {
+ final service = ShopInBitService.instance;
+ final model = ShopInBitOrderModel();
+
+ if (!service.loadSetupComplete()) {
+ // First-time user: show setup.
+ final completed = await showDialog<bool>(
+ context: context,
+ barrierDismissible: false,
+ builder: (_) => _ShopInBitDesktopSetupDialog(model: model),
+ );
+ if (completed != true) return; // user cancelled
+ } else {
+ // Returning user: restore display name.
+ final savedName = service.loadDisplayName();
+ if (savedName != null && savedName.isNotEmpty) {
+ model.displayName = savedName;
+ }
+ }
+
+ // Show warning dialog.
+ if (!mounted) return;
showDialog<void>(
context: context,
- barrierDismissible: true,
+ barrierDismissible: false,
builder: (dialogContext) => DesktopDialog(
maxWidth: 550,
maxHeight: 300,
@@ -93,7 +119,7 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text("ShopInBit", style: STextStyles.desktopH2(dialogContext)),
+ Text("ShopinBit", style: STextStyles.desktopH2(dialogContext)),
const SizedBox(height: 16),
RichText(
text: TextSpan(
@@ -104,28 +130,7 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
"Please note the following before proceeding:"
"\n\n\u2022 Minimum order amount: 1,000 EUR"
"\n\u2022 Service fee: 10% of the order total",
- // "\n\nBy continuing, you agree to the ShopInBit ",
),
- // TextSpan(
- // text: "Privacy Policy",
- // style: STextStyles.richLink(dialogContext).copyWith(
- // fontSize: 18,
- // ),
- // recognizer: TapGestureRecognizer()
- // ..onTap = () async {
- // const url =
- // "https://api.shopinbit.com/static/policy/privacy.html";
- // final shouldOpen =
- // await _showOpenBrowserWarning(dialogContext, url);
- // if (shouldOpen) {
- // await launchUrl(
- // Uri.parse(url),
- // mode: LaunchMode.externalApplication,
- // );
- // }
- // },
- // ),
- // const TextSpan(text: "."),
],
),
),
@@ -150,8 +155,9 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
Navigator.of(dialogContext, rootNavigator: true).pop();
await showDialog<void>(
context: context,
+ barrierDismissible: false,
builder: (_) =>
- ShopInBitStep1(model: ShopInBitOrderModel()),
+ ShopInBitStep1(model: model),
);
if (mounted) setState(() {});
},
@@ -192,16 +198,19 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
style: STextStyles.desktopTextExtraExtraSmall(context),
children: [
TextSpan(
- text: "ShopInBit",
+ text: "ShopinBit",
style: STextStyles.desktopTextSmall(context),
),
const TextSpan(
text:
- "\n\nConcierge shopping service. Purchase "
- "products and services using cryptocurrency.\n\n"
+ "\n\nTurn your crypto into Electronics, Flights, Hotel, "
+ "Cars or any other legal product or service... "
+ "ShopinBit is a concierge shopping service that helps "
+ "you 'live the good life with crypto'..."
+ "\n\n"
"Minimum order value of 1,000 EUR. "
"A 10% service fee applies to all orders.\n\n"
- "By using ShopInBit, you agree to their ",
+ "By using ShopinBit, you agree to their ",
),
TextSpan(
text: "Terms & Conditions",
@@ -259,7 +268,7 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
width: 250,
buttonHeight: ButtonHeight.m,
enabled: true,
- label: "Shop with ShopInBit",
+ label: "Shop with ShopinBit",
onPressed: () => _showShopDialog(context),
),
const SizedBox(width: 16),
@@ -272,8 +281,8 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
width: 200,
buttonHeight: ButtonHeight.m,
label: count > 0
- ? "My tickets ($count)"
- : "My tickets",
+ ? "My requests ($count)"
+ : "My requests",
onPressed: () async {
await showDialog<void>(
context: context,
@@ -316,3 +325,173 @@ class _DesktopServicesViewState extends ConsumerState<DesktopShopInBitView> {
);
}
}
+
+class _ShopInBitDesktopSetupDialog extends StatefulWidget {
+ const _ShopInBitDesktopSetupDialog({required this.model});
+
+ final ShopInBitOrderModel model;
+
+ @override
+ State<_ShopInBitDesktopSetupDialog> createState() =>
+ _ShopInBitDesktopSetupDialogState();
+}
+
+class _ShopInBitDesktopSetupDialogState
+ extends State<_ShopInBitDesktopSetupDialog> {
+ late final Future<String> _keyFuture;
+ late final TextEditingController _nameController;
+ late final FocusNode _nameFocusNode;
+
+ bool get _canContinue => _nameController.text.trim().isNotEmpty;
+
+ @override
+ void initState() {
+ super.initState();
+ _keyFuture = ShopInBitService.instance.ensureCustomerKey();
+ _nameController = TextEditingController();
+ _nameFocusNode = FocusNode();
+
+ _nameFocusNode.addListener(() {
+ setState(() {});
+ });
+ }
+
+ @override
+ void dispose() {
+ _nameController.dispose();
+ _nameFocusNode.dispose();
+ super.dispose();
+ }
+
+ Future<void> _completeSetup() async {
+ final name = _nameController.text.trim();
+ widget.model.displayName = name;
+ await ShopInBitService.instance.setDisplayName(name);
+ await ShopInBitService.instance.setSetupComplete(true);
+ if (mounted) {
+ Navigator.of(context, rootNavigator: true).pop(true);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return DesktopDialog(
+ maxWidth: 580,
+ maxHeight: 500,
+ child: Column(
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "ShopinBit Setup",
+ style: STextStyles.desktopH3(context),
+ ),
+ ),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Text(
+ "Your Customer Key",
+ style: STextStyles.desktopTextSmall(context).copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 8),
+ Text(
+ "This is your ShopinBit customer key: save it "
+ "somewhere safe, you'll need it to recover "
+ "your ShopinBit account on a new device.",
+ style:
+ STextStyles.desktopTextExtraExtraSmall(context),
+ ),
+ const SizedBox(height: 12),
+ FutureBuilder<String>(
+ future: _keyFuture,
+ builder: (context, snapshot) {
+ if (snapshot.connectionState != ConnectionState.done) {
+ return const Center(
+ child: CircularProgressIndicator(),
+ );
+ }
+ if (snapshot.hasError) {
+ return Text(
+ "Failed to generate key. Please try again.",
+ style: STextStyles.desktopTextSmall(context).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textError,
+ ),
+ );
+ }
+ final key = snapshot.data!;
+ return RoundedWhiteContainer(
+ child: Row(
+ children: [
+ Expanded(
+ child: SelectableText(
+ key,
+ style:
+ STextStyles.desktopTextExtraExtraSmall(
+ context,
+ ),
+ ),
+ ),
+ IconButton(
+ icon: const Icon(Icons.copy, size: 20),
+ onPressed: () {
+ Clipboard.setData(
+ ClipboardData(text: key),
+ );
+ showFloatingFlushBar(
+ type: FlushBarType.info,
+ message: "Copied to clipboard!",
+ context: context,
+ );
+ },
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ const SizedBox(height: 24),
+ Text(
+ "Display Name",
+ style: STextStyles.desktopTextSmall(context).copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ const SizedBox(height: 8),
+ TextField(
+ controller: _nameController,
+ focusNode: _nameFocusNode,
+ onChanged: (_) => setState(() {}),
+ style: STextStyles.desktopTextSmall(context),
+ decoration: const InputDecoration(
+ hintText: "Display name",
+ ),
+ ),
+ const Spacer(),
+ PrimaryButton(
+ label: "Complete Setup",
+ enabled: _canContinue,
+ onPressed: _canContinue ? _completeSetup : null,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/pages_desktop_specific/settings/settings_menu.dart b/lib/pages_desktop_specific/settings/settings_menu.dart
index 27f816e..a7f5129 100644
--- a/lib/pages_desktop_specific/settings/settings_menu.dart
+++ b/lib/pages_desktop_specific/settings/settings_menu.dart
@@ -46,7 +46,7 @@ class _SettingsMenuState extends ConsumerState<SettingsMenu> {
"Syncing preferences",
if (AppConfig.hasFeature(AppFeature.themeSelection)) "Appearance",
"Advanced",
- if (familiarity >= 6) "ShopInBit",
+ if (familiarity >= 6) "ShopinBit",
];
return Column(
diff --git a/lib/pages_desktop_specific/settings/settings_menu/shopinbit_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/shopinbit_settings.dart
index 1d39a5d..d65b088 100644
--- a/lib/pages_desktop_specific/settings/settings_menu/shopinbit_settings.dart
+++ b/lib/pages_desktop_specific/settings/settings_menu/shopinbit_settings.dart
@@ -178,7 +178,7 @@ class _ShopInBitDesktopSettingsState
const SizedBox(height: 16),
Text(
"Changing your key will disconnect you from "
- "existing ShopInBit conversations. Make sure "
+ "existing ShopinBit requests. Make sure "
"you have saved your current key before "
"proceeding.",
style: STextStyles.desktopTextExtraExtraSmall(ctx),
@@ -345,7 +345,7 @@ class _ShopInBitDesktopSettingsState
),
const SizedBox(height: 16),
Text(
- "Your customer key identifies you to ShopInBit. "
+ "Your customer key identifies you to ShopinBit. "
"Save it to restore access to your conversations "
"on another device. If you change it, you will "
"lose access to existing conversations.",
@@ -427,7 +427,7 @@ class _ShopInBitDesktopSettingsState
const SizedBox(height: 8),
Text(
"Enter a previously saved customer key to "
- "restore access to your ShopInBit "
+ "restore access to your ShopinBit "
"conversations.",
style: STextStyles.desktopTextExtraExtraSmall(context),
),
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.