Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

PE-6085: Release ArDrive App v2.43.0 #1733

Merged
merged 22 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3cccb68
wip: search
thiagocarvalhodev Apr 29, 2024
5a6dd92
refactors and hidden item UI
thiagocarvalhodev May 1, 2024
f3d322c
Update license_assertion.dart
thiagocarvalhodev May 1, 2024
8bd8d15
fix hidden file UI and add unit test for search bloc
thiagocarvalhodev May 1, 2024
e2c9512
implement tests for repository
thiagocarvalhodev May 1, 2024
3054026
Update search_modal.dart
thiagocarvalhodev May 1, 2024
b6b9112
Update search_modal.dart
thiagocarvalhodev May 1, 2024
cd7c34a
fix scrollbar and file navigation
thiagocarvalhodev May 1, 2024
abde480
Update search_modal.dart
thiagocarvalhodev May 1, 2024
2067a00
Update search_modal.dart
thiagocarvalhodev May 1, 2024
bc5fac7
doesnt show the folder as subtitle if the folder is on the root path …
thiagocarvalhodev May 1, 2024
4959103
implements the clear search and fixes the navigatiion to the root folder
thiagocarvalhodev May 1, 2024
51fdbf6
share text controller on the app bar and on the modal
thiagocarvalhodev May 1, 2024
59e141c
bump version and turn on search feature flag
thiagocarvalhodev May 2, 2024
52dd2ec
refactor and move search result to its own file
thiagocarvalhodev May 2, 2024
c8cda3a
Update search_modal.dart
thiagocarvalhodev May 2, 2024
c02cf96
refactor: pass the text controller instead of callback
thiagocarvalhodev May 2, 2024
a96913a
Update search_bloc.dart
thiagocarvalhodev May 2, 2024
35e8988
Merge pull request #1729 from ardriveapp/PE-5883-implement-basic-sear…
thiagocarvalhodev May 2, 2024
10a2758
Merge branch 'dev' into PE-6085-release-ar-drive-v-2-43-0
thiagocarvalhodev May 2, 2024
1f1323b
Update 125.txt
thiagocarvalhodev May 2, 2024
b3a441e
Merge pull request #1732 from ardriveapp/PE-6085-release-ar-drive-v-2…
thiagocarvalhodev May 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/fastlane/metadata/android/en-US/changelogs/125.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- New Feature: Search (for quickly locating files and navigating through folders and drives)
2 changes: 1 addition & 1 deletion assets/config/prod.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
"enablePins": true,
"useNewUploader": true,
"enableMetamaskLogin": true,
"enableSearch": false
"enableSearch": true
}
15 changes: 7 additions & 8 deletions lib/blocs/drive_detail/drive_detail_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ class DriveDetailCubit extends Cubit<DriveDetailState> {
pathSegments: pathSegments,
driveIsEmpty: folderContents.files.isEmpty &&
folderContents.subfolders.isEmpty,

),
);
} else {
Expand Down Expand Up @@ -307,11 +306,7 @@ class DriveDetailCubit extends Cubit<DriveDetailState> {
var state = this.state as DriveDetailLoadSuccess;

if (state.currentDrive.isPublic && item is FileDataTableItem) {
final fileWithRevisions = _driveDao.latestFileRevisionByFileId(
driveId: driveId,
fileId: item.id,
);
final dataTxId = (await fileWithRevisions.getSingle()).dataTxId;
final dataTxId = item.dataTxId;
state = state.copyWith(
selectedFilePreviewUrl:
'${_configService.config.defaultArweaveGatewayUrl}/$dataTxId');
Expand All @@ -326,10 +321,14 @@ class DriveDetailCubit extends Cubit<DriveDetailState> {
state.currentFolderContents.indexOf(item) ~/ state.rowsPerPage;
}

emit(state.copyWith(
emit(
state.copyWith(
selectedItem: item,
showSelectedItemDetails: true,
selectedPage: selectedPage));
selectedPage: selectedPage,
forceRebuildKey: selectedPage != null ? UniqueKey() : null,
),
);
}

ArDriveDataTableItem? _selectedItem;
Expand Down
1 change: 1 addition & 0 deletions lib/blocs/drive_detail/drive_detail_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class DriveDetailLoadSuccess extends DriveDetailState {
multiselect,
forceRebuildKey,
selectedItem,
selectedPage,
];
SelectedItem? maybeSelectedItem() =>
selectedItems.isNotEmpty ? selectedItems.first : null;
Expand Down
35 changes: 20 additions & 15 deletions lib/components/app_top_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:ardrive/gift/reedem_button.dart';
import 'package:ardrive/pages/drive_detail/components/dropdown_item.dart';
import 'package:ardrive/pages/drive_detail/components/hover_widget.dart';
import 'package:ardrive/search/search_modal.dart';
import 'package:ardrive/search/search_text_field.dart';
import 'package:ardrive/services/config/config.dart';
import 'package:ardrive/sync/domain/cubit/sync_cubit.dart';
import 'package:ardrive/utils/app_localizations_wrapper.dart';
Expand All @@ -21,6 +22,7 @@ class AppTopBar extends StatelessWidget {
Widget build(BuildContext context) {
final enableSearch = context.read<ConfigService>().config.enableSearch;
final colorTokens = ArDriveTheme.of(context).themeData.colorTokens;
final controller = TextEditingController();

return SizedBox(
height: 110,
Expand All @@ -32,25 +34,28 @@ class AppTopBar extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (enableSearch) ...[
Flexible(
child: ArDriveTextFieldNew(
hintText: 'Search',
suffixIcon: const Icon(Icons.search),
onFieldSubmitted: (s) {
showArDriveDialog(
context,
content: FileSearchModal(
initialQuery: s,
driveDetailCubit: context.read<DriveDetailCubit>(),
),
// blur effect
barrierColor: colorTokens.containerL1.withOpacity(0.8),
);
},
Expanded(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: SearchTextField(
controller: controller,
onFieldSubmitted: (query) {
showArDriveDialog(
context,
content: FileSearchModal(
initialQuery: query,
driveDetailCubit: context.read<DriveDetailCubit>(),
controller: controller,
),
barrierColor: colorTokens.containerL1.withOpacity(0.8),
);
},
),
),
),
const SizedBox(width: 24),
],
const Spacer(),
const SyncButton(),
const SizedBox(width: 24),
const RedeemButton(),
Expand Down
93 changes: 46 additions & 47 deletions lib/models/daos/drive_dao/drive_dao.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:ardrive/core/crypto/crypto.dart';
import 'package:ardrive/entities/entities.dart';
import 'package:ardrive/models/license.dart';
import 'package:ardrive/models/models.dart';
import 'package:ardrive/search/search_result.dart';
import 'package:ardrive_utils/ardrive_utils.dart';
import 'package:arweave/arweave.dart';
import 'package:cryptography/cryptography.dart';
Expand Down Expand Up @@ -430,65 +431,75 @@ class DriveDao extends DatabaseAccessor<Database> with _$DriveDaoMixin {
});
}

Future<List<SearchResult>> searchFiles(String name) async {
final resultFiles = await (select(fileRevisions)
..where((tbl) => tbl.name.like('%$name%')))
.get();
final resultFolders = await (select(folderRevisions)
..where((tbl) => tbl.name.like('%$name%')))
Future<List<SearchResult>> search({
required String query,
required SearchQueryType type,
}) async {
final resultFiles = await (select(fileEntries)
..where((tbl) => tbl.name.like('%$query%')))
.get();
final resultDrives = await (select(driveRevisions)
..where((tbl) => tbl.name.like('%$name%')))
final resultFolders = await (select(folderEntries)
..where((tbl) => tbl.name.like('%$query%')))
.get();
final resultDrives =
await (select(drives)..where((tbl) => tbl.name.like('%$query%'))).get();

resultFolders.removeWhere((element) => element.parentFolderId == null);

final List<SearchResult> results = [];
final fileResults = await Future.wait(
resultFiles.map(
(file) async {
final folder = await folderById(
driveId: file.driveId,
folderId: file.parentFolderId,
).getSingle();

final drive = await driveById(driveId: file.driveId).getSingle();
FolderEntry? folder;

return SearchResult<FileRevision>(
if (file.parentFolderId != drive.rootFolderId) {
folder = await folderById(
driveId: file.driveId,
folderId: file.parentFolderId,
).getSingle();
}

return SearchResult<FileEntry>(
result: file,
folder: folder,
parentFolder: folder,
drive: drive,
);
},
),
);

final folderResults = await Future.wait(resultFolders.map((folder) async {
FolderEntry? parentFolder;

if (folder.parentFolderId != null) {
final parentFolderEntry = await folderById(
driveId: folder.driveId,
folderId: folder.parentFolderId!,
).getSingle();
parentFolder = parentFolderEntry;
}

final drive = await driveById(driveId: folder.driveId).getSingle();
final folderResults = await Future.wait(
resultFolders.map(
(folder) async {
FolderEntry? parentFolder;
final drive = await driveById(driveId: folder.driveId).getSingle();

if (folder.parentFolderId != null &&
folder.parentFolderId! != drive.rootFolderId) {
final parentFolderEntry = await folderById(
driveId: folder.driveId,
folderId: folder.parentFolderId!,
).getSingle();
parentFolder = parentFolderEntry;
}

return SearchResult<FolderRevision>(
result: folder,
folder: parentFolder,
drive: drive,
);
}));
return SearchResult<FolderEntry>(
result: folder,
parentFolder: parentFolder,
drive: drive,
);
},
),
);

final driveResults = await Future.wait(resultDrives.map((drive) async {
return SearchResult<DriveRevision>(
return SearchResult<Drive>(
result: drive,
drive: await driveById(driveId: drive.driveId).getSingle(),
drive: await driveById(driveId: drive.id).getSingle(),
);
}));

results.addAll(driveResults);
results.addAll(folderResults);
results.addAll(fileResults);
Expand Down Expand Up @@ -641,15 +652,3 @@ class DriveDao extends DatabaseAccessor<Database> with _$DriveDaoMixin {
});
}
}

class SearchResult<T> {
final T result;
final FolderEntry? folder;
final Drive drive;

SearchResult({
required this.result,
this.folder,
required this.drive,
});
}
27 changes: 26 additions & 1 deletion lib/pages/drive_detail/components/drive_detail_data_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ Widget _buildDataListContent(
!context.watch<ActivityTracker>().isMultiSelectEnabled,
rowsPerPageText: appLocalizationsOf(context).rowsPerPage,
maxItemsPerPage: 100,

pageItemsDivisorFactor: 25,
onSelectedRows: (boxes) {
final bloc = context.read<DriveDetailCubit>();
Expand Down Expand Up @@ -408,6 +407,32 @@ class DriveDataTableItemMapper {
);
}

static FileDataTableItem fromFileEntryForSearchModal(
FileEntry fileEntry,
) {
return FileDataTableItem(
isOwner: true,
lastModifiedDate: fileEntry.lastModifiedDate,
name: fileEntry.name,
size: fileEntry.size,
lastUpdated: fileEntry.lastUpdated,
dateCreated: fileEntry.dateCreated,
contentType: fileEntry.dataContentType ?? '',
fileStatusFromTransactions: null,
fileId: fileEntry.id,
driveId: fileEntry.driveId,
parentFolderId: fileEntry.parentFolderId,
dataTxId: fileEntry.dataTxId,
bundledIn: fileEntry.bundledIn,
licenseTxId: fileEntry.licenseTxId,
metadataTx: null,
dataTx: null,
index: 0,
pinnedDataOwnerAddress: fileEntry.pinnedDataOwnerAddress,
isHidden: fileEntry.isHidden,
);
}

static FolderDataTableItem fromFolderEntry(
FolderEntry folderEntry,
int index,
Expand Down
31 changes: 31 additions & 0 deletions lib/search/domain/bloc/search_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:ardrive/search/domain/repository/search_repository.dart';
import 'package:ardrive/search/search_result.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

part 'search_event.dart';
part 'search_state.dart';

class SearchBloc extends Bloc<SearchEvent, SearchState> {
final SearchRepository _searchRepository;

SearchBloc(
this._searchRepository,
) : super(SearchInitial()) {
on<SearchEvent>((event, emit) async {
if (event is SearchQueryChanged) {
if (event.query.isEmpty) {
emit(SearchQueryEmpty());
} else {
final results = await _searchRepository.search(event.query);

if (results.isEmpty) {
emit(SearchEmpty());
} else {
emit(SearchSuccess(results));
}
}
}
});
}
}
17 changes: 17 additions & 0 deletions lib/search/domain/bloc/search_event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
part of 'search_bloc.dart';

sealed class SearchEvent extends Equatable {
const SearchEvent();

@override
List<Object> get props => [];
}

class SearchQueryChanged extends SearchEvent {
final String query;

const SearchQueryChanged(this.query);

@override
List<Object> get props => [query];
}
23 changes: 23 additions & 0 deletions lib/search/domain/bloc/search_state.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
part of 'search_bloc.dart';

sealed class SearchState extends Equatable {
const SearchState();

@override
List<Object> get props => [];
}

final class SearchInitial extends SearchState {}

final class SearchEmpty extends SearchState {}

final class SearchQueryEmpty extends SearchState {}

final class SearchSuccess extends SearchState {
final List<SearchResult> results;

const SearchSuccess(this.results);

@override
List<Object> get props => [results];
}
21 changes: 21 additions & 0 deletions lib/search/domain/repository/search_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:ardrive/models/daos/drive_dao/drive_dao.dart';
import 'package:ardrive/search/search_result.dart';

abstract class SearchRepository {
Future<List<SearchResult>> search(String query);
}

class ArDriveSearchRepository implements SearchRepository {
final DriveDao _driveDao;

ArDriveSearchRepository(this._driveDao);

@override
Future<List<SearchResult>> search(String query) async {
if (query.isEmpty) return Future.value([]);

String sanitizedQuery = query.toLowerCase();

return _driveDao.search(query: sanitizedQuery, type: SearchQueryType.name);
}
}
Loading
Loading