diff --git a/CHANGELOG.md b/CHANGELOG.md
index d93716a..482baa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
# CHANGELOG
## 1.5.2+18
+* Fixed input colors in login view when using dark theme
+* Added removal of individual files selected for upload
+* Added size for individual files selected for upload
+* Replaced intent sharing library with `flutter_sharing_intent`
* Added proper linting to project
## 1.5.1+17
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 579db4d..2c6fc11 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -21,46 +21,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 1faf8d4..8e2d676 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -60,7 +60,7 @@
- // TODO follow steps 2) on create share extension (https://pub.dev/packages/receive_sharing_intent)
+ // TODO follow steps on create share extension (https://pub.dev/packages/flutter_sharing_intent)
NSPhotoLibraryUsageDescription
Allow to select photos and upload them via the app
LSApplicationQueriesSchemes
diff --git a/lib/core/viewmodels/base_model.dart b/lib/core/viewmodels/base_model.dart
index 9fc7570..953fd51 100644
--- a/lib/core/viewmodels/base_model.dart
+++ b/lib/core/viewmodels/base_model.dart
@@ -80,6 +80,7 @@ class BaseModel extends ChangeNotifier {
@override
void dispose() {
+ _logger.d("Calling dispose");
super.dispose();
_isDisposed = true;
}
diff --git a/lib/core/viewmodels/upload_model.dart b/lib/core/viewmodels/upload_model.dart
index 2be59b6..71e43ed 100644
--- a/lib/core/viewmodels/upload_model.dart
+++ b/lib/core/viewmodels/upload_model.dart
@@ -4,10 +4,11 @@ import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import 'package:flutter_sharing_intent/flutter_sharing_intent.dart';
+import 'package:flutter_sharing_intent/model/sharing_file.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:logger/logger.dart';
import 'package:path/path.dart';
-import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import '../../locator.dart';
import '../enums/error_code.dart';
@@ -45,6 +46,41 @@ class UploadModel extends BaseModel {
TextEditingController get pasteTextController => _pasteTextController;
+ void _parseIntentFiles(List files) {
+ if (files.isNotEmpty) {
+ setStateView(ViewState.busy);
+
+ paths = files.map((sharedFile) {
+ _logger.d("Shared file name: ${basename(sharedFile.value ?? '')}");
+ _logger.d("Shared file path: ${sharedFile.value}");
+ _logger.d(
+ "Shared file size: ${File(sharedFile.value ?? '').lengthSync()}");
+ _logger.d("Shared file type: ${sharedFile.type}");
+ return PlatformFile.fromMap({
+ 'path': sharedFile.value,
+ 'name': basename(sharedFile.value!),
+ 'size': File(sharedFile.value!).lengthSync(),
+ 'bytes': null
+ });
+ }).toList();
+
+ setStateView(ViewState.idle);
+ }
+ }
+
+ void deleteIntentFile(String path) {
+ setStateView(ViewState.busy);
+ _logger.d("Removing path '$path' from $paths");
+
+ paths?.removeWhere((element) => element.path == path);
+
+ int length = paths!.length;
+ if (length == 0) {
+ paths = null;
+ }
+ setStateView(ViewState.idle);
+ }
+
void init() {
_pasteTextController.addListener(() {
pasteTextTouched = pasteTextController.text.isNotEmpty;
@@ -52,61 +88,21 @@ class UploadModel extends BaseModel {
});
// For sharing images coming from outside the app while the app is in the memory
- _intentDataStreamSubscription = ReceiveSharingIntent.getMediaStream().listen((List value) {
- if (value.isNotEmpty) {
- setStateView(ViewState.busy);
- paths = value.map((sharedFile) {
- return PlatformFile.fromMap({
- 'path': sharedFile.path,
- 'name': basename(sharedFile.path),
- 'size': File(sharedFile.path).lengthSync(),
- 'bytes': null
- });
- }).toList();
- setStateView(ViewState.idle);
- }
+ _intentDataStreamSubscription = FlutterSharingIntent.instance
+ .getMediaStream()
+ .listen((List value) {
+ _logger.d("Retrieved ${value.length} files from intent");
+ _parseIntentFiles(value);
}, onError: (err) {
_errorIntentHandle(err);
});
// For sharing images coming from outside the app while the app is closed
- ReceiveSharingIntent.getInitialMedia().then((List value) {
- if (value.isNotEmpty) {
- setStateView(ViewState.busy);
- paths = value.map((sharedFile) {
- return PlatformFile.fromMap({
- 'path': sharedFile.path,
- 'name': basename(sharedFile.path),
- 'size': File(sharedFile.path).lengthSync(),
- 'bytes': null
- });
- }).toList();
- setStateView(ViewState.idle);
- }
- }, onError: (err) {
- _errorIntentHandle(err);
- });
-
- // For sharing or opening urls/text coming from outside the app while the app is in the memory
- _intentDataStreamSubscription = ReceiveSharingIntent.getTextStream().listen((String value) {
- if (value.isNotEmpty) {
- setStateView(ViewState.busy);
- pasteTextController.text = value;
- setStateView(ViewState.idle);
- }
- }, onError: (err) {
- _errorIntentHandle(err);
- });
-
- // For sharing or opening urls/text coming from outside the app while the app is closed
- ReceiveSharingIntent.getInitialText().then((String? value) {
- if (value != null && value.isNotEmpty) {
- setStateView(ViewState.busy);
- pasteTextController.text = value;
- setStateView(ViewState.idle);
- }
- }, onError: (err) {
- _errorIntentHandle(err);
+ FlutterSharingIntent.instance
+ .getInitialSharing()
+ .then((List value) {
+ _logger.d("Retrieved ${value.length} files from inactive intent");
+ _parseIntentFiles(value);
});
}
@@ -150,7 +146,9 @@ class UploadModel extends BaseModel {
allowMultiple: true,
withData: false,
withReadStream: true,
- allowedExtensions: (_extension?.isNotEmpty ?? false) ? _extension?.replaceAll(' ', '').split(',') : null,
+ allowedExtensions: (_extension?.isNotEmpty ?? false)
+ ? _extension?.replaceAll(' ', '').split(',')
+ : null,
))
?.files;
} on PlatformException catch (e) {
@@ -185,21 +183,25 @@ class UploadModel extends BaseModel {
Map? additionalFiles;
if (pasteTextController.text.isNotEmpty) {
- additionalFiles =
- Map.from({'paste-${(DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt': pasteTextController.text});
+ additionalFiles = Map.from({
+ 'paste-${(DateTime.now().millisecondsSinceEpoch / 1000).round()}.txt':
+ pasteTextController.text
+ });
}
if (paths != null && paths!.isNotEmpty) {
files = paths!.map((e) => File(e.path!)).toList();
}
- UploadedResponse response = await _fileService.uploadPaste(files, additionalFiles);
+ UploadedResponse response =
+ await _fileService.uploadPaste(files, additionalFiles);
for (var element in response.data.ids) {
uploadedPasteIds.putIfAbsent(element, () => false);
}
if (createMulti && response.data.ids.length > 1) {
- UploadedMultiResponse multiResponse = await _fileService.uploadMultiPaste(response.data.ids);
+ UploadedMultiResponse multiResponse =
+ await _fileService.uploadMultiPaste(response.data.ids);
uploadedPasteIds.putIfAbsent(multiResponse.data.urlId, () => true);
}
@@ -219,9 +221,11 @@ class UploadModel extends BaseModel {
e.responseBody is RestError &&
e.responseBody.message != null) {
if (e.statusCode == HttpStatus.badRequest) {
- errorMessage = translate('api.bad_request', args: {'reason': e.responseBody.message});
+ errorMessage = translate('api.bad_request',
+ args: {'reason': e.responseBody.message});
} else {
- errorMessage = translate('api.general_rest_error_payload', args: {'message': e.responseBody.message});
+ errorMessage = translate('api.general_rest_error_payload',
+ args: {'message': e.responseBody.message});
}
} else {
errorMessage = translate('api.general_rest_error');
@@ -252,6 +256,8 @@ class UploadModel extends BaseModel {
void dispose() {
_pasteTextController.dispose();
_intentDataStreamSubscription.cancel();
+ FlutterSharingIntent.instance.reset();
+ paths = null;
super.dispose();
}
}
diff --git a/lib/ui/views/upload_view.dart b/lib/ui/views/upload_view.dart
index 21048b6..4815fa2 100644
--- a/lib/ui/views/upload_view.dart
+++ b/lib/ui/views/upload_view.dart
@@ -1,4 +1,5 @@
import 'package:clipboard/clipboard.dart';
+import 'package:fbmobile/core/util/formatter_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:provider/provider.dart';
@@ -20,12 +21,14 @@ class UploadView extends StatelessWidget {
Widget build(BuildContext context) {
return BaseView(
onModelReady: (model) => model.init(),
- builder: (context, model, child) =>
- Scaffold(appBar: MyAppBar(title: Text(translate('titles.upload'))), body: _render(model, context)));
+ builder: (context, model, child) => Scaffold(
+ appBar: MyAppBar(title: Text(translate('titles.upload'))),
+ body: _render(model, context)));
}
bool _isUploadButtonEnabled(UploadModel model) {
- return model.pasteTextTouched || (model.paths != null && model.paths!.isNotEmpty);
+ return model.pasteTextTouched ||
+ (model.paths != null && model.paths!.isNotEmpty);
}
Widget _render(UploadModel model, BuildContext context) {
@@ -38,7 +41,9 @@ class UploadView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircularProgressIndicator(),
- (model.stateMessage != null && model.stateMessage!.isNotEmpty ? Text(model.stateMessage!) : Container())
+ (model.stateMessage != null && model.stateMessage!.isNotEmpty
+ ? Text(model.stateMessage!)
+ : Container())
]))
: ListView(children: [
Padding(
@@ -56,12 +61,15 @@ class UploadView extends StatelessWidget {
Icons.text_snippet,
),
suffixIcon: IconButton(
- onPressed: () => model.pasteTextController.clear(),
+ onPressed: () =>
+ model.pasteTextController.clear(),
icon: const Icon(Icons.clear),
),
hintText: translate('upload.text_to_be_pasted'),
- contentPadding: const EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
- border: OutlineInputBorder(borderRadius: BorderRadius.circular(32.0)),
+ contentPadding: const EdgeInsets.fromLTRB(
+ 20.0, 10.0, 20.0, 10.0),
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(32.0)),
),
controller: model.pasteTextController)),
Padding(
@@ -77,14 +85,17 @@ class UploadView extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
ElevatedButton.icon(
- icon: const Icon(Icons.file_copy_sharp, color: blueColor),
+ icon: const Icon(Icons.file_copy_sharp,
+ color: blueColor),
onPressed: () => model.openFileExplorer(),
label: Text(
translate('upload.open_file_explorer'),
)),
ElevatedButton.icon(
- icon: const Icon(Icons.cancel, color: orangeColor),
- onPressed: model.paths != null && model.paths!.isNotEmpty
+ icon: const Icon(Icons.cancel,
+ color: orangeColor),
+ onPressed: model.paths != null &&
+ model.paths!.isNotEmpty
? () => model.clearCachedFiles()
: null,
label: Text(
@@ -114,34 +125,47 @@ class UploadView extends StatelessWidget {
onPressed: !_isUploadButtonEnabled(model)
? null
: () async {
- Map? items = await model.upload();
- String? clipboardContent = model.generatePasteLinks(items, url);
+ Map? items =
+ await model.upload();
+ String? clipboardContent = model
+ .generatePasteLinks(items, url);
- if (clipboardContent != null && clipboardContent.isNotEmpty) {
- FlutterClipboard.copy(clipboardContent).then((value) {
+ if (clipboardContent != null &&
+ clipboardContent.isNotEmpty) {
+ FlutterClipboard.copy(
+ clipboardContent)
+ .then((value) {
final snackBar = SnackBar(
action: SnackBarAction(
- label: translate('upload.dismiss'),
+ label: translate(
+ 'upload.dismiss'),
textColor: blueColor,
onPressed: () {
- ScaffoldMessenger.of(context).hideCurrentSnackBar();
+ ScaffoldMessenger.of(
+ context)
+ .hideCurrentSnackBar();
},
),
- content: Text(translate('upload.uploaded')),
- duration: const Duration(seconds: 10),
+ content: Text(translate(
+ 'upload.uploaded')),
+ duration:
+ const Duration(seconds: 10),
);
- ScaffoldMessenger.of(context).showSnackBar(snackBar);
+ ScaffoldMessenger.of(context)
+ .showSnackBar(snackBar);
});
}
},
- icon: const Icon(Icons.upload_rounded, color: greenColor),
+ icon: const Icon(Icons.upload_rounded,
+ color: greenColor),
label: Text(
translate('upload.upload'),
)),
])),
model.errorMessage != null && model.errorMessage!.isNotEmpty
? (Padding(
- padding: const EdgeInsets.only(top: 10.0, bottom: 10.0),
+ padding:
+ const EdgeInsets.only(top: 10.0, bottom: 10.0),
child: CenteredErrorRow(model.errorMessage)))
: Container(),
Builder(
@@ -152,28 +176,54 @@ class UploadView extends StatelessWidget {
)
: model.paths != null
? Container(
- padding: const EdgeInsets.only(bottom: 30.0),
- height: MediaQuery.of(context).size.height * 0.50,
+ padding: const EdgeInsets.only(bottom: 20.0),
+ height:
+ MediaQuery.of(context).size.height * 0.50,
child: ListView.separated(
- itemCount: model.paths != null && model.paths!.isNotEmpty ? model.paths!.length : 1,
- itemBuilder: (BuildContext context, int index) {
- final bool isMultiPath = model.paths != null && model.paths!.isNotEmpty;
+ itemCount: model.paths != null &&
+ model.paths!.isNotEmpty
+ ? model.paths!.length
+ : 1,
+ itemBuilder:
+ (BuildContext context, int index) {
+ final bool isMultiPath =
+ model.paths != null &&
+ model.paths!.isNotEmpty;
final String name = (isMultiPath
- ? model.paths!.map((e) => e.name).toList()[index]
+ ? model.paths!
+ .map((e) => e.name)
+ .toList()[index]
: model.fileName ?? '...');
+ final size = model.paths!.isNotEmpty
+ ? model.paths!
+ .map((e) => e.size)
+ .toList()[index]
+ .toString()
+ : '';
final path = model.paths!.isNotEmpty
- ? model.paths!.map((e) => e.path).toList()[index].toString()
+ ? model.paths!
+ .map((e) => e.path)
+ .toList()[index]
+ .toString()
: '';
return Card(
child: ListTile(
+ trailing: IconButton(
+ icon: const Icon(Icons.clear,
+ color: orangeColor),
+ onPressed: () {
+ model.deleteIntentFile(path);
+ }),
title: Text(
- name,
+ "$name (${FormatterUtil.formatBytes(int.parse(size), 2)})",
),
subtitle: Text(path),
));
},
- separatorBuilder: (BuildContext context, int index) => const Divider(),
+ separatorBuilder:
+ (BuildContext context, int index) =>
+ const Divider(),
),
)
: Container(),
diff --git a/lib/ui/widgets/login_text_field.dart b/lib/ui/widgets/login_text_field.dart
index 56c0e85..6d04cf7 100644
--- a/lib/ui/widgets/login_text_field.dart
+++ b/lib/ui/widgets/login_text_field.dart
@@ -1,7 +1,5 @@
import 'package:flutter/material.dart';
-import '../shared/app_colors.dart';
-
class LoginTextField extends StatelessWidget {
final TextEditingController controller;
final String placeHolder;
@@ -19,7 +17,6 @@ class LoginTextField extends StatelessWidget {
margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
height: 50.0,
alignment: Alignment.centerLeft,
- decoration: BoxDecoration(color: whiteColor, borderRadius: BorderRadius.circular(10.0)),
child: TextFormField(
keyboardType: keyboardType,
obscureText: obscureText,
diff --git a/pubspec.lock b/pubspec.lock
index 7771c72..a80f2af 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -256,6 +256,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
+ flutter_sharing_intent:
+ dependency: "direct main"
+ description:
+ name: flutter_sharing_intent
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.0.5"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -588,13 +595,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
- receive_sharing_intent:
- dependency: "direct main"
- description:
- name: receive_sharing_intent
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.4.5"
share_plus:
dependency: "direct main"
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index fcd276a..e996478 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -36,13 +36,13 @@ dependencies:
share_plus: 6.3.0
file_picker: 5.2.4
clipboard: 0.1.3
- receive_sharing_intent: 1.4.5
permission_handler: 10.2.0
package_info_plus: 3.0.2
json_annotation: 4.7.0
dynamic_color: 1.5.4
intl: 0.17.0
path: 1.8.2
+ flutter_sharing_intent: 1.0.5
dev_dependencies:
flutter_test: