mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-03-03 13:36:47 +00:00
improve image editor
This commit is contained in:
parent
cd5deca6b6
commit
2ef4566d69
44 changed files with 465 additions and 1040 deletions
|
|
@ -5,6 +5,8 @@
|
||||||
- Adds option to manual focus in the camera
|
- Adds option to manual focus in the camera
|
||||||
- Adds support to switch between front and back camera during video recording
|
- Adds support to switch between front and back camera during video recording
|
||||||
- Adds basic face filters
|
- Adds basic face filters
|
||||||
|
- Improves image editor like emojies or text under a drawing can be moved
|
||||||
|
- Fixes issue with emojis disappearing in the image editor
|
||||||
|
|
||||||
## 0.0.86
|
## 0.0.86
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
|
||||||
String emoji,
|
String emoji,
|
||||||
bool remove,
|
bool remove,
|
||||||
) async {
|
) async {
|
||||||
if (!isEmoji(emoji)) {
|
if (!isOneEmoji(emoji)) {
|
||||||
Log.error('Did not update reaction as it is not an emoji!');
|
Log.error('Did not update reaction as it is not an emoji!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
|
||||||
String emoji,
|
String emoji,
|
||||||
bool remove,
|
bool remove,
|
||||||
) async {
|
) async {
|
||||||
if (!isEmoji(emoji)) {
|
if (!isOneEmoji(emoji)) {
|
||||||
Log.error('Did not update reaction as it is not an emoji!');
|
Log.error('Did not update reaction as it is not an emoji!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
import 'package:twonly/src/views/contact/contact.view.dart';
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,9 @@ class MainCameraPreview extends StatelessWidget {
|
||||||
requiredHeight: 0,
|
requiredHeight: 0,
|
||||||
additionalPadding: 59,
|
additionalPadding: 59,
|
||||||
bottomNavigation: Container(),
|
bottomNavigation: Container(),
|
||||||
child: Screenshot(
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Screenshot(
|
||||||
controller: mainCameraController.screenshotController,
|
controller: mainCameraController.screenshotController,
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 9 / 16,
|
aspectRatio: 9 / 16,
|
||||||
|
|
@ -48,7 +50,27 @@ class MainCameraPreview extends StatelessWidget {
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: mainCameraController.facePaint!,
|
child: mainCameraController.facePaint!,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
if (mainCameraController.focusPointOffset != null)
|
if (mainCameraController.focusPointOffset != null)
|
||||||
|
AspectRatio(
|
||||||
|
aspectRatio: 9 / 16,
|
||||||
|
child: ClipRect(
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
child: SizedBox(
|
||||||
|
width: mainCameraController
|
||||||
|
.cameraController!.value.previewSize!.height,
|
||||||
|
height: mainCameraController
|
||||||
|
.cameraController!.value.previewSize!.width,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
Positioned(
|
Positioned(
|
||||||
top: mainCameraController.focusPointOffset!.dy - 40,
|
top: mainCameraController.focusPointOffset!.dy - 40,
|
||||||
left:
|
left:
|
||||||
|
|
@ -65,14 +87,14 @@ class MainCameraPreview extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ import 'package:twonly/src/views/camera/camera_preview_components/permissions_vi
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/send_to.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/send_to.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/video_recording_time.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/video_recording_time.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/zoom_selector.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/zoom_selector.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/components/loader.dart';
|
import 'package:twonly/src/views/components/loader.dart';
|
||||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/face_filters/beard_filter_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/beard_filter_painter.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/face_filters/dog_filter_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/dog_filter_painter.dart';
|
||||||
|
|
||||||
enum FaceFilterType {
|
enum FaceFilterType {
|
||||||
none,
|
none,
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@ import 'package:twonly/src/utils/qr.dart';
|
||||||
import 'package:twonly/src/utils/screenshot.dart';
|
import 'package:twonly/src/utils/screenshot.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/face_filters.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/face_filters.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/barcode_detector_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/barcode_detector_painter.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/face_filters/beard_filter_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/beard_filter_painter.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/face_filters/dog_filter_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/dog_filter_painter.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/face_filters/face_filter_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart';
|
||||||
|
|
||||||
class ScannedVerifiedContact {
|
class ScannedVerifiedContact {
|
||||||
ScannedVerifiedContact({
|
ScannedVerifiedContact({
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/coordinates_translator.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/coordinates_translator.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/face_filters/face_filter_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart';
|
||||||
|
|
||||||
class BeardFilterPainter extends FaceFilterPainter {
|
class BeardFilterPainter extends FaceFilterPainter {
|
||||||
BeardFilterPainter(
|
BeardFilterPainter(
|
||||||
|
|
@ -6,8 +6,8 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/coordinates_translator.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/coordinates_translator.dart';
|
||||||
import 'package:twonly/src/views/camera/painters/face_filters/face_filter_painter.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart';
|
||||||
|
|
||||||
class DogFilterPainter extends FaceFilterPainter {
|
class DogFilterPainter extends FaceFilterPainter {
|
||||||
DogFilterPainter(
|
DogFilterPainter(
|
||||||
|
|
@ -1,710 +0,0 @@
|
||||||
Map<String, String> emojiWeights = {};
|
|
||||||
|
|
||||||
List<String> emojis = [
|
|
||||||
'😀',
|
|
||||||
'😁',
|
|
||||||
'😂',
|
|
||||||
'🤣',
|
|
||||||
'😃',
|
|
||||||
'😄',
|
|
||||||
'😅',
|
|
||||||
'😆',
|
|
||||||
'😉',
|
|
||||||
'😊',
|
|
||||||
'😋',
|
|
||||||
'😎',
|
|
||||||
'😍',
|
|
||||||
'😘',
|
|
||||||
'🥰',
|
|
||||||
'😗',
|
|
||||||
'😙',
|
|
||||||
'😚',
|
|
||||||
'🙂️',
|
|
||||||
'🤗',
|
|
||||||
'🤩',
|
|
||||||
'🤔',
|
|
||||||
'🤔',
|
|
||||||
'🤨',
|
|
||||||
'😐',
|
|
||||||
'😑',
|
|
||||||
'😶',
|
|
||||||
'🙄',
|
|
||||||
'😏',
|
|
||||||
'😣',
|
|
||||||
'😥',
|
|
||||||
'😮',
|
|
||||||
'🤐',
|
|
||||||
'😯',
|
|
||||||
'😪',
|
|
||||||
'😫',
|
|
||||||
'😴',
|
|
||||||
'😌',
|
|
||||||
'😛',
|
|
||||||
'😜',
|
|
||||||
'😝',
|
|
||||||
'🤤',
|
|
||||||
'😒',
|
|
||||||
'😓',
|
|
||||||
'😔',
|
|
||||||
'😕',
|
|
||||||
'🙃',
|
|
||||||
'🤑',
|
|
||||||
'😲',
|
|
||||||
'🙁',
|
|
||||||
'😖',
|
|
||||||
'😞',
|
|
||||||
'😟',
|
|
||||||
'😤',
|
|
||||||
'😢',
|
|
||||||
'😭',
|
|
||||||
'😦',
|
|
||||||
'😧',
|
|
||||||
'😨',
|
|
||||||
'😩',
|
|
||||||
'🤯',
|
|
||||||
'😬',
|
|
||||||
'😰',
|
|
||||||
'😱',
|
|
||||||
'🥵',
|
|
||||||
'🥶',
|
|
||||||
'😳',
|
|
||||||
'🤪',
|
|
||||||
'😵',
|
|
||||||
'😡',
|
|
||||||
'😠',
|
|
||||||
'🤬',
|
|
||||||
'😷',
|
|
||||||
'🤒',
|
|
||||||
'🤕',
|
|
||||||
'🤢',
|
|
||||||
'🤮',
|
|
||||||
'🤧',
|
|
||||||
'😇',
|
|
||||||
'🤠',
|
|
||||||
'🤡',
|
|
||||||
'🥳',
|
|
||||||
'🥴',
|
|
||||||
'🤥',
|
|
||||||
'🤫',
|
|
||||||
'🤭',
|
|
||||||
'🤭',
|
|
||||||
'🧐',
|
|
||||||
'🤓',
|
|
||||||
'😈',
|
|
||||||
'👿',
|
|
||||||
'👹',
|
|
||||||
'👺',
|
|
||||||
'💀',
|
|
||||||
'👻',
|
|
||||||
'👽',
|
|
||||||
'🤖',
|
|
||||||
'💩',
|
|
||||||
'😺',
|
|
||||||
'😸',
|
|
||||||
'😹',
|
|
||||||
'😻',
|
|
||||||
'😼',
|
|
||||||
'😽',
|
|
||||||
'🙀',
|
|
||||||
'😿',
|
|
||||||
'😾',
|
|
||||||
'😾',
|
|
||||||
|
|
||||||
/// People and Fantasy
|
|
||||||
'👶',
|
|
||||||
'👧',
|
|
||||||
'🧒',
|
|
||||||
'👩',
|
|
||||||
'🧑',
|
|
||||||
'👨',
|
|
||||||
'👵',
|
|
||||||
'👴',
|
|
||||||
'👲',
|
|
||||||
'👳♀️️',
|
|
||||||
'👳♂️️️',
|
|
||||||
'🧕️️️',
|
|
||||||
'🧔',
|
|
||||||
'👱♂️️',
|
|
||||||
'👱♀️️️',
|
|
||||||
'👨🦰️️️',
|
|
||||||
'👩🦰',
|
|
||||||
'👨🦱',
|
|
||||||
'👨🦲',
|
|
||||||
'👩🦲',
|
|
||||||
'👨🦳',
|
|
||||||
'👩🦳',
|
|
||||||
'🦸♀️',
|
|
||||||
'🦸♂️️',
|
|
||||||
'🦹♀️️️',
|
|
||||||
'🦹♂️️️️',
|
|
||||||
'👮♀️',
|
|
||||||
'👮♂️️',
|
|
||||||
'👷♀️️️',
|
|
||||||
'👷♂️️️️',
|
|
||||||
'💂♀️️️️️',
|
|
||||||
'💂♂️️️️️️',
|
|
||||||
'🕵️♀️️️️️️️',
|
|
||||||
'🕵️♂️️️️️️️️',
|
|
||||||
'👩⚕️️️️️️️️️',
|
|
||||||
'👨⚕️️️️️️️️️️',
|
|
||||||
'👩🌾️️️️️️️️️️',
|
|
||||||
'👨🌾',
|
|
||||||
'👩🍳',
|
|
||||||
'👨🍳',
|
|
||||||
'👩🎓',
|
|
||||||
'👨🎓',
|
|
||||||
'👩🎤',
|
|
||||||
'👨🎤',
|
|
||||||
'👩🏫',
|
|
||||||
'👨🏫',
|
|
||||||
'👩🏭',
|
|
||||||
'👨🏭',
|
|
||||||
'👩💻',
|
|
||||||
'👨💻',
|
|
||||||
'👩💼',
|
|
||||||
'👨💼',
|
|
||||||
'👩🔧',
|
|
||||||
'👨🔧',
|
|
||||||
'👩🔬',
|
|
||||||
'👨🔬',
|
|
||||||
'👩🎨',
|
|
||||||
'👨🎨',
|
|
||||||
'👩🚒',
|
|
||||||
'👨🚒',
|
|
||||||
'👩✈️',
|
|
||||||
'👨✈️️',
|
|
||||||
'👩🚀',
|
|
||||||
'👨🚀',
|
|
||||||
'👩⚖️',
|
|
||||||
'👨⚖️️',
|
|
||||||
'👰',
|
|
||||||
'🤵',
|
|
||||||
'👸',
|
|
||||||
'🤴',
|
|
||||||
'🤶',
|
|
||||||
'🎅',
|
|
||||||
'🧙♀️',
|
|
||||||
'🧙♂️️',
|
|
||||||
'🧝♀️️️',
|
|
||||||
'🧝♂️',
|
|
||||||
'🧛♀️️',
|
|
||||||
'🧛♂️️️',
|
|
||||||
'🧟♀️️️️',
|
|
||||||
'🧟♂️️️️️',
|
|
||||||
'🧞♀️️️️️️',
|
|
||||||
'🧞♂️️️️️️️',
|
|
||||||
'🧜♀️️️️️️️️',
|
|
||||||
'🧜♂️️️️️️️️️',
|
|
||||||
'🧚♀️️️️️️️️️️',
|
|
||||||
'🧚♂️️️️️️️️️️️',
|
|
||||||
'👼️️️️️️️️️️️',
|
|
||||||
'🤰',
|
|
||||||
'🤱',
|
|
||||||
'🙇♀️',
|
|
||||||
'🙇♂️',
|
|
||||||
'💁♀️️',
|
|
||||||
'💁♂️️️',
|
|
||||||
'🙅♀️️️️',
|
|
||||||
'🙅♂️',
|
|
||||||
'🙆♀️️',
|
|
||||||
'🙆♂️️️',
|
|
||||||
'🙋♀️️️️',
|
|
||||||
'🙋♂️',
|
|
||||||
'🤦♀️️',
|
|
||||||
'🤦♂️️️',
|
|
||||||
'🤷♀️️️️',
|
|
||||||
'🤷♂️️️️️',
|
|
||||||
'🙎♀️️️️️️',
|
|
||||||
'🙎♂️️️️️️️',
|
|
||||||
'🙍♀️️️️️️️️',
|
|
||||||
'🙍♂️️️️️️️️️',
|
|
||||||
'💇♀️️️️️️️️️️',
|
|
||||||
'💇♂️️️️️️️️️️️',
|
|
||||||
'💆♀️️️️️️️️️️️️',
|
|
||||||
'💆♂️️️️️️️️️️️️️',
|
|
||||||
'🧖♀️️️️️️️️️️️️️️',
|
|
||||||
'🧖♂️️️️️️️️️️️️️️️',
|
|
||||||
'💅️️️️️️️️️️️️️️️',
|
|
||||||
'🤳️️️️️️️️️️️️️️',
|
|
||||||
'💃️️️️️️️️️️️️️',
|
|
||||||
'🕺️️️️️️️️️️️️',
|
|
||||||
'👯♀️',
|
|
||||||
'👯♂️️',
|
|
||||||
'🕴️️',
|
|
||||||
'🚶♀️️',
|
|
||||||
'🚶♂️️️',
|
|
||||||
'🏃♀️️️️',
|
|
||||||
'🏃♂️',
|
|
||||||
'👫️',
|
|
||||||
'👭',
|
|
||||||
'👬',
|
|
||||||
'💑',
|
|
||||||
'👩❤️👩',
|
|
||||||
'👨❤️👨',
|
|
||||||
'💏',
|
|
||||||
'👩❤️💋👩',
|
|
||||||
'👨❤️💋👨',
|
|
||||||
'👪',
|
|
||||||
'👨👩👧',
|
|
||||||
'👨👩👧👦',
|
|
||||||
'👨👩👦👦',
|
|
||||||
'👨👩👧👧',
|
|
||||||
'👩👩👦',
|
|
||||||
'👩👩👧',
|
|
||||||
'👩👩👧👦',
|
|
||||||
'👩👩👦👦',
|
|
||||||
'👩👩👧👧',
|
|
||||||
'👨👨👦',
|
|
||||||
'👨👨👧',
|
|
||||||
'👨👨👧👦',
|
|
||||||
'👨👨👦👦',
|
|
||||||
'👨👨👧👧',
|
|
||||||
'👩👦',
|
|
||||||
'👩👧',
|
|
||||||
'👩👧👦',
|
|
||||||
'👩👦👦',
|
|
||||||
'👩👧👧',
|
|
||||||
'👨👦',
|
|
||||||
'👨👧',
|
|
||||||
'👨👧👦',
|
|
||||||
'👨👦👦',
|
|
||||||
'👨👧👧',
|
|
||||||
'🤲',
|
|
||||||
'👐',
|
|
||||||
'🙌',
|
|
||||||
'👏',
|
|
||||||
'🤝',
|
|
||||||
'👍',
|
|
||||||
'👎',
|
|
||||||
'👊',
|
|
||||||
'✊',
|
|
||||||
'🤛',
|
|
||||||
'🤜',
|
|
||||||
'🤞',
|
|
||||||
'✌️',
|
|
||||||
'🤟️',
|
|
||||||
'🤘',
|
|
||||||
'👌',
|
|
||||||
'👈',
|
|
||||||
'👉',
|
|
||||||
'👆',
|
|
||||||
'👇',
|
|
||||||
'☝️',
|
|
||||||
'✋️',
|
|
||||||
'🤚️',
|
|
||||||
'🤚️',
|
|
||||||
'🖐',
|
|
||||||
'🖖',
|
|
||||||
'👋',
|
|
||||||
'🤙',
|
|
||||||
'💪',
|
|
||||||
'🦵',
|
|
||||||
'🦶',
|
|
||||||
'🖕',
|
|
||||||
'✍️',
|
|
||||||
'🙏️',
|
|
||||||
'💍',
|
|
||||||
'💄',
|
|
||||||
'💋',
|
|
||||||
'👄',
|
|
||||||
'👅',
|
|
||||||
'👂',
|
|
||||||
'👃',
|
|
||||||
'👣',
|
|
||||||
'👁',
|
|
||||||
'👀',
|
|
||||||
'🧠',
|
|
||||||
'🦴',
|
|
||||||
'🦷',
|
|
||||||
'🗣',
|
|
||||||
'👤',
|
|
||||||
'👥',
|
|
||||||
'🧥',
|
|
||||||
'👚',
|
|
||||||
'👕',
|
|
||||||
'👖',
|
|
||||||
'👔',
|
|
||||||
'👗',
|
|
||||||
'👙',
|
|
||||||
'👘',
|
|
||||||
'👠',
|
|
||||||
'👡',
|
|
||||||
'👢',
|
|
||||||
'👞',
|
|
||||||
'👟',
|
|
||||||
'🥾',
|
|
||||||
'🥿',
|
|
||||||
'🧦',
|
|
||||||
'🧤',
|
|
||||||
'🧣',
|
|
||||||
'🎩',
|
|
||||||
'🧢',
|
|
||||||
'👒',
|
|
||||||
'🎓',
|
|
||||||
'⛑',
|
|
||||||
'👑',
|
|
||||||
'👝',
|
|
||||||
'👛',
|
|
||||||
'👜',
|
|
||||||
'💼',
|
|
||||||
'🎒',
|
|
||||||
'👓',
|
|
||||||
'🕶',
|
|
||||||
'🥽',
|
|
||||||
'🥼',
|
|
||||||
'🌂',
|
|
||||||
'🧵',
|
|
||||||
'🧶',
|
|
||||||
|
|
||||||
/// Animals
|
|
||||||
'🐶',
|
|
||||||
'🐱',
|
|
||||||
'🐭',
|
|
||||||
'🐰',
|
|
||||||
'🦊',
|
|
||||||
'🦝',
|
|
||||||
'🐻',
|
|
||||||
'🦘',
|
|
||||||
'🦡',
|
|
||||||
'🐨',
|
|
||||||
'🐯',
|
|
||||||
'🦁',
|
|
||||||
'🐼',
|
|
||||||
'🐼',
|
|
||||||
'🐮',
|
|
||||||
'🐷',
|
|
||||||
'🐽',
|
|
||||||
'🐸',
|
|
||||||
'🐵',
|
|
||||||
'🙈',
|
|
||||||
'🙉',
|
|
||||||
'🙊',
|
|
||||||
'🐒',
|
|
||||||
'🐔',
|
|
||||||
'🐧',
|
|
||||||
'🐦',
|
|
||||||
'🐤',
|
|
||||||
'🐣',
|
|
||||||
'🐥',
|
|
||||||
'🦆',
|
|
||||||
'🦢',
|
|
||||||
'🦅',
|
|
||||||
'🦉',
|
|
||||||
'🦚',
|
|
||||||
'🦜',
|
|
||||||
'🦇',
|
|
||||||
'🐺',
|
|
||||||
'🐗',
|
|
||||||
'🐴',
|
|
||||||
'🦄',
|
|
||||||
'🐝',
|
|
||||||
'🐛',
|
|
||||||
'🦋',
|
|
||||||
'🐌',
|
|
||||||
'🐚',
|
|
||||||
'🐞',
|
|
||||||
'🐜',
|
|
||||||
'🦗',
|
|
||||||
'🕷',
|
|
||||||
'🕸',
|
|
||||||
'🦂',
|
|
||||||
'🦟',
|
|
||||||
'🦠',
|
|
||||||
'🐢',
|
|
||||||
'🐍',
|
|
||||||
'🦎',
|
|
||||||
'🦖',
|
|
||||||
'🦕',
|
|
||||||
'🐙',
|
|
||||||
'🦑',
|
|
||||||
'🦐',
|
|
||||||
'🦀',
|
|
||||||
'🐡',
|
|
||||||
'🐠',
|
|
||||||
'🐟',
|
|
||||||
'🐬',
|
|
||||||
'🐳',
|
|
||||||
'🐋',
|
|
||||||
'🦈',
|
|
||||||
'🐊',
|
|
||||||
'🐅',
|
|
||||||
'🐆',
|
|
||||||
'🦓',
|
|
||||||
'🦍',
|
|
||||||
'🐘',
|
|
||||||
'🦏',
|
|
||||||
'🦛',
|
|
||||||
'🐪',
|
|
||||||
'🐫',
|
|
||||||
'🦙',
|
|
||||||
'🦒',
|
|
||||||
'🐃',
|
|
||||||
'🐂',
|
|
||||||
'🐄',
|
|
||||||
'🐎',
|
|
||||||
'🐖',
|
|
||||||
'🐏',
|
|
||||||
'🐐',
|
|
||||||
'🦌',
|
|
||||||
'🐕',
|
|
||||||
'🐩',
|
|
||||||
'🐈',
|
|
||||||
'🐓',
|
|
||||||
'🦃',
|
|
||||||
'🕊',
|
|
||||||
'🐇',
|
|
||||||
'🐁',
|
|
||||||
'🐀',
|
|
||||||
'🐿',
|
|
||||||
'🦔',
|
|
||||||
'🐾',
|
|
||||||
'🐉',
|
|
||||||
'🐲',
|
|
||||||
'🌵',
|
|
||||||
'🎄',
|
|
||||||
'🌲',
|
|
||||||
'🌳',
|
|
||||||
'🌴',
|
|
||||||
'🌱',
|
|
||||||
'🌿',
|
|
||||||
'☘️',
|
|
||||||
'🎍️',
|
|
||||||
'🎋️',
|
|
||||||
'🍃',
|
|
||||||
'🍂',
|
|
||||||
'🍁',
|
|
||||||
'🍄',
|
|
||||||
'🌾️',
|
|
||||||
'💐️',
|
|
||||||
'🌷️',
|
|
||||||
'🌹',
|
|
||||||
'🥀',
|
|
||||||
'🌺',
|
|
||||||
'🌸',
|
|
||||||
'🌼',
|
|
||||||
'🌻️',
|
|
||||||
'🌞',
|
|
||||||
'🌝',
|
|
||||||
'🌛',
|
|
||||||
'🌜',
|
|
||||||
'🌚',
|
|
||||||
'🌕',
|
|
||||||
'🌖',
|
|
||||||
'🌗',
|
|
||||||
'🌘',
|
|
||||||
'🌑',
|
|
||||||
'🌒',
|
|
||||||
'🌔',
|
|
||||||
'🌙',
|
|
||||||
'🌎',
|
|
||||||
'🌍',
|
|
||||||
'🌏',
|
|
||||||
'💫',
|
|
||||||
'⭐️',
|
|
||||||
'🌟️',
|
|
||||||
'✨️',
|
|
||||||
'⚡️️',
|
|
||||||
'☄️️️',
|
|
||||||
'💥️️️',
|
|
||||||
'🔥',
|
|
||||||
'🌪',
|
|
||||||
'🌈',
|
|
||||||
'☀️',
|
|
||||||
'🌤️',
|
|
||||||
'⛅️️',
|
|
||||||
'🌥️️',
|
|
||||||
'☁️️',
|
|
||||||
'🌦️️',
|
|
||||||
'🌧️',
|
|
||||||
'⛈',
|
|
||||||
'🌩',
|
|
||||||
'🌨',
|
|
||||||
'❄️',
|
|
||||||
'☃️️',
|
|
||||||
'⛄️️️',
|
|
||||||
'🌬️️️',
|
|
||||||
'💨️️️',
|
|
||||||
'💧️️️',
|
|
||||||
'💦️️️',
|
|
||||||
'☔️️️️',
|
|
||||||
'☂️️️️️',
|
|
||||||
'🌊️️️️️',
|
|
||||||
'🌫️️️️',
|
|
||||||
|
|
||||||
/// Foods
|
|
||||||
'🍏',
|
|
||||||
'🍎',
|
|
||||||
'🍐',
|
|
||||||
'🍊',
|
|
||||||
'🍋',
|
|
||||||
'🍌',
|
|
||||||
'🍉',
|
|
||||||
'🍇',
|
|
||||||
'🍓',
|
|
||||||
'🍈',
|
|
||||||
'🍒',
|
|
||||||
'🍑',
|
|
||||||
'🍍',
|
|
||||||
'🥭',
|
|
||||||
'🥥',
|
|
||||||
'🥝',
|
|
||||||
'🍅',
|
|
||||||
'🍆',
|
|
||||||
'🥑',
|
|
||||||
'🥦',
|
|
||||||
'🥒',
|
|
||||||
'🥬',
|
|
||||||
'🌶',
|
|
||||||
'🌽',
|
|
||||||
'🥕',
|
|
||||||
'🥔',
|
|
||||||
'🍠',
|
|
||||||
'🥐',
|
|
||||||
'🍞',
|
|
||||||
'🥖',
|
|
||||||
'🥨',
|
|
||||||
'🥯',
|
|
||||||
'🧀',
|
|
||||||
'🥚',
|
|
||||||
'🍳',
|
|
||||||
'🥞',
|
|
||||||
'🥓',
|
|
||||||
'🥩',
|
|
||||||
'🍗',
|
|
||||||
'🍖',
|
|
||||||
'🌭',
|
|
||||||
'🍔',
|
|
||||||
'🍟',
|
|
||||||
'🍕',
|
|
||||||
'🥪',
|
|
||||||
'🥙',
|
|
||||||
'🌮',
|
|
||||||
'🌯',
|
|
||||||
'🥗',
|
|
||||||
'🥘',
|
|
||||||
'🥫',
|
|
||||||
'🍝',
|
|
||||||
'🍜',
|
|
||||||
'🍲',
|
|
||||||
'🍛',
|
|
||||||
'🍣',
|
|
||||||
'🍱',
|
|
||||||
'🥟',
|
|
||||||
'🍤',
|
|
||||||
'🍙',
|
|
||||||
'🍚',
|
|
||||||
'🍘',
|
|
||||||
'🍥',
|
|
||||||
'🥮',
|
|
||||||
'🥠',
|
|
||||||
'🍢',
|
|
||||||
'🍧',
|
|
||||||
'🍨',
|
|
||||||
'🍦',
|
|
||||||
'🥧',
|
|
||||||
'🍰',
|
|
||||||
'🎂',
|
|
||||||
'🍮',
|
|
||||||
'🍭',
|
|
||||||
'🍬',
|
|
||||||
'🍫',
|
|
||||||
'🍿',
|
|
||||||
'🧂',
|
|
||||||
'🍩',
|
|
||||||
'🍪',
|
|
||||||
'🌰',
|
|
||||||
'🥜',
|
|
||||||
'🍯',
|
|
||||||
'🥛',
|
|
||||||
'🍼',
|
|
||||||
'☕️',
|
|
||||||
'🍵️',
|
|
||||||
'🥤️',
|
|
||||||
'🍶',
|
|
||||||
'🍺',
|
|
||||||
'🍻',
|
|
||||||
'🥂',
|
|
||||||
'🍷',
|
|
||||||
'🍸',
|
|
||||||
'🍹',
|
|
||||||
'🍾',
|
|
||||||
'🥄',
|
|
||||||
'🍴',
|
|
||||||
'🍽',
|
|
||||||
'🥣',
|
|
||||||
'🥡',
|
|
||||||
'🥢',
|
|
||||||
|
|
||||||
/// Activity and Sports
|
|
||||||
'⚽️',
|
|
||||||
'🏀️',
|
|
||||||
'🏈',
|
|
||||||
'⚾️',
|
|
||||||
'🥎️',
|
|
||||||
'🏐️',
|
|
||||||
'🏉',
|
|
||||||
'🎾',
|
|
||||||
'🥏',
|
|
||||||
'🎱',
|
|
||||||
'🏓',
|
|
||||||
'🏸',
|
|
||||||
'🥅',
|
|
||||||
'🏒',
|
|
||||||
'🏑',
|
|
||||||
'🥍',
|
|
||||||
'🏏',
|
|
||||||
'⛳️',
|
|
||||||
'🏹️',
|
|
||||||
'🎣️',
|
|
||||||
'🥊',
|
|
||||||
'🥋',
|
|
||||||
'🎽',
|
|
||||||
'⛸',
|
|
||||||
'🥌',
|
|
||||||
'🛷',
|
|
||||||
'🛹',
|
|
||||||
'🎿',
|
|
||||||
'⛷',
|
|
||||||
'🏂',
|
|
||||||
'🏋️♀️',
|
|
||||||
'🏋🏼♀️',
|
|
||||||
'🏋🏽♀️️',
|
|
||||||
'🏋🏾♀️️️',
|
|
||||||
'🏋🏿♀️️️️',
|
|
||||||
'🏋️♂️️️️',
|
|
||||||
'🏋🏻♂️️️️',
|
|
||||||
'🏋🏼♂️️️️',
|
|
||||||
'🏋🏽♂️️️️',
|
|
||||||
'🏋🏾♂️️️️',
|
|
||||||
'🏋🏿♂️️️️',
|
|
||||||
'🤼♀️️️️',
|
|
||||||
'🤼♂️️️️',
|
|
||||||
'🤸♀️️️️',
|
|
||||||
'🤸🏻♀️️️️',
|
|
||||||
'🤸🏼♀️️️️',
|
|
||||||
'🤸🏽♀️️️️',
|
|
||||||
'🤸🏿♀️️️️️',
|
|
||||||
'🤸♂️️️️',
|
|
||||||
'🤸🏻♂️️️️',
|
|
||||||
'🤸🏼♂️️️️️',
|
|
||||||
'🤸🏽♂️️️️️️',
|
|
||||||
'🤸🏾♂️️️️️️',
|
|
||||||
'🤸🏿♂️️️️️️',
|
|
||||||
'⛹️♀️️️️️️',
|
|
||||||
'⛹🏻♀️️️️️️️',
|
|
||||||
'⛹🏼♀️️️️️️️️',
|
|
||||||
'⛹🏽♀️️️️️️️️️',
|
|
||||||
'⛹🏾♀️️️️️️️️️️',
|
|
||||||
'⛹🏿♀️️️️️️️️️️️',
|
|
||||||
'⛹️♂️️️️️️️️️️️️',
|
|
||||||
'⛹🏻♂️️️️️️️️️️️️️',
|
|
||||||
'⛹🏼♂️️️️️️️️️️️️️️',
|
|
||||||
'⛹🏽♂️️️️️️️️️️️️️️️',
|
|
||||||
'⛹🏾♂️️️️️️️️️️️️️️️️',
|
|
||||||
'⛹🏿♂️',
|
|
||||||
'🤺️',
|
|
||||||
'🤾♀️',
|
|
||||||
'🤾🏻♀️️',
|
|
||||||
'🤾🏼♀️️️',
|
|
||||||
'🤾🏾♀️️️️',
|
|
||||||
];
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
||||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
|
||||||
|
|
||||||
/// Emoji layer
|
|
||||||
class EmojiLayer extends StatefulWidget {
|
|
||||||
const EmojiLayer({
|
|
||||||
required this.layerData,
|
|
||||||
super.key,
|
|
||||||
this.onUpdate,
|
|
||||||
});
|
|
||||||
final EmojiLayerData layerData;
|
|
||||||
final VoidCallback? onUpdate;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<EmojiLayer> createState() => _EmojiLayerState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EmojiLayerState extends State<EmojiLayer> {
|
|
||||||
double initialRotation = 0;
|
|
||||||
Offset initialOffset = Offset.zero;
|
|
||||||
Offset initialFocalPoint = Offset.zero;
|
|
||||||
double initialScale = 1;
|
|
||||||
bool deleteLayer = false;
|
|
||||||
bool twoPointerWhereDown = false;
|
|
||||||
final GlobalKey outlineKey = GlobalKey();
|
|
||||||
final GlobalKey emojiKey = GlobalKey();
|
|
||||||
int pointers = 0;
|
|
||||||
bool display = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
if (widget.layerData.offset.dy == 0) {
|
|
||||||
// Set the initial offset to the center of the screen
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
setState(() {
|
|
||||||
widget.layerData.offset = Offset(
|
|
||||||
MediaQuery.of(context).size.width / 2 - (153 / 2),
|
|
||||||
MediaQuery.of(context).size.height / 2 - (153 / 2) - 100,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
display = true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
display = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (!display) return Container();
|
|
||||||
if (widget.layerData.isDeleted) return Container();
|
|
||||||
return Stack(
|
|
||||||
key: outlineKey,
|
|
||||||
children: [
|
|
||||||
Positioned(
|
|
||||||
left: widget.layerData.offset.dx,
|
|
||||||
top: widget.layerData.offset.dy,
|
|
||||||
child: Listener(
|
|
||||||
onPointerUp: (details) {
|
|
||||||
setState(() {
|
|
||||||
pointers--;
|
|
||||||
if (pointers == 0) {
|
|
||||||
twoPointerWhereDown = false;
|
|
||||||
}
|
|
||||||
if (deleteLayer) {
|
|
||||||
widget.layerData.isDeleted = true;
|
|
||||||
widget.onUpdate!();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onPointerDown: (details) {
|
|
||||||
setState(() {
|
|
||||||
pointers++;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: GestureDetector(
|
|
||||||
onScaleStart: (details) {
|
|
||||||
initialScale = widget.layerData.size;
|
|
||||||
initialRotation = widget.layerData.rotation;
|
|
||||||
initialOffset = widget.layerData.offset;
|
|
||||||
initialFocalPoint =
|
|
||||||
Offset(details.focalPoint.dx, details.focalPoint.dy);
|
|
||||||
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
onScaleUpdate: (details) async {
|
|
||||||
if (twoPointerWhereDown && details.pointerCount != 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final outlineBox =
|
|
||||||
outlineKey.currentContext!.findRenderObject()! as RenderBox;
|
|
||||||
|
|
||||||
final emojiBox =
|
|
||||||
emojiKey.currentContext!.findRenderObject()! as RenderBox;
|
|
||||||
|
|
||||||
final isAtTheBottom =
|
|
||||||
(widget.layerData.offset.dy + emojiBox.size.height / 2) >
|
|
||||||
outlineBox.size.height - 80;
|
|
||||||
final isInTheCenter = MediaQuery.of(context).size.width / 2 -
|
|
||||||
30 <
|
|
||||||
(widget.layerData.offset.dx +
|
|
||||||
emojiBox.size.width / 2) &&
|
|
||||||
MediaQuery.of(context).size.width / 2 + 20 >
|
|
||||||
(widget.layerData.offset.dx + emojiBox.size.width / 2);
|
|
||||||
|
|
||||||
if (isAtTheBottom && isInTheCenter) {
|
|
||||||
if (!deleteLayer) {
|
|
||||||
await HapticFeedback.heavyImpact();
|
|
||||||
}
|
|
||||||
deleteLayer = true;
|
|
||||||
} else {
|
|
||||||
deleteLayer = false;
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
twoPointerWhereDown = details.pointerCount >= 2;
|
|
||||||
widget.layerData.size = initialScale * details.scale;
|
|
||||||
if (widget.layerData.size > 96) {
|
|
||||||
// https://github.com/twonlyapp/twonly-app/issues/349
|
|
||||||
widget.layerData.size = 96;
|
|
||||||
}
|
|
||||||
// print(widget.layerData.size);
|
|
||||||
widget.layerData.rotation =
|
|
||||||
initialRotation + details.rotation;
|
|
||||||
|
|
||||||
// Update the position based on the translation
|
|
||||||
final dx = (initialOffset.dx) +
|
|
||||||
(details.focalPoint.dx - initialFocalPoint.dx);
|
|
||||||
final dy = (initialOffset.dy) +
|
|
||||||
(details.focalPoint.dy - initialFocalPoint.dy);
|
|
||||||
widget.layerData.offset = Offset(dx, dy);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Transform.rotate(
|
|
||||||
angle: widget.layerData.rotation,
|
|
||||||
key: emojiKey,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(44),
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: Text(
|
|
||||||
widget.layerData.text,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: widget.layerData.size,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (pointers > 0)
|
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 20,
|
|
||||||
child: Center(
|
|
||||||
child: GestureDetector(
|
|
||||||
child: ActionButton(
|
|
||||||
FontAwesomeIcons.trashCan,
|
|
||||||
tooltipText: '',
|
|
||||||
color: deleteLayer ? Colors.red : Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,7 +13,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/services/flame.service.dart';
|
import 'package:twonly/src/services/flame.service.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_components/best_friends_selector.dart';
|
import 'package:twonly/src/views/camera/share_image_contact_selection/best_friends_selector.dart';
|
||||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||||
import 'package:twonly/src/views/components/flame.dart';
|
import 'package:twonly/src/views/components/flame.dart';
|
||||||
import 'package:twonly/src/views/components/headline.dart';
|
import 'package:twonly/src/views/components/headline.dart';
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:drift/drift.dart' show Value;
|
import 'package:drift/drift.dart' show Value;
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
@ -18,13 +19,13 @@ import 'package:twonly/src/utils/screenshot.dart';
|
||||||
import 'package:twonly/src/utils/storage.dart';
|
import 'package:twonly/src/utils/storage.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
import 'package:twonly/src/views/camera/share_image_contact_selection.view.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/image_item.dart';
|
import 'package:twonly/src/views/camera/share_image_contact_selection/select_show_time.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers_viewer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/image_item.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_components/select_show_time.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers_viewer.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_view.dart';
|
import 'package:twonly/src/views/components/emoji_picker.bottom.dart';
|
||||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||||
import 'package:twonly/src/views/components/notification_badge.dart';
|
import 'package:twonly/src/views/components/notification_badge.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hand_signature/signature.dart';
|
import 'package:hand_signature/signature.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/image_item.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/image_item.dart';
|
||||||
|
|
||||||
/// Layer class with some common properties
|
/// Layer class with some common properties
|
||||||
class Layer {
|
class Layer {
|
||||||
|
|
@ -51,7 +51,7 @@ class EmojiLayerData extends Layer {
|
||||||
EmojiLayerData({
|
EmojiLayerData({
|
||||||
required super.key,
|
required super.key,
|
||||||
this.text = '',
|
this.text = '',
|
||||||
this.size = 64,
|
this.size = 94,
|
||||||
super.offset,
|
super.offset,
|
||||||
super.opacity,
|
super.opacity,
|
||||||
super.rotation,
|
super.rotation,
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
|
|
||||||
class BackgroundLayer extends StatefulWidget {
|
class BackgroundLayer extends StatefulWidget {
|
||||||
const BackgroundLayer({
|
const BackgroundLayer({
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:hand_signature/signature.dart';
|
import 'package:hand_signature/signature.dart';
|
||||||
// ignore: implementation_imports
|
|
||||||
import 'package:hand_signature/src/utils.dart';
|
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/screenshot.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/draw/custom_hand_signature.dart';
|
||||||
|
|
||||||
class DrawLayer extends StatefulWidget {
|
class DrawLayer extends StatefulWidget {
|
||||||
const DrawLayer({
|
const DrawLayer({
|
||||||
|
|
@ -23,8 +22,6 @@ class DrawLayer extends StatefulWidget {
|
||||||
class _DrawLayerState extends State<DrawLayer> {
|
class _DrawLayerState extends State<DrawLayer> {
|
||||||
Color currentColor = Colors.red;
|
Color currentColor = Colors.red;
|
||||||
|
|
||||||
ScreenshotController screenshotController = ScreenshotController();
|
|
||||||
|
|
||||||
List<CubicPath> undoList = [];
|
List<CubicPath> undoList = [];
|
||||||
bool skipNextEvent = false;
|
bool skipNextEvent = false;
|
||||||
bool showMagnifyingGlass = false;
|
bool showMagnifyingGlass = false;
|
||||||
|
|
@ -85,17 +82,11 @@ class _DrawLayerState extends State<DrawLayer> {
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Container(
|
child: CustomHandSignature(
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.transparent,
|
|
||||||
),
|
|
||||||
child: Screenshot(
|
|
||||||
controller: screenshotController,
|
|
||||||
child: HandSignature(
|
|
||||||
control: widget.layerData.control,
|
control: widget.layerData.control,
|
||||||
drawer: CustomSignatureDrawer(color: currentColor, width: 7),
|
isModificationEnabled: widget.layerData.isEditing,
|
||||||
),
|
currentColor: currentColor,
|
||||||
),
|
width: 7,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.layerData.isEditing && widget.layerData.showCustomButtons)
|
if (widget.layerData.isEditing && widget.layerData.showCustomButtons)
|
||||||
|
|
@ -211,12 +202,12 @@ class _DrawLayerState extends State<DrawLayer> {
|
||||||
top: 50 + (185 * _sliderValue),
|
top: 50 + (185 * _sliderValue),
|
||||||
child: MagnifyingGlass(color: currentColor),
|
child: MagnifyingGlass(color: currentColor),
|
||||||
),
|
),
|
||||||
if (!widget.layerData.isEditing)
|
// if (!widget.layerData.isEditing)
|
||||||
Positioned.fill(
|
// Positioned.fill(
|
||||||
child: Container(
|
// child: Container(
|
||||||
color: Colors.transparent,
|
// color: Colors.transparent,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -244,33 +235,3 @@ class MagnifyingGlass extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CustomSignatureDrawer extends HandSignatureDrawer {
|
|
||||||
const CustomSignatureDrawer({
|
|
||||||
this.width = 1.0,
|
|
||||||
this.color = Colors.black,
|
|
||||||
});
|
|
||||||
final Color color;
|
|
||||||
final double width;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
|
||||||
for (final path in paths) {
|
|
||||||
var lineColor = color;
|
|
||||||
if (path.setup.args!['color'] != null) {
|
|
||||||
lineColor = path.setup.args!['color'] as Color;
|
|
||||||
} else {
|
|
||||||
path.setup.args!['color'] = color;
|
|
||||||
}
|
|
||||||
final paint = Paint()
|
|
||||||
..color = lineColor
|
|
||||||
..style = PaintingStyle.stroke
|
|
||||||
..strokeCap = StrokeCap.round
|
|
||||||
..strokeJoin = StrokeJoin.round
|
|
||||||
..strokeWidth = width;
|
|
||||||
if (path.isFilled) {
|
|
||||||
canvas.drawPath(PathUtil.toLinePath(path.lines), paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hand_signature/signature.dart';
|
||||||
|
// ignore: implementation_imports
|
||||||
|
import 'package:hand_signature/src/utils.dart';
|
||||||
|
|
||||||
|
class CustomHandSignature extends StatelessWidget {
|
||||||
|
const CustomHandSignature({
|
||||||
|
required this.control,
|
||||||
|
required this.isModificationEnabled,
|
||||||
|
required this.currentColor,
|
||||||
|
required this.width,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The controller that manages the creation and manipulation of signature paths.
|
||||||
|
final HandSignatureControl control;
|
||||||
|
final bool isModificationEnabled;
|
||||||
|
final Color currentColor;
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
control.params = SignaturePaintParams(
|
||||||
|
color: currentColor,
|
||||||
|
strokeWidth: 7,
|
||||||
|
);
|
||||||
|
|
||||||
|
final drawer = CustomSignatureDrawer(color: currentColor, width: width);
|
||||||
|
|
||||||
|
if (isModificationEnabled) {
|
||||||
|
return HandSignature(
|
||||||
|
control: control,
|
||||||
|
drawer: drawer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IgnorePointer(
|
||||||
|
child: ClipRRect(
|
||||||
|
child: HandSignaturePaint(
|
||||||
|
control: control,
|
||||||
|
drawer: drawer,
|
||||||
|
onSize: control.notifyDimension,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomSignatureDrawer extends HandSignatureDrawer {
|
||||||
|
const CustomSignatureDrawer({
|
||||||
|
this.width = 1.0,
|
||||||
|
this.color = Colors.black,
|
||||||
|
});
|
||||||
|
final Color color;
|
||||||
|
final double width;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||||
|
for (final path in paths) {
|
||||||
|
var lineColor = color;
|
||||||
|
if (path.setup.args!['color'] != null) {
|
||||||
|
lineColor = path.setup.args!['color'] as Color;
|
||||||
|
} else {
|
||||||
|
path.setup.args!['color'] = color;
|
||||||
|
}
|
||||||
|
final paint = Paint()
|
||||||
|
..color = lineColor
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeCap = StrokeCap.round
|
||||||
|
..strokeJoin = StrokeJoin.round
|
||||||
|
..strokeWidth = width;
|
||||||
|
if (path.isFilled) {
|
||||||
|
canvas.drawPath(PathUtil.toLinePath(path.lines), paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
248
lib/src/views/camera/share_image_editor/layers/emoji.layer.dart
Executable file
248
lib/src/views/camera/share_image_editor/layers/emoji.layer.dart
Executable file
|
|
@ -0,0 +1,248 @@
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||||
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
|
|
||||||
|
/// Emoji layer
|
||||||
|
class EmojiLayer extends StatefulWidget {
|
||||||
|
const EmojiLayer({
|
||||||
|
required this.layerData,
|
||||||
|
super.key,
|
||||||
|
this.onUpdate,
|
||||||
|
});
|
||||||
|
final EmojiLayerData layerData;
|
||||||
|
final VoidCallback? onUpdate;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<EmojiLayer> createState() => _EmojiLayerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EmojiLayerState extends State<EmojiLayer> {
|
||||||
|
double initialRotation = 0;
|
||||||
|
Offset initialOffset = Offset.zero;
|
||||||
|
Offset initialFocalPoint = Offset.zero;
|
||||||
|
double initialScale = 1;
|
||||||
|
bool deleteLayer = false;
|
||||||
|
bool twoPointerWhereDown = false;
|
||||||
|
final GlobalKey outlineKey = GlobalKey();
|
||||||
|
final GlobalKey emojiKey = GlobalKey();
|
||||||
|
int pointers = 0;
|
||||||
|
bool display = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
if (widget.layerData.offset.dy == 0) {
|
||||||
|
// Set the initial offset to the center of the screen
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
setState(() {
|
||||||
|
widget.layerData.offset = Offset(
|
||||||
|
MediaQuery.of(context).size.width / 2 - (153 / 2),
|
||||||
|
MediaQuery.of(context).size.height / 2 - (153 / 2) - 100,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
display = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
display = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (!display) return Container();
|
||||||
|
if (widget.layerData.isDeleted) return Container();
|
||||||
|
return Stack(
|
||||||
|
key: outlineKey,
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
left: widget.layerData.offset.dx,
|
||||||
|
top: widget.layerData.offset.dy,
|
||||||
|
child: PhysicalModel(
|
||||||
|
color: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(180),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: Listener(
|
||||||
|
onPointerUp: (details) {
|
||||||
|
setState(() {
|
||||||
|
pointers--;
|
||||||
|
if (pointers == 0) {
|
||||||
|
twoPointerWhereDown = false;
|
||||||
|
}
|
||||||
|
if (deleteLayer) {
|
||||||
|
widget.layerData.isDeleted = true;
|
||||||
|
widget.onUpdate!();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onPointerDown: (details) {
|
||||||
|
setState(() {
|
||||||
|
pointers++;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onScaleStart: (details) {
|
||||||
|
initialScale = widget.layerData.size;
|
||||||
|
initialRotation = widget.layerData.rotation;
|
||||||
|
initialOffset = widget.layerData.offset;
|
||||||
|
initialFocalPoint =
|
||||||
|
Offset(details.focalPoint.dx, details.focalPoint.dy);
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
onScaleUpdate: (details) async {
|
||||||
|
if (twoPointerWhereDown && details.pointerCount != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final outlineBox = outlineKey.currentContext!
|
||||||
|
.findRenderObject()! as RenderBox;
|
||||||
|
|
||||||
|
final emojiBox =
|
||||||
|
emojiKey.currentContext!.findRenderObject()! as RenderBox;
|
||||||
|
|
||||||
|
final isAtTheBottom =
|
||||||
|
(widget.layerData.offset.dy + emojiBox.size.height / 2) >
|
||||||
|
outlineBox.size.height - 80;
|
||||||
|
final isInTheCenter =
|
||||||
|
MediaQuery.of(context).size.width / 2 - 30 <
|
||||||
|
(widget.layerData.offset.dx +
|
||||||
|
emojiBox.size.width / 2) &&
|
||||||
|
MediaQuery.of(context).size.width / 2 + 20 >
|
||||||
|
(widget.layerData.offset.dx +
|
||||||
|
emojiBox.size.width / 2);
|
||||||
|
|
||||||
|
if (isAtTheBottom && isInTheCenter) {
|
||||||
|
if (!deleteLayer) {
|
||||||
|
await HapticFeedback.heavyImpact();
|
||||||
|
}
|
||||||
|
deleteLayer = true;
|
||||||
|
} else {
|
||||||
|
deleteLayer = false;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
twoPointerWhereDown = details.pointerCount >= 2;
|
||||||
|
widget.layerData.size = initialScale * details.scale;
|
||||||
|
// print(widget.layerData.size);
|
||||||
|
widget.layerData.rotation =
|
||||||
|
initialRotation + details.rotation;
|
||||||
|
|
||||||
|
// Update the position based on the translation
|
||||||
|
final dx = (initialOffset.dx) +
|
||||||
|
(details.focalPoint.dx - initialFocalPoint.dx);
|
||||||
|
final dy = (initialOffset.dy) +
|
||||||
|
(details.focalPoint.dy - initialFocalPoint.dy);
|
||||||
|
widget.layerData.offset = Offset(dx, dy);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Transform.rotate(
|
||||||
|
angle: widget.layerData.rotation,
|
||||||
|
key: emojiKey,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(44),
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: ScreenshotEmoji(
|
||||||
|
emoji: widget.layerData.text,
|
||||||
|
displaySize: widget.layerData.size,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (pointers > 0)
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 20,
|
||||||
|
child: Center(
|
||||||
|
child: GestureDetector(
|
||||||
|
child: ActionButton(
|
||||||
|
FontAwesomeIcons.trashCan,
|
||||||
|
tooltipText: '',
|
||||||
|
color: deleteLayer ? Colors.red : Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround: https://github.com/twonlyapp/twonly-app/issues/349
|
||||||
|
class ScreenshotEmoji extends StatefulWidget {
|
||||||
|
const ScreenshotEmoji({
|
||||||
|
required this.emoji,
|
||||||
|
required this.displaySize,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
final String emoji;
|
||||||
|
final double displaySize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ScreenshotEmoji> createState() => _ScreenshotEmojiState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ScreenshotEmojiState extends State<ScreenshotEmoji> {
|
||||||
|
final GlobalKey _boundaryKey = GlobalKey();
|
||||||
|
ui.Image? _capturedImage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// Capture the emoji immediately after the first frame
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => _captureEmoji());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _captureEmoji() async {
|
||||||
|
try {
|
||||||
|
final boundary = _boundaryKey.currentContext?.findRenderObject()
|
||||||
|
as RenderRepaintBoundary?;
|
||||||
|
if (boundary == null) return;
|
||||||
|
|
||||||
|
final image = await boundary.toImage(pixelRatio: 4);
|
||||||
|
setState(() {
|
||||||
|
_capturedImage = image;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Error capturing emoji: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (_capturedImage != null) {
|
||||||
|
return SizedBox(
|
||||||
|
width: widget.displaySize,
|
||||||
|
height: widget.displaySize,
|
||||||
|
child: RawImage(
|
||||||
|
image: _capturedImage,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
Positioned(
|
||||||
|
top: -200, // hide from the user as the size changes with the image
|
||||||
|
child: RepaintBoundary(
|
||||||
|
key: _boundaryKey,
|
||||||
|
child: Text(
|
||||||
|
widget.emoji,
|
||||||
|
style: const TextStyle(fontSize: 94),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: widget.displaySize, height: widget.displaySize),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,10 +2,10 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/datetime_filter.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/datetime_filter.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/image_filter.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/image_filter.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/location_filter.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/location_filter.dart';
|
||||||
|
|
||||||
/// Main layer
|
/// Main layer
|
||||||
class FilterLayer extends StatefulWidget {
|
class FilterLayer extends StatefulWidget {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||||
|
|
||||||
class DateTimeFilter extends StatelessWidget {
|
class DateTimeFilter extends StatelessWidget {
|
||||||
const DateTimeFilter({super.key, this.color = Colors.white});
|
const DateTimeFilter({super.key, this.color = Colors.white});
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||||
|
|
||||||
class ImageFilter extends StatelessWidget {
|
class ImageFilter extends StatelessWidget {
|
||||||
const ImageFilter({required this.imagePath, super.key});
|
const ImageFilter({required this.imagePath, super.key});
|
||||||
|
|
@ -11,8 +11,8 @@ import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/datetime_filter.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/datetime_filter.dart';
|
||||||
|
|
||||||
class LocationFilter extends StatefulWidget {
|
class LocationFilter extends StatefulWidget {
|
||||||
const LocationFilter({super.key});
|
const LocationFilter({super.key});
|
||||||
|
|
@ -5,8 +5,8 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:twonly/src/providers/image_editor.provider.dart';
|
import 'package:twonly/src/providers/image_editor.provider.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
|
|
||||||
/// Text layer
|
/// Text layer
|
||||||
class TextLayer extends StatefulWidget {
|
class TextLayer extends StatefulWidget {
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/background_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/background.layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/draw_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/draw.layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/emoji_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/emoji.layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/text_layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/text.layer.dart';
|
||||||
|
|
||||||
/// View stacked layers (unbounded height, width)
|
/// View stacked layers (unbounded height, width)
|
||||||
class LayersViewer extends StatelessWidget {
|
class LayersViewer extends StatelessWidget {
|
||||||
|
|
@ -37,7 +37,9 @@ class LayersViewer extends StatelessWidget {
|
||||||
...layers
|
...layers
|
||||||
.where(
|
.where(
|
||||||
(layerItem) =>
|
(layerItem) =>
|
||||||
layerItem is EmojiLayerData || layerItem is DrawLayerData,
|
layerItem is EmojiLayerData ||
|
||||||
|
layerItem is DrawLayerData ||
|
||||||
|
layerItem is TextLayerData,
|
||||||
)
|
)
|
||||||
.map((layerItem) {
|
.map((layerItem) {
|
||||||
if (layerItem is EmojiLayerData) {
|
if (layerItem is EmojiLayerData) {
|
||||||
|
|
@ -52,15 +54,14 @@ class LayersViewer extends StatelessWidget {
|
||||||
layerData: layerItem,
|
layerData: layerItem,
|
||||||
onUpdate: onUpdate,
|
onUpdate: onUpdate,
|
||||||
);
|
);
|
||||||
}
|
} else if (layerItem is TextLayerData) {
|
||||||
return Container();
|
|
||||||
}),
|
|
||||||
...layers.whereType<TextLayerData>().map((layerItem) {
|
|
||||||
return TextLayer(
|
return TextLayer(
|
||||||
key: layerItem.key,
|
key: layerItem.key,
|
||||||
layerData: layerItem,
|
layerData: layerItem,
|
||||||
onUpdate: onUpdate,
|
onUpdate: onUpdate,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
return Container();
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
@ -9,7 +9,7 @@ import 'package:twonly/src/database/tables/messages.table.dart';
|
||||||
import 'package:twonly/src/database/twonly.db.dart';
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart';
|
import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart';
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dar
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
import 'package:twonly/src/views/components/emoji_picker.bottom.dart';
|
||||||
import 'package:twonly/src/views/chats/message_info.view.dart';
|
import 'package:twonly/src/views/chats/message_info.view.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/context_menu.component.dart';
|
import 'package:twonly/src/views/components/context_menu.component.dart';
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import 'package:twonly/src/database/twonly.db.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/services/api/messages.dart';
|
import 'package:twonly/src/services/api/messages.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
||||||
|
|
||||||
class MessageInput extends StatefulWidget {
|
class MessageInput extends StatefulWidget {
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
||||||
case MessageSendState.receivedOpened:
|
case MessageSendState.receivedOpened:
|
||||||
icon = Icon(Icons.crop_square, size: 14, color: color);
|
icon = Icon(Icons.crop_square, size: 14, color: color);
|
||||||
if (message.content != null) {
|
if (message.content != null) {
|
||||||
if (isEmoji(message.content!)) {
|
if (isOneEmoji(message.content!)) {
|
||||||
icon = Text(
|
icon = Text(
|
||||||
message.content!,
|
message.content!,
|
||||||
style: const TextStyle(fontSize: 12),
|
style: const TextStyle(fontSize: 12),
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||||
import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart';
|
import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
import 'package:twonly/src/views/components/loader.dart';
|
import 'package:twonly/src/views/components/loader.dart';
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import 'package:flutter/scheduler.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
import 'package:twonly/src/views/components/emoji_picker.bottom.dart';
|
||||||
import 'package:twonly/src/views/chats/media_viewer_components/emoji_reactions_row.component.dart';
|
import 'package:twonly/src/views/chats/media_viewer_components/emoji_reactions_row.component.dart';
|
||||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@ import 'dart:io';
|
||||||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||||
|
|
||||||
class EmojiPickerBottom extends StatelessWidget {
|
class EmojiPickerBottom extends StatelessWidget {
|
||||||
const EmojiPickerBottom({super.key});
|
const EmojiPickerBottom({super.key});
|
||||||
|
|
@ -14,7 +14,7 @@ import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
||||||
import 'package:twonly/src/views/memories/memories.view.dart';
|
import 'package:twonly/src/views/memories/memories.view.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
||||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||||
import 'package:twonly/src/views/components/video_player_wrapper.dart';
|
import 'package:twonly/src/views/components/video_player_wrapper.dart';
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/location_filter.dart';
|
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/location_filter.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class UrlListTitle extends StatelessWidget {
|
class UrlListTitle extends StatelessWidget {
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ import 'package:twonly/src/views/components/animate_icon.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group('testing utils', () {
|
group('testing utils', () {
|
||||||
test('test isEmoji function', () {
|
test('test isEmoji function', () {
|
||||||
expect(isEmoji('Hallo'), false);
|
expect(isOneEmoji('Hallo'), false);
|
||||||
expect(isEmoji('😂'), true);
|
expect(isOneEmoji('😂'), true);
|
||||||
expect(isEmoji('😂😂'), false);
|
expect(isOneEmoji('😂😂'), false);
|
||||||
expect(isEmoji('Hallo 😂'), false);
|
expect(isOneEmoji('Hallo 😂'), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test proof-of-work simple', () async {
|
test('test proof-of-work simple', () async {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue