mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: page style issues (#5317)
* fix: 7 emojis per line * fix: remove shadow of the icons using presets cover * fix: expand appbar buttons size * fix: save new title name when it changed * feat: add cover image preview * fix: dismiss unsplash and presets panel auto * feat: add selected color for cover image and layout section * fix: selected icon size too small * fix: dismiss page style panel before pushing to emoji and font selector * chore: update back button icon * chore: bump version 0.5.7
This commit is contained in:
18
CHANGELOG.md
18
CHANGELOG.md
@ -1,4 +1,22 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
|
## Version 0.5.7 - 05/10/2024
|
||||||
|
### Bug Fixes
|
||||||
|
- Resolved page opening issue on Android.
|
||||||
|
- Fixed text input inconsistency on Kanban board cards.
|
||||||
|
|
||||||
|
## Version 0.5.6 - 05/07/2024
|
||||||
|
### New Features
|
||||||
|
- Team collaboration is live! Add members to your workspace to edit and collaborate on pages together.
|
||||||
|
- Collaborate in real time on the same page with other members. Edits made by others will appear instantly.
|
||||||
|
- Create multiple workspaces for different kinds of content.
|
||||||
|
- Customize your entire page on mobile through the Page Style menu with options for layout, font, font size, emoji, and cover image.
|
||||||
|
- Open a row record as a full page.
|
||||||
|
### Bug Fixes
|
||||||
|
- Resolved issue with setting background color for the Simple Table block.
|
||||||
|
- Adjusted toolbar for various screen sizes.
|
||||||
|
- Added a request for photo permission before uploading images on mobile.
|
||||||
|
- Exported creation and last modification timestamps to CSV.
|
||||||
|
|
||||||
## Version 0.5.5 - 04/24/2024
|
## Version 0.5.5 - 04/24/2024
|
||||||
### New Features
|
### New Features
|
||||||
- Improved the display of code blocks with line numbers
|
- Improved the display of code blocks with line numbers
|
||||||
|
@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
|||||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||||
LIB_NAME = "dart_ffi"
|
LIB_NAME = "dart_ffi"
|
||||||
APPFLOWY_VERSION = "0.5.6"
|
APPFLOWY_VERSION = "0.5.7"
|
||||||
FLUTTER_DESKTOP_FEATURES = "dart"
|
FLUTTER_DESKTOP_FEATURES = "dart"
|
||||||
PRODUCT_NAME = "AppFlowy"
|
PRODUCT_NAME = "AppFlowy"
|
||||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||||
|
import 'package:appflowy/user/application/user_service.dart';
|
||||||
import 'package:appflowy/workspace/application/view/prelude.dart';
|
import 'package:appflowy/workspace/application/view/prelude.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:appflowy_result/appflowy_result.dart';
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
@ -21,6 +23,9 @@ class MobileViewPageBloc
|
|||||||
initial: () async {
|
initial: () async {
|
||||||
_registerListeners();
|
_registerListeners();
|
||||||
|
|
||||||
|
final userProfilePB =
|
||||||
|
await UserBackendService.getCurrentUserProfile()
|
||||||
|
.fold((s) => s, (f) => null);
|
||||||
final result = await ViewBackendService.getView(viewId);
|
final result = await ViewBackendService.getView(viewId);
|
||||||
final isImmersiveMode =
|
final isImmersiveMode =
|
||||||
_isImmersiveMode(result.fold((s) => s, (f) => null));
|
_isImmersiveMode(result.fold((s) => s, (f) => null));
|
||||||
@ -29,6 +34,7 @@ class MobileViewPageBloc
|
|||||||
isLoading: false,
|
isLoading: false,
|
||||||
result: result,
|
result: result,
|
||||||
isImmersiveMode: isImmersiveMode,
|
isImmersiveMode: isImmersiveMode,
|
||||||
|
userProfilePB: userProfilePB,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -71,7 +77,7 @@ class MobileViewPageBloc
|
|||||||
final cover = view.cover;
|
final cover = view.cover;
|
||||||
if (cover == null || cover.type == PageStyleCoverImageType.none) {
|
if (cover == null || cover.type == PageStyleCoverImageType.none) {
|
||||||
return false;
|
return false;
|
||||||
} else if (view.layout == ViewLayoutPB.Document) {
|
} else if (view.layout == ViewLayoutPB.Document && !cover.isPresets) {
|
||||||
// only support immersive mode for document layout
|
// only support immersive mode for document layout
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -93,6 +99,7 @@ class MobileViewPageState with _$MobileViewPageState {
|
|||||||
@Default(true) bool isLoading,
|
@Default(true) bool isLoading,
|
||||||
@Default(null) FlowyResult<ViewPB, FlowyError>? result,
|
@Default(null) FlowyResult<ViewPB, FlowyError>? result,
|
||||||
@Default(false) bool isImmersiveMode,
|
@Default(false) bool isImmersiveMode,
|
||||||
|
@Default(null) UserProfilePB? userProfilePB,
|
||||||
}) = _MobileViewPageState;
|
}) = _MobileViewPageState;
|
||||||
|
|
||||||
factory MobileViewPageState.initial() => const MobileViewPageState();
|
factory MobileViewPageState.initial() => const MobileViewPageState();
|
||||||
|
@ -37,6 +37,7 @@ class FlowyAppBar extends AppBar {
|
|||||||
Widget? title,
|
Widget? title,
|
||||||
String? titleText,
|
String? titleText,
|
||||||
FlowyAppBarLeadingType leadingType = FlowyAppBarLeadingType.back,
|
FlowyAppBarLeadingType leadingType = FlowyAppBarLeadingType.back,
|
||||||
|
double? leadingWidth,
|
||||||
Widget? leading,
|
Widget? leading,
|
||||||
super.centerTitle,
|
super.centerTitle,
|
||||||
VoidCallback? onTapLeading,
|
VoidCallback? onTapLeading,
|
||||||
@ -52,7 +53,7 @@ class FlowyAppBar extends AppBar {
|
|||||||
titleSpacing: 0,
|
titleSpacing: 0,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
leading: leading ?? leadingType.getWidget(onTapLeading),
|
leading: leading ?? leadingType.getWidget(onTapLeading),
|
||||||
leadingWidth: leadingType.width,
|
leadingWidth: leadingWidth ?? leadingType.width,
|
||||||
toolbarHeight: 44.0,
|
toolbarHeight: 44.0,
|
||||||
bottom: showDivider
|
bottom: showDivider
|
||||||
? const PreferredSize(
|
? const PreferredSize(
|
||||||
|
@ -43,8 +43,9 @@ class MobileViewPageImmersiveAppBar extends StatelessWidget
|
|||||||
AppBarTheme.of(context).backgroundColor?.withOpacity(opacity),
|
AppBarTheme.of(context).backgroundColor?.withOpacity(opacity),
|
||||||
showDivider: false,
|
showDivider: false,
|
||||||
title: Opacity(opacity: opacity >= 0.99 ? 1.0 : 0, child: title),
|
title: Opacity(opacity: opacity >= 0.99 ? 1.0 : 0, child: title),
|
||||||
|
leadingWidth: 44,
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 2.0, vertical: 4.0),
|
padding: const EdgeInsets.only(top: 4.0, bottom: 4.0, left: 12.0),
|
||||||
child: _buildAppBarBackButton(context),
|
child: _buildAppBarBackButton(context),
|
||||||
),
|
),
|
||||||
actions: actions,
|
actions: actions,
|
||||||
@ -59,7 +60,7 @@ class MobileViewPageImmersiveAppBar extends StatelessWidget
|
|||||||
child: _ImmersiveAppBarButton(
|
child: _ImmersiveAppBarButton(
|
||||||
icon: FlowySvgs.m_app_bar_back_s,
|
icon: FlowySvgs.m_app_bar_back_s,
|
||||||
dimension: 30.0,
|
dimension: 30.0,
|
||||||
iconPadding: 6.0,
|
iconPadding: 3.0,
|
||||||
isImmersiveMode:
|
isImmersiveMode:
|
||||||
context.read<MobileViewPageBloc>().state.isImmersiveMode,
|
context.read<MobileViewPageBloc>().state.isImmersiveMode,
|
||||||
appBarOpacity: appBarOpacity,
|
appBarOpacity: appBarOpacity,
|
||||||
@ -104,7 +105,7 @@ class MobileViewPageMoreButton extends StatelessWidget {
|
|||||||
child: _ImmersiveAppBarButton(
|
child: _ImmersiveAppBarButton(
|
||||||
icon: FlowySvgs.m_app_bar_more_s,
|
icon: FlowySvgs.m_app_bar_more_s,
|
||||||
dimension: 30.0,
|
dimension: 30.0,
|
||||||
iconPadding: 5.0,
|
iconPadding: 3.0,
|
||||||
isImmersiveMode: isImmersiveMode,
|
isImmersiveMode: isImmersiveMode,
|
||||||
appBarOpacity: appBarOpacity,
|
appBarOpacity: appBarOpacity,
|
||||||
),
|
),
|
||||||
@ -144,8 +145,11 @@ class MobileViewPageLayoutButton extends StatelessWidget {
|
|||||||
showHeader: true,
|
showHeader: true,
|
||||||
title: LocaleKeys.pageStyle_title.tr(),
|
title: LocaleKeys.pageStyle_title.tr(),
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
builder: (_) => BlocProvider.value(
|
builder: (_) => MultiBlocProvider(
|
||||||
value: context.read<DocumentPageStyleBloc>(),
|
providers: [
|
||||||
|
BlocProvider.value(value: context.read<DocumentPageStyleBloc>()),
|
||||||
|
BlocProvider.value(value: context.read<MobileViewPageBloc>()),
|
||||||
|
],
|
||||||
child: PageStyleBottomSheet(
|
child: PageStyleBottomSheet(
|
||||||
view: context.read<ViewBloc>().state.view,
|
view: context.read<ViewBloc>().state.view,
|
||||||
),
|
),
|
||||||
@ -155,7 +159,7 @@ class MobileViewPageLayoutButton extends StatelessWidget {
|
|||||||
child: _ImmersiveAppBarButton(
|
child: _ImmersiveAppBarButton(
|
||||||
icon: FlowySvgs.m_layout_s,
|
icon: FlowySvgs.m_layout_s,
|
||||||
dimension: 30.0,
|
dimension: 30.0,
|
||||||
iconPadding: 5.0,
|
iconPadding: 3.0,
|
||||||
isImmersiveMode: isImmersiveMode,
|
isImmersiveMode: isImmersiveMode,
|
||||||
appBarOpacity: appBarOpacity,
|
appBarOpacity: appBarOpacity,
|
||||||
),
|
),
|
||||||
|
@ -149,14 +149,11 @@ class _DocumentImmersiveCoverState extends State<DocumentImmersiveCover> {
|
|||||||
fontSize: 28.0,
|
fontSize: 28.0,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
fontFamily: fontFamily,
|
fontFamily: fontFamily,
|
||||||
color: state.cover.type == PageStyleCoverImageType.none
|
color:
|
||||||
? null
|
state.cover.isNone || state.cover.isPresets ? null : Colors.white,
|
||||||
: Colors.white,
|
|
||||||
),
|
),
|
||||||
onSubmitted: (value) {
|
onChanged: _rename,
|
||||||
scrollController.position.jumpTo(0);
|
onSubmitted: _rename,
|
||||||
context.read<ViewBloc>().add(ViewEvent.rename(value));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,4 +247,9 @@ class _DocumentImmersiveCoverState extends State<DocumentImmersiveCover> {
|
|||||||
focusNode.unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
|
focusNode.unfocus(disposition: UnfocusDisposition.previouslyFocusedChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _rename(String name) {
|
||||||
|
scrollController.position.jumpTo(0);
|
||||||
|
context.read<ViewBloc>().add(ViewEvent.rename(name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,12 +131,16 @@ class _UnsplashImages extends StatelessWidget {
|
|||||||
};
|
};
|
||||||
final mainAxisSpacing = switch (type) {
|
final mainAxisSpacing = switch (type) {
|
||||||
UnsplashImageType.halfScreen => 16.0,
|
UnsplashImageType.halfScreen => 16.0,
|
||||||
UnsplashImageType.fullScreen => 8.0,
|
UnsplashImageType.fullScreen => 16.0,
|
||||||
|
};
|
||||||
|
final crossAxisSpacing = switch (type) {
|
||||||
|
UnsplashImageType.halfScreen => 10.0,
|
||||||
|
UnsplashImageType.fullScreen => 16.0,
|
||||||
};
|
};
|
||||||
return GridView.count(
|
return GridView.count(
|
||||||
crossAxisCount: crossAxisCount,
|
crossAxisCount: crossAxisCount,
|
||||||
mainAxisSpacing: mainAxisSpacing,
|
mainAxisSpacing: mainAxisSpacing,
|
||||||
crossAxisSpacing: 10.0,
|
crossAxisSpacing: crossAxisSpacing,
|
||||||
childAspectRatio: 4 / 3,
|
childAspectRatio: 4 / 3,
|
||||||
children: photos
|
children: photos
|
||||||
.map(
|
.map(
|
||||||
@ -197,28 +201,31 @@ class _UnsplashImage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFullScreenImage(BuildContext context) {
|
Widget _buildFullScreenImage(BuildContext context) {
|
||||||
return Stack(
|
return ClipRRect(
|
||||||
children: [
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
LayoutBuilder(
|
child: Stack(
|
||||||
builder: (context, constraints) {
|
children: [
|
||||||
return Image.network(
|
LayoutBuilder(
|
||||||
photo.urls.thumb.toString(),
|
builder: (context, constraints) {
|
||||||
fit: BoxFit.cover,
|
return Image.network(
|
||||||
width: constraints.maxWidth,
|
photo.urls.thumb.toString(),
|
||||||
height: constraints.maxHeight,
|
fit: BoxFit.cover,
|
||||||
);
|
width: constraints.maxWidth,
|
||||||
},
|
height: constraints.maxHeight,
|
||||||
),
|
);
|
||||||
Positioned(
|
},
|
||||||
bottom: 6,
|
|
||||||
left: 6,
|
|
||||||
child: FlowyText.medium(
|
|
||||||
photo.name,
|
|
||||||
fontSize: 10.0,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
),
|
Positioned(
|
||||||
],
|
bottom: 9,
|
||||||
|
left: 10,
|
||||||
|
child: FlowyText.medium(
|
||||||
|
photo.name,
|
||||||
|
fontSize: 13.0,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/mobile/application/base/mobile_view_page_bloc.dart';
|
||||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_cover_bottom_sheet.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_cover_bottom_sheet.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_style_util.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_style_util.dart';
|
||||||
|
import 'package:appflowy/shared/appflowy_network_image.dart';
|
||||||
import 'package:appflowy/shared/feedback_gesture_detector.dart';
|
import 'package:appflowy/shared/feedback_gesture_detector.dart';
|
||||||
|
import 'package:appflowy/shared/flowy_gradient_colors.dart';
|
||||||
import 'package:appflowy/shared/permission/permission_checker.dart';
|
import 'package:appflowy/shared/permission/permission_checker.dart';
|
||||||
import 'package:appflowy/user/application/user_service.dart';
|
import 'package:appflowy/user/application/user_service.dart';
|
||||||
|
import 'package:appflowy/util/string_extension.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:appflowy_result/appflowy_result.dart';
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
@ -19,6 +24,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|||||||
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
|
import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
class PageStyleCoverImage extends StatelessWidget {
|
class PageStyleCoverImage extends StatelessWidget {
|
||||||
@ -33,13 +39,11 @@ class PageStyleCoverImage extends StatelessWidget {
|
|||||||
final backgroundColor = context.pageStyleBackgroundColor;
|
final backgroundColor = context.pageStyleBackgroundColor;
|
||||||
return BlocBuilder<DocumentPageStyleBloc, DocumentPageStyleState>(
|
return BlocBuilder<DocumentPageStyleBloc, DocumentPageStyleState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Row(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
_buildOptionGroup(
|
_buildOptionGroup(context, backgroundColor, state),
|
||||||
context,
|
const VSpace(16.0),
|
||||||
backgroundColor,
|
_buildPreview(context, state),
|
||||||
state,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -51,46 +55,124 @@ class PageStyleCoverImage extends StatelessWidget {
|
|||||||
Color backgroundColor,
|
Color backgroundColor,
|
||||||
DocumentPageStyleState state,
|
DocumentPageStyleState state,
|
||||||
) {
|
) {
|
||||||
return Expanded(
|
return Container(
|
||||||
child: Container(
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: backgroundColor,
|
||||||
color: backgroundColor,
|
borderRadius: const BorderRadius.horizontal(
|
||||||
borderRadius: const BorderRadius.horizontal(
|
left: Radius.circular(12),
|
||||||
left: Radius.circular(12),
|
right: Radius.circular(12),
|
||||||
right: Radius.circular(12),
|
),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(4.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
_CoverOptionButton(
|
||||||
|
showLeftCorner: true,
|
||||||
|
showRightCorner: false,
|
||||||
|
selected: state.coverImage.isPresets,
|
||||||
|
onTap: () => _showPresets(context),
|
||||||
|
child: const _PresetCover(),
|
||||||
),
|
),
|
||||||
),
|
_CoverOptionButton(
|
||||||
padding: const EdgeInsets.all(4.0),
|
showLeftCorner: false,
|
||||||
child: Row(
|
showRightCorner: false,
|
||||||
children: [
|
selected: state.coverImage.isPhoto,
|
||||||
_CoverOptionButton(
|
onTap: () => _pickImage(context),
|
||||||
showLeftCorner: true,
|
child: const _PhotoCover(),
|
||||||
showRightCorner: false,
|
),
|
||||||
selected: state.coverImage.isPresets,
|
_CoverOptionButton(
|
||||||
onTap: () => _showPresets(context),
|
showLeftCorner: false,
|
||||||
child: const _PresetCover(),
|
showRightCorner: true,
|
||||||
),
|
selected: state.coverImage.isUnsplashImage,
|
||||||
_CoverOptionButton(
|
onTap: () => _showUnsplash(context),
|
||||||
showLeftCorner: false,
|
child: const _UnsplashCover(),
|
||||||
showRightCorner: false,
|
),
|
||||||
selected: state.coverImage.isPhoto,
|
],
|
||||||
onTap: () => _pickImage(context),
|
|
||||||
child: const _PhotoCover(),
|
|
||||||
),
|
|
||||||
_CoverOptionButton(
|
|
||||||
showLeftCorner: false,
|
|
||||||
showRightCorner: true,
|
|
||||||
selected: state.coverImage.isUnsplashImage,
|
|
||||||
onTap: () => _showUnsplash(context),
|
|
||||||
child: const _UnsplashCover(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildPreview(
|
||||||
|
BuildContext context,
|
||||||
|
DocumentPageStyleState state,
|
||||||
|
) {
|
||||||
|
final cover = state.coverImage;
|
||||||
|
if (cover.isNone) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final value = cover.value;
|
||||||
|
final type = cover.type;
|
||||||
|
|
||||||
|
Widget preview = const SizedBox.shrink();
|
||||||
|
|
||||||
|
if (type == PageStyleCoverImageType.customImage ||
|
||||||
|
type == PageStyleCoverImageType.unsplashImage) {
|
||||||
|
final userProfilePB =
|
||||||
|
context.read<MobileViewPageBloc>().state.userProfilePB;
|
||||||
|
preview = FlowyNetworkImage(
|
||||||
|
url: value,
|
||||||
|
userProfilePB: userProfilePB,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == PageStyleCoverImageType.builtInImage) {
|
||||||
|
preview = Image.asset(
|
||||||
|
PageStyleCoverImageType.builtInImagePath(value),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == PageStyleCoverImageType.pureColor) {
|
||||||
|
final color = value.coverColor(context);
|
||||||
|
if (color != null) {
|
||||||
|
preview = ColoredBox(
|
||||||
|
color: color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == PageStyleCoverImageType.gradientColor) {
|
||||||
|
preview = Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: FlowyGradientColor.fromId(value).linear,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == PageStyleCoverImageType.localImage) {
|
||||||
|
preview = Image.file(
|
||||||
|
File(value),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
FlowyText(LocaleKeys.pageStyle_image.tr()),
|
||||||
|
const Spacer(),
|
||||||
|
Container(
|
||||||
|
width: 40,
|
||||||
|
height: 28,
|
||||||
|
clipBehavior: Clip.hardEdge,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(6.0)),
|
||||||
|
border: Border.all(color: const Color(0x1F222533)),
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(5.0)),
|
||||||
|
child: preview,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _showPresets(BuildContext context) {
|
void _showPresets(BuildContext context) {
|
||||||
|
final pageStyleBloc = context.read<DocumentPageStyleBloc>();
|
||||||
|
|
||||||
|
context.pop();
|
||||||
|
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
@ -99,18 +181,18 @@ class PageStyleCoverImage extends StatelessWidget {
|
|||||||
showHeader: true,
|
showHeader: true,
|
||||||
showRemoveButton: true,
|
showRemoveButton: true,
|
||||||
onRemove: () {
|
onRemove: () {
|
||||||
context.read<DocumentPageStyleBloc>().add(
|
pageStyleBloc.add(
|
||||||
DocumentPageStyleEvent.updateCoverImage(
|
DocumentPageStyleEvent.updateCoverImage(
|
||||||
PageStyleCover.none(),
|
PageStyleCover.none(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
title: LocaleKeys.pageStyle_pageCover.tr(),
|
title: LocaleKeys.pageStyle_presets.tr(),
|
||||||
barrierColor: Colors.transparent,
|
barrierColor: Colors.transparent,
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: context.read<DocumentPageStyleBloc>(),
|
value: pageStyleBloc,
|
||||||
child: const PageCoverBottomSheet(),
|
child: const PageCoverBottomSheet(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -174,6 +256,9 @@ class PageStyleCoverImage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showUnsplash(BuildContext context) {
|
void _showUnsplash(BuildContext context) {
|
||||||
|
final pageStyleBloc = context.read<DocumentPageStyleBloc>();
|
||||||
|
context.pop();
|
||||||
|
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
@ -181,15 +266,15 @@ class PageStyleCoverImage extends StatelessWidget {
|
|||||||
showDoneButton: true,
|
showDoneButton: true,
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
showRemoveButton: true,
|
showRemoveButton: true,
|
||||||
title: LocaleKeys.pageStyle_coverImage.tr(),
|
title: LocaleKeys.pageStyle_unsplash.tr(),
|
||||||
barrierColor: Colors.transparent,
|
barrierColor: Colors.transparent,
|
||||||
backgroundColor: Theme.of(context).colorScheme.background,
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
onRemove: () {
|
onRemove: () {
|
||||||
context.read<DocumentPageStyleBloc>().add(
|
pageStyleBloc.add(
|
||||||
DocumentPageStyleEvent.updateCoverImage(
|
DocumentPageStyleEvent.updateCoverImage(
|
||||||
PageStyleCover.none(),
|
PageStyleCover.none(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
@ -204,14 +289,14 @@ class PageStyleCoverImage extends StatelessWidget {
|
|||||||
child: UnsplashImageWidget(
|
child: UnsplashImageWidget(
|
||||||
type: UnsplashImageType.fullScreen,
|
type: UnsplashImageType.fullScreen,
|
||||||
onSelectUnsplashImage: (url) {
|
onSelectUnsplashImage: (url) {
|
||||||
context.read<DocumentPageStyleBloc>().add(
|
pageStyleBloc.add(
|
||||||
DocumentPageStyleEvent.updateCoverImage(
|
DocumentPageStyleEvent.updateCoverImage(
|
||||||
PageStyleCover(
|
PageStyleCover(
|
||||||
type: PageStyleCoverImageType.unsplashImage,
|
type: PageStyleCoverImageType.unsplashImage,
|
||||||
value: url,
|
value: url,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -308,6 +393,7 @@ class _CoverOptionButton extends StatelessWidget {
|
|||||||
duration: Durations.medium1,
|
duration: Durations.medium1,
|
||||||
decoration: selected
|
decoration: selected
|
||||||
? ShapeDecoration(
|
? ShapeDecoration(
|
||||||
|
color: const Color(0x141AC3F2),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
side: const BorderSide(
|
side: const BorderSide(
|
||||||
width: 1.50,
|
width: 1.50,
|
||||||
|
@ -11,8 +11,9 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
class PageStyleIcon extends StatelessWidget {
|
class PageStyleIcon extends StatefulWidget {
|
||||||
const PageStyleIcon({
|
const PageStyleIcon({
|
||||||
super.key,
|
super.key,
|
||||||
required this.view,
|
required this.view,
|
||||||
@ -20,10 +21,15 @@ class PageStyleIcon extends StatelessWidget {
|
|||||||
|
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PageStyleIcon> createState() => _PageStyleIconState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PageStyleIconState extends State<PageStyleIcon> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => PageStyleIconBloc(view: view)
|
create: (_) => PageStyleIconBloc(view: widget.view)
|
||||||
..add(const PageStyleIconEvent.initial()),
|
..add(const PageStyleIconEvent.initial()),
|
||||||
child: BlocBuilder<PageStyleIconBloc, PageStyleIconState>(
|
child: BlocBuilder<PageStyleIconBloc, PageStyleIconState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
@ -60,6 +66,10 @@ class PageStyleIcon extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showIconSelector(BuildContext context, String selectedIcon) {
|
void _showIconSelector(BuildContext context, String selectedIcon) {
|
||||||
|
context.pop();
|
||||||
|
|
||||||
|
final pageStyleIconBloc = PageStyleIconBloc(view: widget.view)
|
||||||
|
..add(const PageStyleIconEvent.initial());
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
@ -75,13 +85,13 @@ class PageStyleIcon extends StatelessWidget {
|
|||||||
initialChildSize: 0.61,
|
initialChildSize: 0.61,
|
||||||
showRemoveButton: true,
|
showRemoveButton: true,
|
||||||
onRemove: () {
|
onRemove: () {
|
||||||
context.read<PageStyleIconBloc>().add(
|
pageStyleIconBloc.add(
|
||||||
const PageStyleIconEvent.updateIcon('', true),
|
const PageStyleIconEvent.updateIcon('', true),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
scrollableWidgetBuilder: (_, controller) {
|
scrollableWidgetBuilder: (_, controller) {
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: context.read<PageStyleIconBloc>(),
|
value: pageStyleIconBloc,
|
||||||
child: Expanded(
|
child: Expanded(
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
@ -112,6 +122,8 @@ class _IconSelectorState extends State<_IconSelector> {
|
|||||||
EmojiData? emojiData;
|
EmojiData? emojiData;
|
||||||
List<String> availableEmojis = [];
|
List<String> availableEmojis = [];
|
||||||
|
|
||||||
|
PageStyleIconBloc? pageStyleIconBloc;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -131,6 +143,14 @@ class _IconSelectorState extends State<_IconSelector> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pageStyleIconBloc = context.read<PageStyleIconBloc>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
pageStyleIconBloc?.close();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -146,7 +166,7 @@ class _IconSelectorState extends State<_IconSelector> {
|
|||||||
_buildSearchBar(context),
|
_buildSearchBar(context),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GridView.count(
|
child: GridView.count(
|
||||||
crossAxisCount: _getEmojiPerLine(context),
|
crossAxisCount: 7,
|
||||||
controller: widget.scrollController,
|
controller: widget.scrollController,
|
||||||
children: [
|
children: [
|
||||||
for (final emoji in availableEmojis)
|
for (final emoji in availableEmojis)
|
||||||
@ -165,27 +185,33 @@ class _IconSelectorState extends State<_IconSelector> {
|
|||||||
String emoji,
|
String emoji,
|
||||||
String? selectedEmoji,
|
String? selectedEmoji,
|
||||||
) {
|
) {
|
||||||
Widget child = Center(
|
Widget child = SizedBox.square(
|
||||||
child: FlowyText.emoji(
|
dimension: 24.0,
|
||||||
emoji,
|
child: Center(
|
||||||
fontSize: 24,
|
child: FlowyText.emoji(
|
||||||
|
emoji,
|
||||||
|
fontSize: 24,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (emoji == selectedEmoji) {
|
if (emoji == selectedEmoji) {
|
||||||
child = Container(
|
child = Center(
|
||||||
margin: const EdgeInsets.all(8.0),
|
child: Container(
|
||||||
decoration: ShapeDecoration(
|
width: 40,
|
||||||
shape: RoundedRectangleBorder(
|
height: 40,
|
||||||
side: const BorderSide(
|
decoration: ShapeDecoration(
|
||||||
width: 1.50,
|
shape: RoundedRectangleBorder(
|
||||||
strokeAlign: BorderSide.strokeAlignOutside,
|
side: const BorderSide(
|
||||||
color: Color(0xFF00BCF0),
|
width: 1.40,
|
||||||
|
strokeAlign: BorderSide.strokeAlignOutside,
|
||||||
|
color: Color(0xFF00BCF0),
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(9),
|
|
||||||
),
|
),
|
||||||
|
child: child,
|
||||||
),
|
),
|
||||||
child: child,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,11 +234,6 @@ class _IconSelectorState extends State<_IconSelector> {
|
|||||||
return availableEmojis;
|
return availableEmojis;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _getEmojiPerLine(BuildContext context) {
|
|
||||||
final width = MediaQuery.of(context).size.width;
|
|
||||||
return width ~/ 48.0; // the size of the emoji
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSearchBar(BuildContext context) {
|
Widget _buildSearchBar(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
|
@ -11,6 +11,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
const kPageStyleLayoutHeight = 52.0;
|
const kPageStyleLayoutHeight = 52.0;
|
||||||
|
|
||||||
@ -117,6 +118,7 @@ class _OptionGroup<T> extends StatelessWidget {
|
|||||||
duration: Durations.medium1,
|
duration: Durations.medium1,
|
||||||
decoration: selected
|
decoration: selected
|
||||||
? ShapeDecoration(
|
? ShapeDecoration(
|
||||||
|
color: const Color(0x141AC3F2),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
side: const BorderSide(
|
side: const BorderSide(
|
||||||
width: 1.50,
|
width: 1.50,
|
||||||
@ -180,7 +182,10 @@ class _FontButton extends StatelessWidget {
|
|||||||
const HSpace(16.0),
|
const HSpace(16.0),
|
||||||
FlowyText(LocaleKeys.titleBar_font.tr()),
|
FlowyText(LocaleKeys.titleBar_font.tr()),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
FlowyText(fontFamilyDisplayName),
|
FlowyText(
|
||||||
|
fontFamilyDisplayName,
|
||||||
|
color: context.pageStyleTextColor,
|
||||||
|
),
|
||||||
const HSpace(6.0),
|
const HSpace(6.0),
|
||||||
const FlowySvg(FlowySvgs.m_page_style_arrow_right_s),
|
const FlowySvg(FlowySvgs.m_page_style_arrow_right_s),
|
||||||
const HSpace(12.0),
|
const HSpace(12.0),
|
||||||
@ -193,6 +198,9 @@ class _FontButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showFontSelector(BuildContext context) {
|
void _showFontSelector(BuildContext context) {
|
||||||
|
final pageStyleBloc = context.read<DocumentPageStyleBloc>();
|
||||||
|
context.pop();
|
||||||
|
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
@ -208,7 +216,7 @@ class _FontButton extends StatelessWidget {
|
|||||||
initialChildSize: 0.61,
|
initialChildSize: 0.61,
|
||||||
scrollableWidgetBuilder: (_, controller) {
|
scrollableWidgetBuilder: (_, controller) {
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: context.read<DocumentPageStyleBloc>(),
|
value: pageStyleBloc,
|
||||||
child: BlocBuilder<DocumentPageStyleBloc, DocumentPageStyleState>(
|
child: BlocBuilder<DocumentPageStyleBloc, DocumentPageStyleState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
@ -219,11 +227,11 @@ class _FontButton extends StatelessWidget {
|
|||||||
selectedFontFamilyName:
|
selectedFontFamilyName:
|
||||||
state.fontFamily ?? defaultFontFamily,
|
state.fontFamily ?? defaultFontFamily,
|
||||||
onFontFamilySelected: (fontFamilyName) {
|
onFontFamilySelected: (fontFamilyName) {
|
||||||
context.read<DocumentPageStyleBloc>().add(
|
pageStyleBloc.add(
|
||||||
DocumentPageStyleEvent.updateFontFamily(
|
DocumentPageStyleEvent.updateFontFamily(
|
||||||
fontFamilyName,
|
fontFamilyName,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -25,7 +25,7 @@ class PageStyleBottomSheet extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
// cover image
|
// cover image
|
||||||
FlowyText(
|
FlowyText(
|
||||||
LocaleKeys.pageStyle_backgroundImage.tr(),
|
LocaleKeys.pageStyle_coverImage.tr(),
|
||||||
color: context.pageStyleTextColor,
|
color: context.pageStyleTextColor,
|
||||||
fontSize: 14.0,
|
fontSize: 14.0,
|
||||||
),
|
),
|
||||||
|
@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
|||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 0.5.6
|
version: 0.5.7
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
flutter: ">=3.19.0"
|
flutter: ">=3.19.0"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g id="icon_left_outlined">
|
<g opacity="0.8">
|
||||||
<path id="Union" d="M13.5774 1.9107C13.9028 2.23614 13.9028 2.76378 13.5774 3.08921L6.66667 9.99996L13.5774 16.9107C13.9028 17.2361 13.9028 17.7638 13.5774 18.0892C13.252 18.4147 12.7243 18.4147 12.3989 18.0892L5.48816 11.1785C4.83728 10.5276 4.83728 9.47232 5.48816 8.82145L12.3989 1.9107C12.7243 1.58527 13.252 1.58527 13.5774 1.9107Z" fill="#2B2F36"/>
|
<path d="M15.0796 19.9001L8.57604 13.3966C7.80799 12.6285 7.80799 11.3717 8.57604 10.6036L15.0796 4.1001" stroke="black" stroke-width="1.49621" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 491 B After Width: | Height: | Size: 342 B |
@ -1611,7 +1611,8 @@
|
|||||||
"photoPermissionDescription": "Allow access to the photo library for uploading images.",
|
"photoPermissionDescription": "Allow access to the photo library for uploading images.",
|
||||||
"openSettings": "Open Settings",
|
"openSettings": "Open Settings",
|
||||||
"photoPermissionTitle": "AppFlowy Would Like to Access Your Photo Library",
|
"photoPermissionTitle": "AppFlowy Would Like to Access Your Photo Library",
|
||||||
"doNotAllow": "Don't Allow"
|
"doNotAllow": "Don't Allow",
|
||||||
|
"image": "Image"
|
||||||
},
|
},
|
||||||
"commandPalette": {
|
"commandPalette": {
|
||||||
"placeholder": "Type to search for views...",
|
"placeholder": "Type to search for views...",
|
||||||
|
Reference in New Issue
Block a user