Set app version to 1.0.0+3; share sheet and markdown viewer polish; remove demo screenshot tooling
Build App Bundle / build (push) Successful in 5m22s

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
2026-10-01 14:50:07 -04:00
co-authored by Claude Code
parent 8aa7015c3f
commit 9c9e57ec22
30 changed files with 341 additions and 1230 deletions
+82 -19
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
@@ -71,11 +72,13 @@ class ShareSheet extends StatefulWidget {
return showNooSheet(
context,
children: [
NooOverlayHeader(
leading: detailsFileTile(item),
title: item.name,
subtitle: detailsMetaLine(item),
onClose: () => Navigator.pop(context),
Builder(
builder: (sheetContext) => NooOverlayHeader(
leading: detailsFileTile(item),
title: item.name,
subtitle: detailsMetaLine(item),
onClose: () => Navigator.of(sheetContext).pop(),
),
),
ShareSheet(item: item),
],
@@ -99,9 +102,36 @@ class _ShareSheetState extends State<ShareSheet> {
bool _showInherited = false;
final _peopleController = TextEditingController();
final _linkController = TextEditingController();
final _peopleFocus = FocusNode();
final _peopleKey = GlobalKey();
/// Once the user types, scrolls the sheet so the people field and its
/// results (under their title) sit near the top, leaving a little room under the sheet's
/// rounded top edge.
void _revealPeopleSection() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final ctx = _peopleKey.currentContext;
final box = ctx?.findRenderObject();
if (ctx == null || box is! RenderBox) return;
final position = Scrollable.maybeOf(ctx)?.position;
final viewport = RenderAbstractViewport.maybeOf(box);
if (position == null || viewport == null) return;
final target = (viewport.getOffsetToReveal(box, 0).offset - 8).clamp(
position.minScrollExtent,
position.maxScrollExtent,
);
position.animateTo(
target,
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
);
});
}
@override
void dispose() {
_peopleFocus.dispose();
_peopleController.dispose();
_linkController.dispose();
super.dispose();
@@ -128,6 +158,7 @@ class _ShareSheetState extends State<ShareSheet> {
return;
}
setState(() => _isSearching = true);
_revealPeopleSection();
final ops = context.read<ItemOperations>();
final results = await ops.searchSharees(trimmed);
if (!mounted) return;
@@ -135,6 +166,7 @@ class _ShareSheetState extends State<ShareSheet> {
_searchResults = results;
_isSearching = false;
});
_revealPeopleSection();
}
/// The single "Name, email or group" field: reuses whichever match is
@@ -364,6 +396,7 @@ class _ShareSheetState extends State<ShareSheet> {
absorbing: _isAddingPerson,
child: NooTextField(
controller: _peopleController,
focusNode: _peopleFocus,
leadingIcon: LucideIcons.search,
placeholder: 'Name, email or group',
onChanged: _search,
@@ -381,17 +414,34 @@ class _ShareSheetState extends State<ShareSheet> {
),
),
for (final sharee in _searchResults)
NooSettingsRow(
icon: sharee.type == ShareeType.group
? LucideIcons.users
: LucideIcons.user,
label: Text(sharee.label),
subtitle: sharee.subtitle != null ? Text(sharee.subtitle!) : null,
InkWell(
borderRadius: BorderRadius.circular(NooRadii.input),
onTap: () => _addSharee(sharee),
child: NooPersonAccessRow(
avatar: NooAvatar(
initials: _initial(sharee.label),
color: NooPhotoTile.paletteColor(sharee.label.hashCode),
icon: sharee.type == ShareeType.group
? LucideIcons.users
: null,
),
name: sharee.label,
subtitle: sharee.subtitle,
trailing: const SizedBox.shrink(),
),
),
const SizedBox(height: NooSpace.xs),
if (_searchResults.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: NooSpace.xs),
child: Divider(height: 1, thickness: 1, color: colors.line),
)
else
const SizedBox(height: NooSpace.xs),
NooPersonAccessRow(
avatar: NooAvatar(initials: _initial(session.username), current: true),
avatar: NooAvatar(
initials: _initial(session.username),
current: true,
),
name: session.username,
owner: true,
),
@@ -402,12 +452,16 @@ class _ShareSheetState extends State<ShareSheet> {
color: NooPhotoTile.paletteColor(
(share.sharedWithDisplayName ?? share.id).hashCode,
),
icon: share.shareType == ShareType.group ? LucideIcons.users : null,
icon: share.shareType == ShareType.group
? LucideIcons.users
: null,
),
name: share.sharedWithDisplayName ?? 'Shared',
subtitle: share.shareType == ShareType.group
? 'Group'
: (share.shareType == ShareType.email ? 'Invited by email' : null),
: (share.shareType == ShareType.email
? 'Invited by email'
: null),
permission: (share.permissions & 2) != 0 ? 'Can edit' : 'Can view',
onPermissionTap: () => _openPersonMenu(share),
),
@@ -429,7 +483,9 @@ class _ShareSheetState extends State<ShareSheet> {
),
),
Icon(
_showInherited ? LucideIcons.chevronUp : LucideIcons.chevronDown,
_showInherited
? LucideIcons.chevronUp
: LucideIcons.chevronDown,
size: 16,
color: colors.fg3,
),
@@ -464,7 +520,10 @@ class _ShareSheetState extends State<ShareSheet> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('Internal link', style: NooText.bodyL.copyWith(color: colors.fg1)),
Text(
'Internal link',
style: NooText.bodyL.copyWith(color: colors.fg1),
),
Text(
'For people who already have access',
style: NooText.meta.copyWith(color: colors.fg3),
@@ -559,6 +618,7 @@ class _ShareSheetState extends State<ShareSheet> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
NooShareSection(
key: _peopleKey,
title: 'Share with people',
child: _peopleSection(colors, session, peopleShares, internalLink),
),
@@ -572,13 +632,16 @@ class _ShareSheetState extends State<ShareSheet> {
caption: hasLink
? null
: 'Anyone with the link can access this ${widget.item.isFolder ? 'folder' : 'file'}.',
child: hasLink ? _linkSection(publicLinkShares.first) : const SizedBox.shrink(),
child: hasLink
? _linkSection(publicLinkShares.first)
: const SizedBox.shrink(),
),
if (!widget.item.isFolder) ...[
const SizedBox(height: 22),
NooShareSection(
title: 'Send file directly',
caption: "Link settings above don't apply - this sends the file's bytes directly.",
caption:
"Link settings above don't apply - this sends the file's bytes directly.",
child: NooButton(
variant: NooButtonVariant.outline,
size: NooButtonSize.card,