Skip to content

Commit

Permalink
Merge pull request #2048 from acterglobal/kumar/pin-improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
kumarpalsinh25 authored Aug 14, 2024
2 parents bb7119a + 00a9e3b commit 481db61
Show file tree
Hide file tree
Showing 30 changed files with 1,582 additions and 496 deletions.
3 changes: 3 additions & 0 deletions .changes/2048-pin-refactoring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- [New] : Brand new UI-UX for Pin creation
- [New] : Support for adding attachment while Pin Creation
- [New] : Now you can add one or more Link as attachment for Pin
12 changes: 4 additions & 8 deletions app/integration_test/tests/pins.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'package:acter/common/utils/constants.dart';
import 'package:acter/common/widgets/html_editor.dart';
import 'package:acter/common/widgets/spaces/select_space_form_field.dart';
import 'package:acter/features/home/data/keys.dart';
import 'package:acter/features/pins/pages/create_pin.dart';
import 'package:acter/features/pins/pages/pin_page.dart';
import 'package:acter/features/pins/pages/create_pin_page.dart';
import 'package:acter/features/pins/widgets/pin_item.dart';
import 'package:acter/features/search/model/keys.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
Expand Down Expand Up @@ -56,23 +56,19 @@ extension ActerNews on ConvenientTest {
await pinActionKey.should(findsOneWidget);
await pinActionKey.tap();

final titleField = find.byKey(CreatePinPage.titleFieldKey);
final titleField = find.byKey(CreatePin.titleFieldKey);
await titleField.should(findsOneWidget);
await titleField.enterTextWithoutReplace(title);

final urlField = find.byKey(CreatePinPage.urlFieldKey);
await urlField.should(findsOneWidget);
await urlField.enterTextWithoutReplace(url);

final descriptionField = find.byKey(CreatePinPage.descriptionFieldKey);
final descriptionField = find.byKey(CreatePin.descriptionFieldKey);
await descriptionField.should(findsOneWidget);
final textEditorState =
(tester.firstState(descriptionField) as HtmlEditorState).editorState;
await textEditorState.insertText(0, content, path: [0]);
textEditorState.service.keyboardService!.closeKeyboard();
await selectSpace(spaceId, SelectSpaceFormField.openKey);

final submit = find.byKey(CreatePinPage.submitBtn);
final submit = find.byKey(CreatePin.submitBtn);
await tester.ensureVisible(submit);
await submit.tap();
}
Expand Down
10 changes: 9 additions & 1 deletion app/lib/common/models/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ typedef MemberInfo = ({String userId, String roomId});
typedef ChatMessageInfo = ({String messageId, String roomId});
typedef RoomQuery = ({String roomId, String query});

enum AttachmentType { camera, image, audio, video, location, file }
enum AttachmentType {
camera,
image,
audio,
video,
location,
file,
link,
}

typedef AttachmentInfo = ({AttachmentType type, File file});

Expand Down
12 changes: 12 additions & 0 deletions app/lib/common/themes/components/input_decoration_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ var inputDecorationTheme = InputDecorationTheme(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(12),
),
errorBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.red,
),
borderRadius: BorderRadius.circular(12),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: const BorderSide(
color: Colors.red,
),
borderRadius: BorderRadius.circular(12),
),
hintStyle: const TextStyle(
color: Color(0xFF898989),
fontWeight: FontWeight.w300,
Expand Down
1 change: 1 addition & 0 deletions app/lib/common/utils/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ enum Routes {
// -- pins
pins('/pins'),
pin('/pins/:pinId'),
createPin('/pins/create'),

// -- events
calendarEvents('/events'),
Expand Down
32 changes: 32 additions & 0 deletions app/lib/common/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ final idMatrixRegexp = RegExp(
r'matrix:roomid/(?<id>[^?]+)(\?via=(?<server_name>[^&]+))?(&via=(?<server_name2>[^&]+))?(&via=(?<server_name3>[^&]+))?',
);

final urlValidatorRegexp = RegExp(
r'^[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_+.~#?&/=]*$',
);

/// Get provider right from the context no matter where we are
extension Context on BuildContext {
// Custom call a provider for reading method only
Expand Down Expand Up @@ -161,6 +165,34 @@ Future<bool> openLink(String target, BuildContext context) async {
}
}

String getHumanReadableFileSize(int bytes) {
if (bytes <= 0) return '0 B';
const suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
var i = (log(bytes) / log(1024)).floor();
return '${(bytes / pow(1024, i)).toStringAsFixed(1)} ${suffixes[i]}';
}

String documentTypeFromFileExtension(String fileExtension) {
switch (fileExtension) {
case 'png':
case 'jpg':
case 'jpeg':
return 'Image';
case 'mov':
case 'mp4':
return 'Video';
case 'mp3':
case 'wav':
return 'Audio';
case 'pdf':
return 'PDF';
case 'txt':
return 'Text File';
default:
return '';
}
}

Future<void> shareTextToWhatsApp(
BuildContext context, {
required String text,
Expand Down
8 changes: 4 additions & 4 deletions app/lib/common/widgets/edit_html_description_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ void showEditHtmlDescriptionBottomSheet({
useSafeArea: true,
context: context,
isDismissible: true,
constraints: const BoxConstraints(maxHeight: 450),
isScrollControlled: true,
builder: (context) {
return EditHtmlDescriptionSheet(
Expand Down Expand Up @@ -69,9 +68,10 @@ class _EditHtmlDescriptionSheetState

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(widget.bottomSheetTitle ?? L10n.of(context).editDescription),
const SizedBox(height: 20),
Expand Down Expand Up @@ -112,7 +112,7 @@ class _EditHtmlDescriptionSheetState
),
],
),
const SizedBox(height: 20),
const SizedBox(height: 100),
],
),
);
Expand Down
6 changes: 3 additions & 3 deletions app/lib/common/widgets/edit_title_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ void showEditTitleBottomSheet({
showDragHandle: true,
useSafeArea: true,
context: context,
constraints: const BoxConstraints(maxHeight: 300),
isScrollControlled: true,
builder: (context) {
return EditTitleSheet(
bottomSheetTitle: bottomSheetTitle,
Expand Down Expand Up @@ -52,9 +52,9 @@ class _EditTitleSheetState extends ConsumerState<EditTitleSheet> {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
constraints: const BoxConstraints(maxWidth: 500),
padding: MediaQuery.of(context).viewInsets,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget.bottomSheetTitle ?? L10n.of(context).editTitle,
Expand Down
45 changes: 25 additions & 20 deletions app/lib/common/widgets/spaces/select_space_form_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SelectSpaceFormField extends ConsumerWidget {
final String? emptyText;
final String canCheck;
final bool mandatory;
final bool useCompatView;

const SelectSpaceFormField({
super.key,
Expand All @@ -23,42 +24,43 @@ class SelectSpaceFormField extends ConsumerWidget {
this.emptyText,
this.mandatory = true,
required this.canCheck,
this.useCompatView = false,
});

void selectSpace(BuildContext context, WidgetRef ref) async {
final newSelectedSpaceId = await selectSpaceDrawer(
context: context,
currentSpaceId: ref.read(selectedSpaceIdProvider),
canCheck: canCheck,
title: Text(selectTitle ?? L10n.of(context).selectSpace),
);
ref.read(selectedSpaceIdProvider.notifier).state = newSelectedSpaceId;
}

@override
Widget build(BuildContext context, WidgetRef ref) {
final currentSelectedSpace = ref.watch(selectedSpaceIdProvider);
final spaceNotifier = ref.watch(selectedSpaceIdProvider.notifier);
final selectedSpace = currentSelectedSpace != null;

void selectSpace() async {
final newSelectedSpaceId = await selectSpaceDrawer(
context: context,
currentSpaceId: ref.read(selectedSpaceIdProvider),
canCheck: canCheck,
title: Text(selectTitle ?? L10n.of(context).selectSpace),
);
spaceNotifier.state = newSelectedSpaceId;
}

final emptyButton = OutlinedButton(
key: openKey,
onPressed: selectSpace,
child: Text(emptyText ?? L10n.of(context).pleaseSelectSpace),
);
key: openKey,
onPressed: () => selectSpace(context, ref),
child: Text(emptyText ?? L10n.of(context).pleaseSelectSpace),
);

return FormField(
builder: (state) => selectedSpace
? InkWell(
key: openKey,
onTap: selectSpace,
onTap: () => selectSpace(context, ref),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title ?? L10n.of(context).space,
style: Theme.of(context).textTheme.bodyMedium,
),
if (!useCompatView)
Text(
title ?? L10n.of(context).space,
style: Theme.of(context).textTheme.bodyMedium,
),
Consumer(builder: spaceBuilder),
],
),
Expand Down Expand Up @@ -96,6 +98,9 @@ class SelectSpaceFormField extends ConsumerWidget {
? SpaceChip(
space: space,
onTapOpenSpaceDetail: false,
useCompatView: useCompatView,
onTapSelectSpace: () =>
useCompatView ? selectSpace(context, ref) : null,
)
: Text(currentSelectedSpace!),
error: (e, s) => Text(L10n.of(context).errorLoading(e)),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:acter/common/models/types.dart';
import 'package:acter/features/home/providers/client_providers.dart';
import 'package:acter_flutter_sdk/acter_flutter_sdk_ffi.dart'
show AttachmentDraft, AttachmentsManager;
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
import 'package:mime/mime.dart';

final _log = Logger('a3::action::attachments');

// if generic attachment, send via manager
Future<void> handleAttachmentSelected({
required BuildContext context,
required WidgetRef ref,
required AttachmentsManager manager,
required List<File> attachments,
String? title,
String? link,
required AttachmentType attachmentType,
}) async {
/// converts user selected media to attachment draft and sends state list.
/// only supports image/video/audio/file.
final lang = L10n.of(context);
EasyLoading.show(status: lang.sendingAttachment);
final client = ref.read(alwaysClientProvider);
List<AttachmentDraft> drafts = [];
try {
for (var selected in attachments) {
final file = selected;
final mimeType = lookupMimeType(file.path);
if (mimeType == null) throw lang.failedToDetectMimeType;
if (attachmentType == AttachmentType.camera ||
attachmentType == AttachmentType.image) {
Uint8List bytes = await file.readAsBytes();
final decodedImage = await decodeImageFromList(bytes);
final imageDraft = client
.imageDraft(file.path, mimeType)
.size(bytes.length)
.width(decodedImage.width)
.height(decodedImage.height);
final attachmentDraft = await manager.contentDraft(imageDraft);
drafts.add(attachmentDraft);
} else if (attachmentType == AttachmentType.audio) {
Uint8List bytes = await file.readAsBytes();
final audioDraft =
client.audioDraft(file.path, mimeType).size(bytes.length);
final attachmentDraft = await manager.contentDraft(audioDraft);
drafts.add(attachmentDraft);
} else if (attachmentType == AttachmentType.video) {
Uint8List bytes = await file.readAsBytes();
final videoDraft =
client.videoDraft(file.path, mimeType).size(bytes.length);
final attachmentDraft = await manager.contentDraft(videoDraft);
drafts.add(attachmentDraft);
} else {
String fileName = file.path.split('/').last;
final fileDraft = client
.fileDraft(file.path, mimeType)
.filename(fileName)
.size(file.lengthSync());
final attachmentDraft = await manager.contentDraft(fileDraft);
drafts.add(attachmentDraft);
}
}
if (attachmentType == AttachmentType.link && link != null) {
final attachmentDraft = await manager.linkDraft(link, title);
drafts.add(attachmentDraft);
}
for (var draft in drafts) {
final res = await draft.send();
_log.info('attachment sent: $res');
}
EasyLoading.dismiss();
} catch (e) {
_log.severe('Error sending attachments', e);
if (!context.mounted) {
EasyLoading.dismiss();
return;
}
EasyLoading.showError(
lang.errorSendingAttachment(e),
duration: const Duration(seconds: 3),
);
}
}
Loading

0 comments on commit 481db61

Please sign in to comment.