Replace blocked content popup with Instagram-style inline banner
This commit is contained in:
parent
f6c4bb88e0
commit
b5002c1ce4
|
|
@ -39,6 +39,7 @@ class _ComposeScreenState extends ConsumerState<ComposeScreen> {
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
bool _isUploadingImage = false;
|
bool _isUploadingImage = false;
|
||||||
String? _errorMessage;
|
String? _errorMessage;
|
||||||
|
String? _blockedMessage;
|
||||||
final int _maxCharacters = 500;
|
final int _maxCharacters = 500;
|
||||||
bool _allowChain = true;
|
bool _allowChain = true;
|
||||||
bool _isBold = false;
|
bool _isBold = false;
|
||||||
|
|
@ -69,6 +70,10 @@ class _ComposeScreenState extends ConsumerState<ComposeScreen> {
|
||||||
_bodyController.addListener(() {
|
_bodyController.addListener(() {
|
||||||
_charCountNotifier.value = _bodyController.text.length;
|
_charCountNotifier.value = _bodyController.text.length;
|
||||||
_handleHashtagSuggestions();
|
_handleHashtagSuggestions();
|
||||||
|
// Clear blocked banner when user edits their text
|
||||||
|
if (_blockedMessage != null) {
|
||||||
|
setState(() => _blockedMessage = null);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
@ -326,7 +331,7 @@ class _ComposeScreenState extends ConsumerState<ComposeScreen> {
|
||||||
// Layer 0: Client-side hard blocklist — never even send to server
|
// Layer 0: Client-side hard blocklist — never even send to server
|
||||||
final blockMessage = ContentFilter.instance.check(_bodyController.text.trim());
|
final blockMessage = ContentFilter.instance.check(_bodyController.text.trim());
|
||||||
if (blockMessage != null) {
|
if (blockMessage != null) {
|
||||||
await _showBlockedDialog(blockMessage);
|
setState(() => _blockedMessage = blockMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,7 +416,7 @@ class _ComposeScreenState extends ConsumerState<ComposeScreen> {
|
||||||
final msg = e.toString().replaceAll('Exception: ', '');
|
final msg = e.toString().replaceAll('Exception: ', '');
|
||||||
// Server-side blocklist catch (422 with blocked content message)
|
// Server-side blocklist catch (422 with blocked content message)
|
||||||
if (msg.contains("isn't allowed on Sojorn") || msg.contains('not allowed')) {
|
if (msg.contains("isn't allowed on Sojorn") || msg.contains('not allowed')) {
|
||||||
if (mounted) await _showBlockedDialog(msg);
|
if (mounted) setState(() => _blockedMessage = msg);
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
_errorMessage = msg;
|
_errorMessage = msg;
|
||||||
|
|
@ -454,35 +459,60 @@ class _ComposeScreenState extends ConsumerState<ComposeScreen> {
|
||||||
return result ?? false;
|
return result ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showBlockedDialog(String message) async {
|
Widget _buildBlockedBanner() {
|
||||||
await showDialog<void>(
|
return AnimatedSize(
|
||||||
context: context,
|
duration: const Duration(milliseconds: 250),
|
||||||
barrierDismissible: false,
|
curve: Curves.easeOut,
|
||||||
builder: (context) => AlertDialog(
|
child: _blockedMessage != null
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
? Container(
|
||||||
title: Row(
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: AppTheme.spacingMd,
|
||||||
|
vertical: 12,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFFEF2F2),
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: AppTheme.error.withValues(alpha: 0.2),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.block, color: AppTheme.error, size: 24),
|
Icon(Icons.info_outline, color: AppTheme.error, size: 20),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 10),
|
||||||
const Text('Not Allowed'),
|
Expanded(
|
||||||
],
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_blockedMessage!,
|
||||||
|
style: AppTheme.textTheme.bodySmall?.copyWith(
|
||||||
|
color: const Color(0xFF991B1B),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
height: 1.4,
|
||||||
),
|
),
|
||||||
content: Text(
|
|
||||||
message,
|
|
||||||
style: AppTheme.textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: AppTheme.brightNavy,
|
|
||||||
foregroundColor: AppTheme.white,
|
|
||||||
shape: const StadiumBorder(),
|
|
||||||
),
|
|
||||||
child: const Text('Edit My Post'),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => setState(() => _blockedMessage = null),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8),
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 16,
|
||||||
|
color: AppTheme.error.withValues(alpha: 0.6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -515,6 +545,7 @@ class _ComposeScreenState extends ConsumerState<ComposeScreen> {
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
_buildBlockedBanner(),
|
||||||
if (_errorMessage != null)
|
if (_errorMessage != null)
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue