🐛 Fixes attachments
This commit is contained in:
@@ -1061,14 +1061,14 @@ class _ChatInput extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
if (attachments.isNotEmpty)
|
if (attachments.isNotEmpty)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 324,
|
height: 280,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
padding: EdgeInsets.symmetric(horizontal: 12),
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: attachments.length,
|
itemCount: attachments.length,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 320,
|
height: 280,
|
||||||
width: 280,
|
width: 280,
|
||||||
child: AttachmentPreview(
|
child: AttachmentPreview(
|
||||||
item: attachments[idx],
|
item: attachments[idx],
|
||||||
|
@@ -367,63 +367,63 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
// Post content form
|
// Post content form
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: KeyboardListener(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
focusNode: FocusNode(),
|
||||||
child: Column(
|
onKeyEvent:
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
(event) => ComposeLogic.handleKeyPress(
|
||||||
children: [
|
event,
|
||||||
TextField(
|
state,
|
||||||
controller: state.titleController,
|
ref,
|
||||||
decoration: InputDecoration(
|
context,
|
||||||
hintText: 'postTitle'.tr(),
|
originalPost: originalPost,
|
||||||
border: InputBorder.none,
|
repliedPost: repliedPost,
|
||||||
isCollapsed: true,
|
forwardedPost: forwardedPost,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 8,
|
|
||||||
horizontal: 8,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
style: theme.textTheme.titleMedium,
|
|
||||||
onTapOutside:
|
|
||||||
(_) =>
|
|
||||||
FocusManager.instance.primaryFocus
|
|
||||||
?.unfocus(),
|
|
||||||
),
|
),
|
||||||
TextField(
|
child: SingleChildScrollView(
|
||||||
controller: state.descriptionController,
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
decoration: InputDecoration(
|
child: Column(
|
||||||
hintText: 'postDescription'.tr(),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
border: InputBorder.none,
|
children: [
|
||||||
isCollapsed: true,
|
TextField(
|
||||||
contentPadding: const EdgeInsets.fromLTRB(
|
controller: state.titleController,
|
||||||
8,
|
decoration: InputDecoration(
|
||||||
4,
|
hintText: 'postTitle'.tr(),
|
||||||
8,
|
border: InputBorder.none,
|
||||||
12,
|
isCollapsed: true,
|
||||||
),
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
),
|
vertical: 8,
|
||||||
style: theme.textTheme.bodyMedium,
|
horizontal: 8,
|
||||||
minLines: 1,
|
|
||||||
maxLines: 3,
|
|
||||||
onTapOutside:
|
|
||||||
(_) =>
|
|
||||||
FocusManager.instance.primaryFocus
|
|
||||||
?.unfocus(),
|
|
||||||
),
|
|
||||||
// Content field with borderless design
|
|
||||||
KeyboardListener(
|
|
||||||
focusNode: FocusNode(),
|
|
||||||
onKeyEvent:
|
|
||||||
(event) => ComposeLogic.handleKeyPress(
|
|
||||||
event,
|
|
||||||
state,
|
|
||||||
ref,
|
|
||||||
context,
|
|
||||||
originalPost: originalPost,
|
|
||||||
repliedPost: repliedPost,
|
|
||||||
forwardedPost: forwardedPost,
|
|
||||||
),
|
),
|
||||||
child: TextField(
|
),
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
onTapOutside:
|
||||||
|
(_) =>
|
||||||
|
FocusManager.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: state.descriptionController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'postDescription'.tr(),
|
||||||
|
border: InputBorder.none,
|
||||||
|
isCollapsed: true,
|
||||||
|
contentPadding: const EdgeInsets.fromLTRB(
|
||||||
|
8,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
minLines: 1,
|
||||||
|
maxLines: 3,
|
||||||
|
onTapOutside:
|
||||||
|
(_) =>
|
||||||
|
FocusManager.instance.primaryFocus
|
||||||
|
?.unfocus(),
|
||||||
|
),
|
||||||
|
// Content field with borderless design
|
||||||
|
TextField(
|
||||||
controller: state.contentController,
|
controller: state.contentController,
|
||||||
style: theme.textTheme.bodyMedium,
|
style: theme.textTheme.bodyMedium,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@@ -441,23 +441,23 @@ class PostComposeScreen extends HookConsumerWidget {
|
|||||||
FocusManager.instance.primaryFocus
|
FocusManager.instance.primaryFocus
|
||||||
?.unfocus(),
|
?.unfocus(),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
|
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
|
|
||||||
// Attachments preview
|
// Attachments preview
|
||||||
if (state.attachments.value.isNotEmpty)
|
if (state.attachments.value.isNotEmpty)
|
||||||
LayoutBuilder(
|
LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
final isWide = isWideScreen(context);
|
final isWide = isWideScreen(context);
|
||||||
return isWide
|
return isWide
|
||||||
? buildWideAttachmentGrid()
|
? buildWideAttachmentGrid()
|
||||||
: buildNarrowAttachmentList();
|
: buildNarrowAttachmentList();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
const SizedBox.shrink(),
|
const SizedBox.shrink(),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -272,8 +272,96 @@ class AttachmentPreview extends HookConsumerWidget {
|
|||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
child: Column(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: ratio,
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Builder(
|
||||||
|
key: ValueKey(item.hashCode),
|
||||||
|
builder: (context) {
|
||||||
|
if (item.isOnCloud) {
|
||||||
|
return CloudFileWidget(item: item.data);
|
||||||
|
} else if (item.data is XFile) {
|
||||||
|
final file = item.data as XFile;
|
||||||
|
if (file.path.isEmpty) {
|
||||||
|
return FutureBuilder<Uint8List>(
|
||||||
|
future: file.readAsBytes(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
return Image.memory(snapshot.data!);
|
||||||
|
}
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.type) {
|
||||||
|
case UniversalFileType.image:
|
||||||
|
return kIsWeb
|
||||||
|
? Image.network(file.path)
|
||||||
|
: Image.file(File(file.path));
|
||||||
|
default:
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.document_scanner),
|
||||||
|
Text(file.name),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (item is List<int> || item is Uint8List) {
|
||||||
|
switch (item.type) {
|
||||||
|
case UniversalFileType.image:
|
||||||
|
return Image.memory(item.data);
|
||||||
|
default:
|
||||||
|
return Column(
|
||||||
|
children: [const Icon(Symbols.document_scanner)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Placeholder();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (progress != null)
|
||||||
|
Positioned.fill(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black.withOpacity(0.3),
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 40,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (progress != null)
|
||||||
|
Text(
|
||||||
|
'${progress!.toStringAsFixed(2)}%',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Text(
|
||||||
|
'uploading'.tr(),
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
Gap(6),
|
||||||
|
Center(
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value:
|
||||||
|
progress != null ? progress! / 100.0 : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@@ -397,94 +485,6 @@ class AttachmentPreview extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 12, vertical: 8),
|
).padding(horizontal: 12, vertical: 8),
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: ratio,
|
|
||||||
child: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
children: [
|
|
||||||
Builder(
|
|
||||||
key: ValueKey(item.hashCode),
|
|
||||||
builder: (context) {
|
|
||||||
if (item.isOnCloud) {
|
|
||||||
return CloudFileWidget(item: item.data);
|
|
||||||
} else if (item.data is XFile) {
|
|
||||||
final file = item.data as XFile;
|
|
||||||
if (file.path.isEmpty) {
|
|
||||||
return FutureBuilder<Uint8List>(
|
|
||||||
future: file.readAsBytes(),
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.hasData) {
|
|
||||||
return Image.memory(snapshot.data!);
|
|
||||||
}
|
|
||||||
return const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (item.type) {
|
|
||||||
case UniversalFileType.image:
|
|
||||||
return kIsWeb
|
|
||||||
? Image.network(file.path)
|
|
||||||
: Image.file(File(file.path));
|
|
||||||
default:
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
const Icon(Symbols.document_scanner),
|
|
||||||
Text(file.name),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (item is List<int> || item is Uint8List) {
|
|
||||||
switch (item.type) {
|
|
||||||
case UniversalFileType.image:
|
|
||||||
return Image.memory(item.data);
|
|
||||||
default:
|
|
||||||
return Column(
|
|
||||||
children: [const Icon(Symbols.document_scanner)],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Placeholder();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (progress != null)
|
|
||||||
Positioned.fill(
|
|
||||||
child: Container(
|
|
||||||
color: Colors.black.withOpacity(0.3),
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 40,
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (progress != null)
|
|
||||||
Text(
|
|
||||||
'${progress!.toStringAsFixed(2)}%',
|
|
||||||
style: TextStyle(color: Colors.white),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Text(
|
|
||||||
'uploading'.tr(),
|
|
||||||
style: TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
Gap(6),
|
|
||||||
Center(
|
|
||||||
child: LinearProgressIndicator(
|
|
||||||
value:
|
|
||||||
progress != null ? progress! / 100.0 : null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@@ -697,7 +697,7 @@ class ComposeLogic {
|
|||||||
SnPost? repliedPost,
|
SnPost? repliedPost,
|
||||||
SnPost? forwardedPost,
|
SnPost? forwardedPost,
|
||||||
}) {
|
}) {
|
||||||
if (event is! RawKeyDownEvent) return;
|
if (event is! KeyDownEvent) return;
|
||||||
|
|
||||||
final isPaste = event.logicalKey == LogicalKeyboardKey.keyV;
|
final isPaste = event.logicalKey == LogicalKeyboardKey.keyV;
|
||||||
final isSave = event.logicalKey == LogicalKeyboardKey.keyS;
|
final isSave = event.logicalKey == LogicalKeyboardKey.keyS;
|
||||||
|
Reference in New Issue
Block a user