🐛 Fixes on post compose
This commit is contained in:
@ -2,10 +2,13 @@ import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_highlight/themes/a11y-dark.dart';
|
||||
import 'package:flutter_highlight/themes/a11y-light.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/content/markdown_latex.dart';
|
||||
import 'package:markdown/markdown.dart' as markdown;
|
||||
import 'package:markdown_widget/markdown_widget.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
@ -18,6 +21,7 @@ class MarkdownTextContent extends HookConsumerWidget {
|
||||
final TextScaler? textScaler;
|
||||
final TextStyle? textStyle;
|
||||
final TextStyle? linkStyle;
|
||||
final EdgeInsets? linesMargin;
|
||||
final bool isSelectable;
|
||||
|
||||
const MarkdownTextContent({
|
||||
@ -28,6 +32,7 @@ class MarkdownTextContent extends HookConsumerWidget {
|
||||
this.textStyle,
|
||||
this.linkStyle,
|
||||
this.isSelectable = false,
|
||||
this.linesMargin,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -54,19 +59,13 @@ class MarkdownTextContent extends HookConsumerWidget {
|
||||
config: config.copy(
|
||||
configs: [
|
||||
isDark
|
||||
? PreConfig.darkConfig.copy(
|
||||
textStyle: textStyle,
|
||||
padding: EdgeInsets.zero,
|
||||
margin: EdgeInsets.zero,
|
||||
)
|
||||
: PreConfig().copy(
|
||||
textStyle: textStyle,
|
||||
padding: EdgeInsets.zero,
|
||||
margin: EdgeInsets.zero,
|
||||
),
|
||||
? PreConfig.darkConfig.copy(textStyle: textStyle)
|
||||
: PreConfig().copy(textStyle: textStyle),
|
||||
PConfig(
|
||||
textStyle: textStyle ?? Theme.of(context).textTheme.bodyMedium!,
|
||||
),
|
||||
HrConfig(height: 1, color: Theme.of(context).dividerColor),
|
||||
PreConfig(theme: isDark ? a11yDarkTheme : a11yLightTheme),
|
||||
LinkConfig(
|
||||
style:
|
||||
linkStyle ??
|
||||
@ -146,8 +145,13 @@ class MarkdownTextContent extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
generator: MarkdownGenerator(
|
||||
inlineSyntaxList: [_UserNameCardInlineSyntax(), _StickerInlineSyntax()],
|
||||
linesMargin: EdgeInsets.zero,
|
||||
generators: [latexGenerator],
|
||||
inlineSyntaxList: [
|
||||
_UserNameCardInlineSyntax(),
|
||||
_StickerInlineSyntax(),
|
||||
LatexSyntax(isDark),
|
||||
],
|
||||
linesMargin: linesMargin ?? EdgeInsets.symmetric(vertical: 4),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
80
lib/widgets/content/markdown_latex.dart
Normal file
80
lib/widgets/content/markdown_latex.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:markdown_widget/markdown_widget.dart';
|
||||
import 'package:flutter_math_fork/flutter_math.dart';
|
||||
import 'package:markdown/markdown.dart' as m;
|
||||
|
||||
SpanNodeGeneratorWithTag latexGenerator = SpanNodeGeneratorWithTag(
|
||||
tag: _latexTag,
|
||||
generator:
|
||||
(e, config, visitor) => LatexNode(e.attributes, e.textContent, config),
|
||||
);
|
||||
|
||||
const _latexTag = 'latex';
|
||||
|
||||
class LatexSyntax extends m.InlineSyntax {
|
||||
final bool isDark;
|
||||
LatexSyntax(this.isDark) : super(r'(\$\$[\s\S]+\$\$)|(\$.+?\$)');
|
||||
|
||||
@override
|
||||
bool onMatch(m.InlineParser parser, Match match) {
|
||||
final input = match.input;
|
||||
final matchValue = input.substring(match.start, match.end);
|
||||
String content = '';
|
||||
bool isInline = true;
|
||||
const blockSyntax = '\$\$';
|
||||
const inlineSyntax = '\$';
|
||||
if (matchValue.startsWith(blockSyntax) &&
|
||||
matchValue.endsWith(blockSyntax) &&
|
||||
(matchValue != blockSyntax)) {
|
||||
content = matchValue.substring(2, matchValue.length - 2);
|
||||
isInline = false;
|
||||
} else if (matchValue.startsWith(inlineSyntax) &&
|
||||
matchValue.endsWith(inlineSyntax) &&
|
||||
matchValue != inlineSyntax) {
|
||||
content = matchValue.substring(1, matchValue.length - 1);
|
||||
}
|
||||
m.Element el = m.Element.text(_latexTag, matchValue);
|
||||
el.attributes['content'] = content;
|
||||
el.attributes['isInline'] = '$isInline';
|
||||
el.attributes['isDark'] = isDark.toString();
|
||||
parser.addNode(el);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class LatexNode extends SpanNode {
|
||||
final Map<String, String> attributes;
|
||||
final String textContent;
|
||||
final MarkdownConfig config;
|
||||
|
||||
LatexNode(this.attributes, this.textContent, this.config);
|
||||
|
||||
@override
|
||||
InlineSpan build() {
|
||||
final content = attributes['content'] ?? '';
|
||||
final isInline = attributes['isInline'] == 'true';
|
||||
final isDark = attributes['isDark'] == 'true';
|
||||
final style = parentStyle ?? config.p.textStyle;
|
||||
if (content.isEmpty) return TextSpan(style: style, text: textContent);
|
||||
final latex = Math.tex(
|
||||
content,
|
||||
mathStyle: MathStyle.text,
|
||||
textStyle: style.copyWith(color: isDark ? Colors.white : Colors.black),
|
||||
textScaleFactor: 1,
|
||||
onErrorFallback: (error) {
|
||||
return Text(textContent, style: style.copyWith(color: Colors.red));
|
||||
},
|
||||
);
|
||||
return WidgetSpan(
|
||||
alignment: PlaceholderAlignment.middle,
|
||||
child:
|
||||
!isInline
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
margin: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Center(child: latex),
|
||||
)
|
||||
: latex,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
@ -23,6 +24,9 @@ class ComposeSettingsSheet extends HookWidget {
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
|
||||
// Listen to visibility changes to trigger rebuilds
|
||||
final currentVisibility = useValueListenable(visibility);
|
||||
|
||||
IconData getVisibilityIcon(int visibilityValue) {
|
||||
switch (visibilityValue) {
|
||||
case 1:
|
||||
@ -71,38 +75,39 @@ class ComposeSettingsSheet extends HookWidget {
|
||||
void showVisibilitySheet() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => SheetScaffold(
|
||||
titleText: 'postVisibility'.tr(),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
0,
|
||||
Symbols.public,
|
||||
'postVisibilityPublic',
|
||||
builder:
|
||||
(context) => SheetScaffold(
|
||||
titleText: 'postVisibility'.tr(),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
0,
|
||||
Symbols.public,
|
||||
'postVisibilityPublic',
|
||||
),
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
1,
|
||||
Symbols.group,
|
||||
'postVisibilityFriends',
|
||||
),
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
2,
|
||||
Symbols.link_off,
|
||||
'postVisibilityUnlisted',
|
||||
),
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
3,
|
||||
Symbols.lock,
|
||||
'postVisibilityPrivate',
|
||||
),
|
||||
],
|
||||
),
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
1,
|
||||
Symbols.group,
|
||||
'postVisibilityFriends',
|
||||
),
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
2,
|
||||
Symbols.link_off,
|
||||
'postVisibilityUnlisted',
|
||||
),
|
||||
buildVisibilityOption(
|
||||
context,
|
||||
3,
|
||||
Symbols.lock,
|
||||
'postVisibilityPrivate',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -124,10 +129,11 @@ class ComposeSettingsSheet extends HookWidget {
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
),
|
||||
style: theme.textTheme.titleLarge,
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
style: theme.textTheme.titleMedium,
|
||||
onTapOutside:
|
||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Gap(16),
|
||||
|
||||
// Description field
|
||||
TextField(
|
||||
@ -140,25 +146,23 @@ class ComposeSettingsSheet extends HookWidget {
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
),
|
||||
style: theme.textTheme.bodyLarge,
|
||||
style: theme.textTheme.bodyMedium,
|
||||
maxLines: 3,
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
onTapOutside:
|
||||
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const Gap(16),
|
||||
|
||||
// Visibility setting
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: colorScheme.outline,
|
||||
width: 1,
|
||||
),
|
||||
border: Border.all(color: colorScheme.outline, width: 1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: ListTile(
|
||||
leading: Icon(getVisibilityIcon(visibility.value)),
|
||||
leading: Icon(getVisibilityIcon(currentVisibility)),
|
||||
title: Text('postVisibility'.tr()),
|
||||
subtitle: Text(getVisibilityText(visibility.value).tr()),
|
||||
subtitle: Text(getVisibilityText(currentVisibility).tr()),
|
||||
trailing: const Icon(Symbols.chevron_right),
|
||||
onTap: showVisibilitySheet,
|
||||
shape: RoundedRectangleBorder(
|
||||
@ -175,4 +179,4 @@ class ComposeSettingsSheet extends HookWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,11 @@ class PostItem extends HookConsumerWidget {
|
||||
),
|
||||
).padding(bottom: 8),
|
||||
if (item.content?.isNotEmpty ?? false)
|
||||
MarkdownTextContent(content: item.content!),
|
||||
MarkdownTextContent(
|
||||
content: item.content!,
|
||||
linesMargin:
|
||||
item.type == 0 ? EdgeInsets.zero : null,
|
||||
),
|
||||
// Show truncation hint if post is truncated
|
||||
if (item.isTruncated && !isFullPost)
|
||||
_PostTruncateHint(),
|
||||
|
Reference in New Issue
Block a user