Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6a7256df3 | ||
|
|
ae1ae8ffa4 | ||
|
|
1421223953 | ||
|
|
b7de448a83 | ||
|
|
27de791a04 | ||
|
|
5abdcdf19d | ||
|
|
27b5c2bed8 | ||
|
|
9c5933d984 | ||
|
|
46f1a7fb2a |
@@ -20,4 +20,8 @@ abstract class MailboxRepository {
|
||||
String name,
|
||||
String role,
|
||||
);
|
||||
|
||||
/// Creates a new mailbox named [name] for [accountId] without a special role.
|
||||
/// Returns the newly created [Mailbox].
|
||||
Future<Mailbox> createMailbox(String accountId, String name);
|
||||
}
|
||||
|
||||
@@ -343,11 +343,23 @@ class MailboxRepositoryImpl implements MailboxRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<model.Mailbox> createMailbox(String accountId, String name) async {
|
||||
final account = (await _accounts.getAccount(accountId))!;
|
||||
final password = await _accounts.getPassword(accountId);
|
||||
switch (account.type) {
|
||||
case account_model.AccountType.imap:
|
||||
return _createMailboxWithRoleImap(account, password, name, null);
|
||||
case account_model.AccountType.jmap:
|
||||
return _createMailboxWithRoleJmap(account, password, name, null);
|
||||
}
|
||||
}
|
||||
|
||||
Future<model.Mailbox> _createMailboxWithRoleImap(
|
||||
account_model.Account account,
|
||||
String password,
|
||||
String name,
|
||||
String role,
|
||||
String? role,
|
||||
) async {
|
||||
final client = await _imapConnect(
|
||||
account,
|
||||
@@ -380,7 +392,7 @@ class MailboxRepositoryImpl implements MailboxRepository {
|
||||
account_model.Account account,
|
||||
String password,
|
||||
String name,
|
||||
String role,
|
||||
String? role,
|
||||
) async {
|
||||
final jmapUrl = account.jmapUrl;
|
||||
if (jmapUrl == null || jmapUrl.isEmpty) {
|
||||
@@ -398,7 +410,10 @@ class MailboxRepositoryImpl implements MailboxRepository {
|
||||
{
|
||||
'accountId': jmap.accountId,
|
||||
'create': {
|
||||
'new-mailbox': {'name': name, 'role': role},
|
||||
'new-mailbox': {
|
||||
'name': name,
|
||||
if (role != null) 'role': role,
|
||||
},
|
||||
},
|
||||
},
|
||||
'0',
|
||||
|
||||
@@ -563,6 +563,42 @@ class _EmailDetailScreenState extends ConsumerState<EmailDetailScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<String?> _promptNewFolderName(BuildContext context) async {
|
||||
final controller = TextEditingController();
|
||||
try {
|
||||
return await showDialog<String>(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Create new folder'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(hintText: 'Folder name'),
|
||||
textCapitalization: TextCapitalization.words,
|
||||
onSubmitted: (value) {
|
||||
if (value.trim().isNotEmpty) Navigator.pop(ctx, value.trim());
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
final name = controller.text.trim();
|
||||
if (name.isNotEmpty) Navigator.pop(ctx, name);
|
||||
},
|
||||
child: const Text('Create'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} finally {
|
||||
controller.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _moveTo(BuildContext context, Email header) async {
|
||||
final nextEmailId = await _getNextEmailIdIfNeeded(header);
|
||||
|
||||
@@ -576,6 +612,8 @@ class _EmailDetailScreenState extends ConsumerState<EmailDetailScreen> {
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
const createNewSentinel = '__create_new__';
|
||||
|
||||
final chosen = await showModalBottomSheet<String>(
|
||||
context: context,
|
||||
builder: (ctx) => ListView(
|
||||
@@ -593,13 +631,28 @@ class _EmailDetailScreenState extends ConsumerState<EmailDetailScreen> {
|
||||
title: Text(m.name),
|
||||
onTap: () => Navigator.pop(ctx, m.path),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.create_new_folder_outlined),
|
||||
title: const Text('Create new folder…'),
|
||||
onTap: () => Navigator.pop(ctx, createNewSentinel),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (chosen == null || !context.mounted) return;
|
||||
|
||||
await ref.read(emailRepositoryProvider).moveEmail(widget.emailId, chosen);
|
||||
String destination = chosen;
|
||||
if (chosen == createNewSentinel) {
|
||||
final name = await _promptNewFolderName(context);
|
||||
if (name == null || !context.mounted) return;
|
||||
final mailbox = await mailboxRepo.createMailbox(header.accountId, name);
|
||||
destination = mailbox.path;
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(emailRepositoryProvider)
|
||||
.moveEmail(widget.emailId, destination);
|
||||
|
||||
unawaited(
|
||||
ref.read(undoServiceProvider.notifier).pushAction(
|
||||
@@ -609,7 +662,7 @@ class _EmailDetailScreenState extends ConsumerState<EmailDetailScreen> {
|
||||
type: UndoType.move,
|
||||
emailIds: [widget.emailId],
|
||||
sourceMailboxPath: header.mailboxPath,
|
||||
destinationMailboxPath: chosen,
|
||||
destinationMailboxPath: destination,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -169,6 +169,15 @@ class _FakeMailboxes implements MailboxRepository {
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
@override
|
||||
Future<Mailbox> createMailbox(String accountId, String name) async => Mailbox(
|
||||
id: '$accountId:$name',
|
||||
accountId: accountId,
|
||||
path: name,
|
||||
name: name,
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeEmails implements EmailRepository {
|
||||
|
||||
@@ -239,6 +239,15 @@ class FakeMailboxRepositoryWithInbox implements MailboxRepository {
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
@override
|
||||
Future<Mailbox> createMailbox(String accountId, String name) async => Mailbox(
|
||||
id: '$accountId:$name',
|
||||
accountId: accountId,
|
||||
path: name,
|
||||
name: name,
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
}
|
||||
|
||||
class _AccountRepositoryWithMissingPlugin implements AccountRepository {
|
||||
|
||||
@@ -235,6 +235,31 @@ class MockMailboxRepository extends _i1.Mock implements _i8.MailboxRepository {
|
||||
),
|
||||
)),
|
||||
) as _i5.Future<_i2.Mailbox>);
|
||||
|
||||
@override
|
||||
_i5.Future<_i2.Mailbox> createMailbox(
|
||||
String? accountId,
|
||||
String? name,
|
||||
) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#createMailbox,
|
||||
[
|
||||
accountId,
|
||||
name,
|
||||
],
|
||||
),
|
||||
returnValue: _i5.Future<_i2.Mailbox>.value(_FakeMailbox_0(
|
||||
this,
|
||||
Invocation.method(
|
||||
#createMailbox,
|
||||
[
|
||||
accountId,
|
||||
name,
|
||||
],
|
||||
),
|
||||
)),
|
||||
) as _i5.Future<_i2.Mailbox>);
|
||||
}
|
||||
|
||||
/// A class which mocks [EmailRepository].
|
||||
|
||||
@@ -77,6 +77,15 @@ class _FakeMailboxes implements MailboxRepository {
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
@override
|
||||
Future<Mailbox> createMailbox(String accountId, String name) async => Mailbox(
|
||||
id: '$accountId:$name',
|
||||
accountId: accountId,
|
||||
path: name,
|
||||
name: name,
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeEmails implements EmailRepository {
|
||||
|
||||
@@ -67,6 +67,15 @@ class _FakeMailboxes implements MailboxRepository {
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
@override
|
||||
Future<Mailbox> createMailbox(String accountId, String name) async => Mailbox(
|
||||
id: '$accountId:$name',
|
||||
accountId: accountId,
|
||||
path: name,
|
||||
name: name,
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
}
|
||||
|
||||
class _CountingEmails implements EmailRepository {
|
||||
|
||||
@@ -192,6 +192,20 @@ class FakeMailboxRepository implements MailboxRepository {
|
||||
_mailboxes.add(mailbox);
|
||||
return mailbox;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Mailbox> createMailbox(String accountId, String name) async {
|
||||
final mailbox = Mailbox(
|
||||
id: '$accountId:$name',
|
||||
accountId: accountId,
|
||||
path: name,
|
||||
name: name,
|
||||
unreadCount: 0,
|
||||
totalCount: 0,
|
||||
);
|
||||
_mailboxes.add(mailbox);
|
||||
return mailbox;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeEmailRepository implements EmailRepository {
|
||||
|
||||
Reference in New Issue
Block a user