add store with purpose of uploading images
This commit is contained in:
parent
663b45bc21
commit
116b0d7961
|
@ -145,7 +145,10 @@ class CreatePostPage extends HookWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Toolbar(bodyController),
|
Toolbar(
|
||||||
|
controller: bodyController,
|
||||||
|
instanceHost: context.read<CreatePostStore>().instanceHost,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,8 +2,14 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
|
||||||
import '../../formatter.dart';
|
import '../../formatter.dart';
|
||||||
|
import '../../hooks/logged_in_action.dart';
|
||||||
|
import '../../util/async_store_listener.dart';
|
||||||
import '../../util/extensions/spaced.dart';
|
import '../../util/extensions/spaced.dart';
|
||||||
|
import '../../util/files.dart';
|
||||||
|
import '../../util/mobx_provider.dart';
|
||||||
|
import '../../util/observer_consumers.dart';
|
||||||
import '../../util/text_lines_iterator.dart';
|
import '../../util/text_lines_iterator.dart';
|
||||||
|
import 'editor_toolbar_store.dart';
|
||||||
|
|
||||||
class Reformat {
|
class Reformat {
|
||||||
final String text;
|
final String text;
|
||||||
|
@ -111,20 +117,55 @@ extension on TextEditingController {
|
||||||
|
|
||||||
class Toolbar extends HookWidget {
|
class Toolbar extends HookWidget {
|
||||||
final TextEditingController controller;
|
final TextEditingController controller;
|
||||||
|
final String instanceHost;
|
||||||
|
final EditorToolbarStore store;
|
||||||
static const _height = 50.0;
|
static const _height = 50.0;
|
||||||
const Toolbar(this.controller);
|
|
||||||
|
Toolbar({
|
||||||
|
required this.controller,
|
||||||
|
required this.instanceHost,
|
||||||
|
}) : store = EditorToolbarStore(instanceHost);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return MobxProvider.value(
|
||||||
|
value: store,
|
||||||
|
child: AsyncStoreListener(
|
||||||
|
asyncStore: store.imageUploadState,
|
||||||
|
child: Container(
|
||||||
height: _height,
|
height: _height,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
color: Theme.of(context).cardColor,
|
color: Theme.of(context).cardColor,
|
||||||
child: Material(
|
child: Material(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: _ToolbarBody(
|
||||||
|
controller: controller,
|
||||||
|
instanceHost: instanceHost,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget safeArea = const SizedBox(height: _height);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ToolbarBody extends HookWidget {
|
||||||
|
const _ToolbarBody({
|
||||||
|
required this.controller,
|
||||||
|
required this.instanceHost,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TextEditingController controller;
|
||||||
|
final String instanceHost;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final loggedInAction = useLoggedInAction(instanceHost);
|
||||||
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => controller.surround('**'),
|
onPressed: () => controller.surround('**'),
|
||||||
|
@ -136,15 +177,45 @@ class Toolbar extends HookWidget {
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final r = await AddLinkDialog.show(
|
final r =
|
||||||
context, controller.selectionText);
|
await AddLinkDialog.show(context, controller.selectionText);
|
||||||
if (r != null) controller.reformat((_) => r);
|
if (r != null) controller.reformat((_) => r);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.link),
|
icon: const Icon(Icons.link),
|
||||||
),
|
),
|
||||||
IconButton(
|
// Insert image
|
||||||
onPressed: () {},
|
ObserverBuilder<EditorToolbarStore>(
|
||||||
icon: const Icon(Icons.image),
|
builder: (context, store) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: loggedInAction((token) async {
|
||||||
|
if (store.imageUploadState.isLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// FIXME: for some reason it doesn't go past this line on iOS. idk why
|
||||||
|
final pic = await pickImage();
|
||||||
|
// pic is null when the picker was cancelled
|
||||||
|
|
||||||
|
if (pic != null) {
|
||||||
|
final picUrl = await context
|
||||||
|
.read<EditorToolbarStore>()
|
||||||
|
.uploadImage(pic.path, token);
|
||||||
|
|
||||||
|
if (picUrl != null) {
|
||||||
|
controller.reformat(
|
||||||
|
(selection) => Reformat(text: '![]($picUrl)'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} on Exception catch (_) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Failed to upload image')));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
icon: store.imageUploadState.isLoading
|
||||||
|
? const CircularProgressIndicator.adaptive()
|
||||||
|
: const Icon(Icons.image),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {},
|
onPressed: () {},
|
||||||
|
@ -198,13 +269,8 @@ class Toolbar extends HookWidget {
|
||||||
icon: const Icon(Icons.question_mark),
|
icon: const Icon(Icons.question_mark),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Widget safeArea = const SizedBox(height: _height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddLinkDialog extends HookWidget {
|
class AddLinkDialog extends HookWidget {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import 'package:lemmy_api_client/pictrs.dart';
|
||||||
|
import 'package:lemmy_api_client/v3.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
|
import '../../util/async_store.dart';
|
||||||
|
import '../../util/pictrs.dart';
|
||||||
|
|
||||||
|
part 'editor_toolbar_store.g.dart';
|
||||||
|
|
||||||
|
class EditorToolbarStore = _EditorToolbarStore with _$EditorToolbarStore;
|
||||||
|
|
||||||
|
abstract class _EditorToolbarStore with Store {
|
||||||
|
final String instanceHost;
|
||||||
|
|
||||||
|
_EditorToolbarStore(this.instanceHost);
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String? url;
|
||||||
|
|
||||||
|
final imageUploadState = AsyncStore<PictrsUploadFile>();
|
||||||
|
|
||||||
|
@computed
|
||||||
|
bool get hasUploadedImage => imageUploadState.map(
|
||||||
|
loading: () => false,
|
||||||
|
error: (_) => false,
|
||||||
|
data: (_) => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
@action
|
||||||
|
Future<String?> uploadImage(String filePath, Jwt token) async {
|
||||||
|
final instanceHost = this.instanceHost;
|
||||||
|
|
||||||
|
final upload = await imageUploadState.run(
|
||||||
|
() => PictrsApi(instanceHost)
|
||||||
|
.upload(
|
||||||
|
filePath: filePath,
|
||||||
|
auth: token.raw,
|
||||||
|
)
|
||||||
|
.then((value) => value.files.single),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (upload != null) {
|
||||||
|
final url = pathToPictrs(instanceHost, upload.file);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void removeImage() {
|
||||||
|
final pictrsFile = imageUploadState.map<PictrsUploadFile?>(
|
||||||
|
data: (data) => data,
|
||||||
|
loading: () => null,
|
||||||
|
error: (_) => null,
|
||||||
|
);
|
||||||
|
if (pictrsFile == null) return;
|
||||||
|
|
||||||
|
PictrsApi(instanceHost).delete(pictrsFile).catchError((_) {});
|
||||||
|
|
||||||
|
imageUploadState.reset();
|
||||||
|
url = '';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'editor_toolbar_store.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// StoreGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
|
||||||
|
|
||||||
|
mixin _$EditorToolbarStore on _EditorToolbarStore, Store {
|
||||||
|
Computed<bool>? _$hasUploadedImageComputed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get hasUploadedImage => (_$hasUploadedImageComputed ??= Computed<bool>(
|
||||||
|
() => super.hasUploadedImage,
|
||||||
|
name: '_EditorToolbarStore.hasUploadedImage'))
|
||||||
|
.value;
|
||||||
|
|
||||||
|
late final _$urlAtom =
|
||||||
|
Atom(name: '_EditorToolbarStore.url', context: context);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get url {
|
||||||
|
_$urlAtom.reportRead();
|
||||||
|
return super.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
set url(String? value) {
|
||||||
|
_$urlAtom.reportWrite(value, super.url, () {
|
||||||
|
super.url = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _$uploadImageAsyncAction =
|
||||||
|
AsyncAction('_EditorToolbarStore.uploadImage', context: context);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String?> uploadImage(String filePath, Jwt token) {
|
||||||
|
return _$uploadImageAsyncAction
|
||||||
|
.run(() => super.uploadImage(filePath, token));
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _$_EditorToolbarStoreActionController =
|
||||||
|
ActionController(name: '_EditorToolbarStore', context: context);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void removeImage() {
|
||||||
|
final _$actionInfo = _$_EditorToolbarStoreActionController.startAction(
|
||||||
|
name: '_EditorToolbarStore.removeImage');
|
||||||
|
try {
|
||||||
|
return super.removeImage();
|
||||||
|
} finally {
|
||||||
|
_$_EditorToolbarStoreActionController.endAction(_$actionInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return '''
|
||||||
|
url: ${url},
|
||||||
|
hasUploadedImage: ${hasUploadedImage}
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue