Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Space Objects : Share options #2390

Merged
merged 29 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
13c87bd
Draft share for new share dialog
kumarpalsinh25 Nov 27, 2024
d35c093
Improvement in AttachedTo Widget Code
kumarpalsinh25 Nov 28, 2024
092dc1e
Improvement in AttachedTo Widget Code
kumarpalsinh25 Nov 28, 2024
9c99d2b
Improvement in ExternalShareOption Widget Code
kumarpalsinh25 Nov 28, 2024
18fa2a2
Improvement in ExternalShareOption Widget Code
kumarpalsinh25 Nov 28, 2024
f1133c5
Improvement in FileShareOptions Widget Code
kumarpalsinh25 Nov 28, 2024
dfdf44f
Improvement in Common Share Dialog Widget Code
kumarpalsinh25 Nov 28, 2024
93a2832
Share dialog draft setup with dummy data
kumarpalsinh25 Nov 28, 2024
4a85d35
Merge branch 'main' into kumar/deep-linking
kumarpalsinh25 Nov 29, 2024
5f0c0bf
Some correction based on the Ben suggestion to get started with basic…
kumarpalsinh25 Nov 29, 2024
b1abfef
Create common share link widget and used it on Features Detail pages
kumarpalsinh25 Nov 29, 2024
f720ce8
Added internal deep link url
kumarpalsinh25 Nov 29, 2024
96d5770
Implementation of share options
kumarpalsinh25 Nov 29, 2024
f39a0b1
Added link parse to extract more information
kumarpalsinh25 Nov 29, 2024
8999d86
Added link parse to extract more information
kumarpalsinh25 Nov 29, 2024
f992394
Make attachment option and external share option as independent ui wi…
kumarpalsinh25 Nov 29, 2024
5ab6ecf
Added section title option to Attachment Widget
kumarpalsinh25 Nov 29, 2024
416c4ce
Naming corrections
kumarpalsinh25 Nov 29, 2024
3faa415
ShareSpaceObjectWidget class with related params
kumarpalsinh25 Nov 29, 2024
f6b3c08
ShareSpaceObjectWidget class with related params
kumarpalsinh25 Nov 29, 2024
36e976c
ShareSpaceObjectWidget class with related params
kumarpalsinh25 Nov 29, 2024
0ff7856
Share space object directly to Create Boost
kumarpalsinh25 Nov 29, 2024
567028e
Wording corrections
kumarpalsinh25 Nov 29, 2024
e7b9e9e
Added internal link for QR Option
kumarpalsinh25 Nov 29, 2024
ce31ec8
Add changelog data
kumarpalsinh25 Dec 2, 2024
54a8b9f
Removed un-wanted generic widget
kumarpalsinh25 Dec 3, 2024
19f58df
Combined share iCal file option in share space object dialog
kumarpalsinh25 Dec 3, 2024
5c40fa6
Minor spacing correction
kumarpalsinh25 Dec 3, 2024
552f64a
PR Feedback Points
kumarpalsinh25 Dec 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changes/2390-space-object-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- [New] : Now you can directly share space objects (Pin, Event and TaskList) to new Boost from their detail page.
- [New] : You can now share any object via its own unique QR code.
Binary file added app/assets/icon/signal_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
170 changes: 170 additions & 0 deletions app/lib/common/widgets/share/action/share_space_object_action.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import 'dart:io';

import 'package:acter/common/utils/routes.dart';
import 'package:acter/common/widgets/share/widgets/attach_options.dart';
import 'package:acter/common/widgets/share/widgets/external_share_options.dart';
import 'package:acter/common/widgets/share/widgets/file_share_options.dart';
import 'package:acter/features/deep_linking/actions/show_qr_code.dart';
import 'package:acter/features/deep_linking/types.dart';
import 'package:acter/features/files/actions/download_file.dart';
import 'package:acter/features/news/model/news_references_model.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:open_filex/open_filex.dart';
import 'package:share_plus/share_plus.dart';

typedef SpaceObjectDetails = ({
String spaceId,
ObjectType objectType,
String objectId,
});

typedef FileDetails = ({
File file,
String? mimeType,
});

Future<void> openShareSpaceObjectDialog({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic action for spaceObject share option.

required BuildContext context,
SpaceObjectDetails? spaceObjectDetails,
FileDetails? fileDetails,
}) async {
await showModalBottomSheet(
showDragHandle: true,
useSafeArea: true,
context: context,
isScrollControlled: true,
isDismissible: true,
builder: (context) => ShareSpaceObjectActionUI(
spaceObjectDetails: spaceObjectDetails,
fileDetails: fileDetails,
),
);
}

class ShareSpaceObjectActionUI extends StatelessWidget {
final SpaceObjectDetails? spaceObjectDetails;
final FileDetails? fileDetails;

const ShareSpaceObjectActionUI({
super.key,
this.spaceObjectDetails,
this.fileDetails,
});

@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (spaceObjectDetails != null) ...[
attachmentOptionsUI(context, spaceObjectDetails!),
SizedBox(height: 16),
externalShareOptionsUI(context, spaceObjectDetails!),
SizedBox(height: 20),
],
if (fileDetails != null) fileShareOptionsUI(context, fileDetails!),
],
),
),
);
}

