ponshu-room-lite/lib/screens/license_screen.dart

241 lines
8.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/license_provider.dart';
import '../services/license_service.dart';
import '../theme/app_colors.dart';
import 'package:url_launcher/url_launcher.dart';
class LicenseScreen extends ConsumerStatefulWidget {
const LicenseScreen({super.key});
@override
ConsumerState<LicenseScreen> createState() => _LicenseScreenState();
}
class _LicenseScreenState extends ConsumerState<LicenseScreen> {
final _keyController = TextEditingController();
bool _isLoading = false;
String? _errorMessage;
@override
void dispose() {
_keyController.dispose();
super.dispose();
}
Future<void> _activate() async {
final key = _keyController.text.trim();
if (key.isEmpty) return;
setState(() {
_isLoading = true;
_errorMessage = null;
});
final result = await LicenseService.activate(key);
if (mounted) {
if (result.success) {
ref.invalidate(licenseStatusProvider);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Text('Pro版ライセンスを有効化しました'),
backgroundColor: Theme.of(context).extension<AppColors>()!.success,
),
);
Navigator.of(context).pop();
} else {
setState(() {
_errorMessage = result.message;
_isLoading = false;
});
}
}
}
void _openStore() async {
const storeUrl = 'https://store.posimai.soar-enrich.com';
final uri = Uri.parse(storeUrl);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).extension<AppColors>()!;
final isPro = ref.watch(isProProvider);
return Scaffold(
backgroundColor: colors.brandSurface,
appBar: AppBar(
title: Text(
'ライセンスの有効化',
style: TextStyle(color: colors.textPrimary, fontWeight: FontWeight.bold),
),
backgroundColor: colors.brandSurface,
elevation: 0,
iconTheme: IconThemeData(color: colors.textPrimary),
),
body: SafeArea(
child: isPro ? _buildProState(colors) : _buildActivationForm(colors),
),
);
}
Widget _buildProState(AppColors colors) {
return Center(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.verified, size: 64, color: colors.success),
const SizedBox(height: 24),
Text(
'Pro版ライセンス有効',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: colors.textPrimary,
),
),
const SizedBox(height: 16),
Text(
'すべてのPro機能をご利用いただけます。\nご購入ありがとうございました。',
textAlign: TextAlign.center,
style: TextStyle(color: colors.textSecondary, height: 1.5),
),
],
),
),
);
}
Widget _buildActivationForm(AppColors colors) {
return SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Icon(Icons.key, size: 64, color: colors.brandPrimary),
const SizedBox(height: 24),
Text(
'ご購入ありがとうございます。',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: colors.textPrimary,
),
),
const SizedBox(height: 8),
Text(
'メールで受け取った「PONSHU-」から始まる\nライセンスキーを入力してください。',
textAlign: TextAlign.center,
style: TextStyle(color: colors.textSecondary, height: 1.5),
),
const SizedBox(height: 32),
TextField(
controller: _keyController,
decoration: InputDecoration(
hintText: 'PONSHU-XXXX-XXXX-XXXX',
filled: true,
fillColor: colors.surfaceSubtle,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: colors.divider),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: colors.divider),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: colors.brandPrimary, width: 2),
),
prefixIcon: Icon(Icons.vpn_key_outlined, color: colors.iconSubtle),
),
textCapitalization: TextCapitalization.characters,
onSubmitted: (_) => _activate(),
),
if (_errorMessage != null) ...[
const SizedBox(height: 16),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: colors.error.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: colors.error.withValues(alpha: 0.3)),
),
child: Row(
children: [
Icon(Icons.error_outline, color: colors.error, size: 20),
const SizedBox(width: 8),
Expanded(
child: Text(
_errorMessage!,
style: TextStyle(color: colors.error, fontSize: 13),
),
),
],
),
),
],
const SizedBox(height: 24),
ElevatedButton(
onPressed: _isLoading ? null : _activate,
style: ElevatedButton.styleFrom(
backgroundColor: colors.brandPrimary,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
),
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
)
: const Text(
'有効化する',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 48),
Divider(color: colors.divider),
const SizedBox(height: 24),
Text(
'ライセンスをお持ちでない方',
textAlign: TextAlign.center,
style: TextStyle(color: colors.textSecondary, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
OutlinedButton(
onPressed: _openStore,
style: OutlinedButton.styleFrom(
foregroundColor: colors.brandPrimary,
side: BorderSide(color: colors.brandPrimary),
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.shopping_cart_outlined, size: 20),
SizedBox(width: 8),
Text('ストアで購入する', style: TextStyle(fontWeight: FontWeight.bold)),
],
),
),
],
),
);
}
}