🐛 Fixes on post compose
This commit is contained in:
parent
9482594117
commit
ffbe399614
@ -8,6 +8,10 @@ class AppRouter extends RootStackRouter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
List<AutoRoute> get routes => [
|
List<AutoRoute> get routes => [
|
||||||
|
AutoRoute(page: PostComposeRoute.page, path: '/posts/compose'),
|
||||||
|
AutoRoute(page: PostEditRoute.page, path: '/posts/:id/edit'),
|
||||||
|
AutoRoute(page: CallRoute.page, path: '/chat/:id/call'),
|
||||||
|
AutoRoute(page: EventCalanderRoute.page, path: '/account/:name/calendar'),
|
||||||
AutoRoute(
|
AutoRoute(
|
||||||
page: TabsRoute.page,
|
page: TabsRoute.page,
|
||||||
path: '/',
|
path: '/',
|
||||||
@ -52,10 +56,6 @@ class AppRouter extends RootStackRouter {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
AutoRoute(page: PostComposeRoute.page, path: '/posts/compose'),
|
|
||||||
AutoRoute(page: PostEditRoute.page, path: '/posts/:id/edit'),
|
|
||||||
AutoRoute(page: CallRoute.page, path: '/chat/:id/call'),
|
|
||||||
AutoRoute(page: EventCalanderRoute.page, path: '/account/:name/calendar'),
|
|
||||||
AutoRoute(
|
AutoRoute(
|
||||||
page: CreatorHubShellRoute.page,
|
page: CreatorHubShellRoute.page,
|
||||||
path: '/creators',
|
path: '/creators',
|
||||||
|
@ -32,11 +32,13 @@ class PostEditScreen extends HookConsumerWidget {
|
|||||||
data: (post) => PostComposeScreen(originalPost: post),
|
data: (post) => PostComposeScreen(originalPost: post),
|
||||||
loading:
|
loading:
|
||||||
() => AppScaffold(
|
() => AppScaffold(
|
||||||
|
noBackground: false,
|
||||||
appBar: AppBar(leading: const PageBackButton()),
|
appBar: AppBar(leading: const PageBackButton()),
|
||||||
body: const Center(child: CircularProgressIndicator()),
|
body: const Center(child: CircularProgressIndicator()),
|
||||||
),
|
),
|
||||||
error:
|
error:
|
||||||
(e, _) => AppScaffold(
|
(e, _) => AppScaffold(
|
||||||
|
noBackground: false,
|
||||||
appBar: AppBar(leading: const PageBackButton()),
|
appBar: AppBar(leading: const PageBackButton()),
|
||||||
body: Text('Error: $e', textAlign: TextAlign.center),
|
body: Text('Error: $e', textAlign: TextAlign.center),
|
||||||
),
|
),
|
||||||
@ -117,32 +119,6 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showKeyboardShortcutsDialog() {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder:
|
|
||||||
(context) => AlertDialog(
|
|
||||||
title: Text('keyboardShortcuts'.tr()),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('Ctrl/Cmd + Enter: ${'submit'.tr()}'),
|
|
||||||
Text('Ctrl/Cmd + V: ${'paste'.tr()}'),
|
|
||||||
Text('Ctrl/Cmd + I: ${'add_image'.tr()}'),
|
|
||||||
Text('Ctrl/Cmd + Shift + V: ${'add_video'.tr()}'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text('close'.tr()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildWideAttachmentGrid() {
|
Widget buildWideAttachmentGrid() {
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
@ -219,14 +195,6 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
onPressed: showSettingsSheet,
|
onPressed: showSettingsSheet,
|
||||||
tooltip: 'postSettings'.tr(),
|
tooltip: 'postSettings'.tr(),
|
||||||
),
|
),
|
||||||
if (isWideScreen(context))
|
|
||||||
Tooltip(
|
|
||||||
message: 'keyboardShortcuts'.tr(),
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Symbols.keyboard),
|
|
||||||
onPressed: showKeyboardShortcutsDialog,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValueListenableBuilder<bool>(
|
ValueListenableBuilder<bool>(
|
||||||
valueListenable: state.submitting,
|
valueListenable: state.submitting,
|
||||||
builder: (context, submitting, _) {
|
builder: (context, submitting, _) {
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
import 'package:island/models/file.dart';
|
import 'package:island/models/file.dart';
|
||||||
import 'package:island/models/post.dart';
|
import 'package:island/models/post.dart';
|
||||||
import 'package:island/screens/creators/publishers.dart';
|
import 'package:island/screens/creators/publishers.dart';
|
||||||
import 'package:island/services/responsive.dart';
|
import 'package:island/services/responsive.dart';
|
||||||
|
|
||||||
import 'package:island/widgets/app_scaffold.dart';
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
import 'package:island/screens/posts/detail.dart';
|
import 'package:island/screens/posts/detail.dart';
|
||||||
import 'package:island/widgets/content/attachment_preview.dart';
|
import 'package:island/widgets/content/attachment_preview.dart';
|
||||||
|
import 'package:island/widgets/content/markdown.dart';
|
||||||
import 'package:island/widgets/post/compose_shared.dart';
|
import 'package:island/widgets/post/compose_shared.dart';
|
||||||
import 'package:island/widgets/post/publishers_modal.dart';
|
import 'package:island/widgets/post/publishers_modal.dart';
|
||||||
import 'package:island/widgets/content/cloud_files.dart';
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
@ -94,33 +92,6 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showKeyboardShortcutsDialog() {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder:
|
|
||||||
(context) => AlertDialog(
|
|
||||||
title: Text('keyboardShortcuts'.tr()),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('Ctrl/Cmd + Enter: ${'submit'.tr()}'),
|
|
||||||
Text('Ctrl/Cmd + V: ${'paste'.tr()}'),
|
|
||||||
Text('Ctrl/Cmd + I: ${'add_image'.tr()}'),
|
|
||||||
Text('Ctrl/Cmd + Shift + V: ${'add_video'.tr()}'),
|
|
||||||
Text('Ctrl/Cmd + P: ${'toggle_preview'.tr()}'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
child: Text('close'.tr()),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildPreviewPane() {
|
Widget buildPreviewPane() {
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -150,33 +121,49 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: ValueListenableBuilder<TextEditingValue>(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
valueListenable: state.titleController,
|
||||||
children: [
|
builder: (context, titleValue, _) {
|
||||||
if (state.titleController.text.isNotEmpty) ...[
|
return ValueListenableBuilder<TextEditingValue>(
|
||||||
Text(
|
valueListenable: state.descriptionController,
|
||||||
state.titleController.text,
|
builder: (context, descriptionValue, _) {
|
||||||
style: theme.textTheme.headlineSmall?.copyWith(
|
return ValueListenableBuilder<TextEditingValue>(
|
||||||
fontWeight: FontWeight.bold,
|
valueListenable: state.contentController,
|
||||||
),
|
builder: (context, contentValue, _) {
|
||||||
),
|
return Column(
|
||||||
const Gap(16),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
],
|
children: [
|
||||||
if (state.descriptionController.text.isNotEmpty) ...[
|
if (titleValue.text.isNotEmpty) ...[
|
||||||
Text(
|
Text(
|
||||||
state.descriptionController.text,
|
titleValue.text,
|
||||||
style: theme.textTheme.bodyLarge?.copyWith(
|
style: theme.textTheme.headlineSmall
|
||||||
color: colorScheme.onSurface.withOpacity(0.7),
|
?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
),
|
const Gap(16),
|
||||||
const Gap(16),
|
],
|
||||||
],
|
if (descriptionValue.text.isNotEmpty) ...[
|
||||||
if (state.contentController.text.isNotEmpty)
|
Text(
|
||||||
Text(
|
descriptionValue.text,
|
||||||
state.contentController.text,
|
style: theme.textTheme.bodyLarge?.copyWith(
|
||||||
style: theme.textTheme.bodyMedium,
|
color: colorScheme.onSurface.withOpacity(
|
||||||
),
|
0.7,
|
||||||
],
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
],
|
||||||
|
if (contentValue.text.isNotEmpty)
|
||||||
|
MarkdownTextContent(
|
||||||
|
content: contentValue.text,
|
||||||
|
textStyle: theme.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -191,6 +178,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
// Publisher row
|
// Publisher row
|
||||||
Card(
|
Card(
|
||||||
|
margin: EdgeInsets.only(bottom: 8),
|
||||||
elevation: 1,
|
elevation: 1,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
@ -316,8 +304,17 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
|
noBackground: false,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
leading: const PageBackButton(),
|
leading: const PageBackButton(),
|
||||||
|
title: ValueListenableBuilder<TextEditingValue>(
|
||||||
|
valueListenable: state.titleController,
|
||||||
|
builder: (context, titleValue, _) {
|
||||||
|
return Text(
|
||||||
|
titleValue.text.isEmpty ? 'postTitle'.tr() : titleValue.text,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
// Info banner for article compose
|
// Info banner for article compose
|
||||||
const SizedBox.shrink(),
|
const SizedBox.shrink(),
|
||||||
@ -333,14 +330,6 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
onPressed: () => showPreview.value = !showPreview.value,
|
onPressed: () => showPreview.value = !showPreview.value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isWideScreen(context))
|
|
||||||
Tooltip(
|
|
||||||
message: 'keyboardShortcuts'.tr(),
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Symbols.keyboard),
|
|
||||||
onPressed: showKeyboardShortcutsDialog,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ValueListenableBuilder<bool>(
|
ValueListenableBuilder<bool>(
|
||||||
valueListenable: state.submitting,
|
valueListenable: state.submitting,
|
||||||
builder: (context, submitting, _) {
|
builder: (context, submitting, _) {
|
||||||
@ -378,7 +367,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
child:
|
child:
|
||||||
isWideScreen(context)
|
isWideScreen(context)
|
||||||
? Row(
|
? Row(
|
||||||
|
@ -2,10 +2,13 @@ import 'package:auto_route/auto_route.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.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:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/widgets/alert.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/markdown.dart' as markdown;
|
||||||
import 'package:markdown_widget/markdown_widget.dart';
|
import 'package:markdown_widget/markdown_widget.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
@ -18,6 +21,7 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
final TextScaler? textScaler;
|
final TextScaler? textScaler;
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
final TextStyle? linkStyle;
|
final TextStyle? linkStyle;
|
||||||
|
final EdgeInsets? linesMargin;
|
||||||
final bool isSelectable;
|
final bool isSelectable;
|
||||||
|
|
||||||
const MarkdownTextContent({
|
const MarkdownTextContent({
|
||||||
@ -28,6 +32,7 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.linkStyle,
|
this.linkStyle,
|
||||||
this.isSelectable = false,
|
this.isSelectable = false,
|
||||||
|
this.linesMargin,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -54,19 +59,13 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
config: config.copy(
|
config: config.copy(
|
||||||
configs: [
|
configs: [
|
||||||
isDark
|
isDark
|
||||||
? PreConfig.darkConfig.copy(
|
? PreConfig.darkConfig.copy(textStyle: textStyle)
|
||||||
textStyle: textStyle,
|
: PreConfig().copy(textStyle: textStyle),
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
)
|
|
||||||
: PreConfig().copy(
|
|
||||||
textStyle: textStyle,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
PConfig(
|
PConfig(
|
||||||
textStyle: textStyle ?? Theme.of(context).textTheme.bodyMedium!,
|
textStyle: textStyle ?? Theme.of(context).textTheme.bodyMedium!,
|
||||||
),
|
),
|
||||||
|
HrConfig(height: 1, color: Theme.of(context).dividerColor),
|
||||||
|
PreConfig(theme: isDark ? a11yDarkTheme : a11yLightTheme),
|
||||||
LinkConfig(
|
LinkConfig(
|
||||||
style:
|
style:
|
||||||
linkStyle ??
|
linkStyle ??
|
||||||
@ -146,8 +145,13 @@ class MarkdownTextContent extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
generator: MarkdownGenerator(
|
generator: MarkdownGenerator(
|
||||||
inlineSyntaxList: [_UserNameCardInlineSyntax(), _StickerInlineSyntax()],
|
generators: [latexGenerator],
|
||||||
linesMargin: EdgeInsets.zero,
|
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:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
import 'package:island/widgets/content/sheet.dart';
|
import 'package:island/widgets/content/sheet.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
@ -23,6 +24,9 @@ class ComposeSettingsSheet extends HookWidget {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final colorScheme = theme.colorScheme;
|
final colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
// Listen to visibility changes to trigger rebuilds
|
||||||
|
final currentVisibility = useValueListenable(visibility);
|
||||||
|
|
||||||
IconData getVisibilityIcon(int visibilityValue) {
|
IconData getVisibilityIcon(int visibilityValue) {
|
||||||
switch (visibilityValue) {
|
switch (visibilityValue) {
|
||||||
case 1:
|
case 1:
|
||||||
@ -71,38 +75,39 @@ class ComposeSettingsSheet extends HookWidget {
|
|||||||
void showVisibilitySheet() {
|
void showVisibilitySheet() {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => SheetScaffold(
|
builder:
|
||||||
titleText: 'postVisibility'.tr(),
|
(context) => SheetScaffold(
|
||||||
child: Column(
|
titleText: 'postVisibility'.tr(),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
buildVisibilityOption(
|
children: [
|
||||||
context,
|
buildVisibilityOption(
|
||||||
0,
|
context,
|
||||||
Symbols.public,
|
0,
|
||||||
'postVisibilityPublic',
|
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),
|
contentPadding: const EdgeInsets.all(16),
|
||||||
),
|
),
|
||||||
style: theme.textTheme.titleLarge,
|
style: theme.textTheme.titleMedium,
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const Gap(16),
|
||||||
|
|
||||||
// Description field
|
// Description field
|
||||||
TextField(
|
TextField(
|
||||||
@ -140,25 +146,23 @@ class ComposeSettingsSheet extends HookWidget {
|
|||||||
),
|
),
|
||||||
contentPadding: const EdgeInsets.all(16),
|
contentPadding: const EdgeInsets.all(16),
|
||||||
),
|
),
|
||||||
style: theme.textTheme.bodyLarge,
|
style: theme.textTheme.bodyMedium,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const Gap(16),
|
||||||
|
|
||||||
// Visibility setting
|
// Visibility setting
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(color: colorScheme.outline, width: 1),
|
||||||
color: colorScheme.outline,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Icon(getVisibilityIcon(visibility.value)),
|
leading: Icon(getVisibilityIcon(currentVisibility)),
|
||||||
title: Text('postVisibility'.tr()),
|
title: Text('postVisibility'.tr()),
|
||||||
subtitle: Text(getVisibilityText(visibility.value).tr()),
|
subtitle: Text(getVisibilityText(currentVisibility).tr()),
|
||||||
trailing: const Icon(Symbols.chevron_right),
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
onTap: showVisibilitySheet,
|
onTap: showVisibilitySheet,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
|
@ -209,7 +209,11 @@ class PostItem extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
).padding(bottom: 8),
|
).padding(bottom: 8),
|
||||||
if (item.content?.isNotEmpty ?? false)
|
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
|
// Show truncation hint if post is truncated
|
||||||
if (item.isTruncated && !isFullPost)
|
if (item.isTruncated && !isFullPost)
|
||||||
_PostTruncateHint(),
|
_PostTruncateHint(),
|
||||||
|
22
pubspec.lock
22
pubspec.lock
@ -149,10 +149,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build
|
name: build
|
||||||
sha256: "74273591bd8b7f82eeb1f191c1b65a6576535bbfd5ca3722778b07d5702d33cc"
|
sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.3"
|
version: "2.5.4"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -173,26 +173,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: badce70566085f2e87434531c4a6bc8e833672f755fc51146d612245947e91c9
|
sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.3"
|
version: "2.5.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: b9070a4127033777c0e63195f6f117ed16a351ed676f6313b095cf4f328c0b82
|
sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.3"
|
version: "2.5.4"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
sha256: "1cdfece3eeb3f1263f7dbf5bcc0cba697bd0c22d2c866cb4b578c954dbb09bcf"
|
sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.1.1"
|
version: "9.1.2"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -836,7 +836,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.4"
|
version: "0.3.4"
|
||||||
flutter_math_fork:
|
flutter_math_fork:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_math_fork
|
name: flutter_math_fork
|
||||||
sha256: "6d5f2f1aa57ae539ffb0a04bb39d2da67af74601d685a161aff7ce5bda5fa407"
|
sha256: "6d5f2f1aa57ae539ffb0a04bb39d2da67af74601d685a161aff7ce5bda5fa407"
|
||||||
@ -1745,10 +1745,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: record_platform_interface
|
name: record_platform_interface
|
||||||
sha256: "8a575828733d4c3cb5983c914696f40db8667eab3538d4c41c50cbb79e722ef4"
|
sha256: c1ad38f51e4af88a085b3e792a22c685cb3e7c23fc37aa7ce44c4cf18f25fe89
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.3.0"
|
||||||
record_web:
|
record_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -118,6 +118,7 @@ dependencies:
|
|||||||
native_exif: ^0.6.2
|
native_exif: ^0.6.2
|
||||||
local_auth: ^2.3.0
|
local_auth: ^2.3.0
|
||||||
flutter_secure_storage: ^4.2.1
|
flutter_secure_storage: ^4.2.1
|
||||||
|
flutter_math_fork: ^0.7.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user