Widget attachmentOptionsUI(
BuildContext context,
SpaceObjectDetails spaceObjectDetails,
) {
String spaceId = spaceObjectDetails.spaceId;
ObjectType objectType = spaceObjectDetails.objectType;
String objectId = spaceObjectDetails.objectId;

final newsRefType = getNewsRefTypeFromObjType(objectType);
return AttachOptions(
onTapBoost: () {
Navigator.pop(context);
context.pushNamed(
Routes.actionAddUpdate.name,
queryParameters: {'spaceId': spaceId},
extra: newsRefType != null
? NewsReferencesModel(type: newsRefType, id: objectId)
: null,
);
},
);
}

Widget externalShareOptionsUI(
BuildContext context,
SpaceObjectDetails spaceObjectDetails,
) {
String spaceId = spaceObjectDetails.spaceId;
ObjectType objectType = spaceObjectDetails.objectType;
String objectId = spaceObjectDetails.objectId;

final internalLink =
'acter:o/${spaceId.substring(1)}/${objectType.name}/${objectId.substring(1)}';

return ExternalShareOptions(
onTapQr: () {
Navigator.pop(context);
showQrCode(
context,
internalLink,
);
},
);
}

Widget fileShareOptionsUI(
BuildContext context,
FileDetails fileDetails,
) {
File file = fileDetails.file;
String? mimeType = fileDetails.mimeType;
return FileShareOptions(
onTapOpen: () async {
final result = await OpenFilex.open(file.absolute.path);
if (result.type == ResultType.done) {
// done, close this dialog
if (context.mounted) {
Navigator.pop(context);
}
}
},
onTapSave: !Platform.isAndroid
? () async {
if (await downloadFile(context, file)) {
// done, close this dialog
if (context.mounted) {
Navigator.pop(context);
}
}
}
: null,
onTapShare: () async {
final result = await Share.shareXFiles(
[XFile(file.path, mimeType: mimeType)],
);
if (result.status == ShareResultStatus.success) {
// done, close this dialog
if (context.mounted) {
Navigator.pop(context);
}
}
},
);
}

NewsReferencesType? getNewsRefTypeFromObjType(ObjectType objectType) {
return switch (objectType) {
ObjectType.pin => NewsReferencesType.pin,
ObjectType.calendarEvent => NewsReferencesType.calendarEvent,
ObjectType.taskList => NewsReferencesType.taskList,
_ => null,
};
}
}
124 changes: 124 additions & 0 deletions app/lib/common/widgets/share/widgets/attach_options.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import 'package:acter/common/themes/colors/color_scheme.dart';
import 'package:atlas_icons/atlas_icons.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';

class AttachOptions extends StatelessWidget {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic AttachOptions UI Widget which is independent of data part.

final String? sectionTitle;
final GestureTapCallback? onTapBoost;
final GestureTapCallback? onTapPin;
final GestureTapCallback? onTapEvent;
final GestureTapCallback? onTapTaskList;
final GestureTapCallback? onTapTaskItem;

const AttachOptions({
super.key,
this.sectionTitle,
this.onTapBoost,
this.onTapPin,
this.onTapEvent,
this.onTapTaskList,
this.onTapTaskItem,
});

@override
Widget build(BuildContext context) {
final lang = L10n.of(context);

return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (sectionTitle != null) ...[
Row(
children: [
Divider(indent: 0),
Text(
sectionTitle!,
textAlign: TextAlign.start,
style: Theme.of(context).textTheme.labelLarge,
),
Expanded(child: Divider(indent: 20)),
],
),
SizedBox(height: 12),
],
Wrap(
children: [
if (onTapBoost != null)
attachToItemUI(
name: lang.newBoost,
iconData: Atlas.megaphone_thin,
color: boastFeatureColor,
onTap: onTapBoost,
),
if (onTapPin != null)
attachToItemUI(
name: lang.pin,
iconData: Atlas.pin,
color: pinFeatureColor,
onTap: onTapPin,
),
if (onTapEvent != null)
attachToItemUI(
name: lang.event,
iconData: Atlas.calendar,
color: eventFeatureColor,
onTap: onTapEvent,
),
if (onTapTaskList != null)
attachToItemUI(
name: lang.taskList,
iconData: Atlas.list,
color: taskFeatureColor,
onTap: onTapTaskList,
),
if (onTapTaskItem != null)
attachToItemUI(
name: lang.task,
iconData: Atlas.list,
color: taskFeatureColor,
onTap: onTapTaskItem,
),
],
),
],
);
}

Widget attachToItemUI({
required String name,
required IconData iconData,
required Color color,
GestureTapCallback? onTap,
}) {
return InkWell(
borderRadius: BorderRadius.circular(16),
onTap: onTap,
child: Container(
margin: const EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: color.withOpacity(0.3),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: color,
style: BorderStyle.solid,
width: 1.0,
),
),
child: Icon(iconData),
),
SizedBox(height: 6),
Text(name, textAlign: TextAlign.center),
],
),
),
);
}
}
Loading
Loading