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

docs: Write some docs for an InfiniteList #26

Merged
merged 2 commits into from
Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 48 additions & 9 deletions lib/src/model/infinite_list.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:bloc_infinite_list/src/model/pageable.dart';
import 'package:bloc_infinite_list/src/model/slice.dart';
import 'package:collection/collection.dart';
Expand Down Expand Up @@ -64,83 +66,120 @@ class InfiniteList<T> extends Equatable {

T operator [](int index) => items[index];

/// The length of the [items].
int get itemCount => items.length;

/// Whether the [items] is empty.
bool get isEmpty => items.isEmpty;

/// Whether the [items] is not empty.
bool get isNotEmpty => !isEmpty;

bool get isFetchNotNeeded => status.isLoadCompleted || status.isLoading;
/// Determines current status will need to fetch next page or not.
bool get isFetchNotNeeded =>
status.isInitialLoading || status.isLoadCompleted || status.isLoading;

/// Get index of the [item] in the [items].
/// Returns `null` if the [item] is not found.
int? indexOf(T item) {
final result = items.indexOf(item);
return result == _IDX_NOT_FOUND ? null : result;
}

/// Get index of the last [item] in the [items].
/// If the [item] is found multiple times, the last index is returned.
/// Returns `null` if the [item] is not found.
///
/// If [item] is found only once, the result is the same as [indexOf].
int? lastIndexOf(T item) {
final result = items.lastIndexOf(item);
return result == _IDX_NOT_FOUND ? null : result;
}

/// Get index of the first item that satisfies the [test].
int? indexWhere(bool Function(T item) test) {
final result = items.indexWhere(test);
return result == _IDX_NOT_FOUND ? null : result;
}

/// Get index of the last item that satisfies the [test].
int? lastIndexWhere(bool Function(T item) test) {
final result = items.lastIndexWhere(test);
return result == _IDX_NOT_FOUND ? null : result;
}

/// Get the first item that satisfies the [test].
/// Returns `null` if no item satisfies the [test].
T? firstWhereOrNull(bool Function(T item) test) =>
items.firstWhereOrNull(test);

/// Gets all items that satisfy the [test].
Iterable<T> where(bool Function(T item) test) => items.where(test);

/// Port of `Iterable.singleWhereOrNull`.
/// Gets only single item that satisfies the [test].
/// Returns `null` if no item satisfies the [test] or multiple items satisfy the [test].
T? singleWhereOrNull(bool Function(T item) test) =>
items.singleWhereOrNull(test);

/// Whether the [items] contains the item that satisfies the [test].
bool containsWhere(bool Function(T item) test) =>
firstWhereOrNull(test) != null;

/// Whether the [items] contains the [item].
bool contains(T item) => items.contains(item);

/// Get sub [Iterable] from the [items].
/// `[start, end)`
Iterable<T> getRange(int start, int end) => items.getRange(start, end);

/// ------------------------------------------ End Queries

/// ------------------------------------------ Mutations

/// Returns a new [InfiniteList] with changed to the loading status.
InfiniteList<T> copyToLoading() => copyWith(
status: status.isInitial
? InfiniteListStatus.initialLoading
: InfiniteListStatus.loading,
);

/// Returns a new [InfiniteList] with changed to the loaded status.
InfiniteList<T> copyToLoaded() => copyWith(status: InfiniteListStatus.loaded);

InfiniteList<T> insert(int idx, T item) => copyWith(
items: [
...items.sublist(0, idx),
item,
...items.sublist(idx),
],
);
/// Returns a new [InfiniteList] with the item is inserted at the [idx].
/// If the [idx] is greater than the [itemCount], the [item] is added to the end of the [items].
InfiniteList<T> insert(int idx, T item) {
final cappedIdx = min(idx, itemCount);
return copyWith(
items: [
...items.sublist(0, cappedIdx),
item,
...items.sublist(cappedIdx),
],
);
}

/// Returns a new [InfiniteList] with the [item] is added to the end of the [items].
InfiniteList<T> addItem(T item) => copyWith(
items: [...items, item],
);

/// Returns a new [InfiniteList] with the [newItems] is added to the end of the [items].
InfiniteList<T> addItems(Iterable<T> newItems) => copyWith(
items: [...items, ...newItems],
);

/// Returns a new [InfiniteList] that [before] in the list is replaced with [after].
/// If the [before] is not found, the [items] is not changed.
InfiniteList<T> replace(T before, T after) => copyWith(
items: [
for (var item in items)
if (item == before) after else item,
],
);

/// Returns a new [InfiniteList] that the item at the [idx] is replaced with [item].
/// If the [idx] is out of range, the [items] is not changed.
InfiniteList<T> replaceAt(int idx, T item) => copyWith(
items: [
for (var j = 0; j < items.length; j++)
Expand Down
68 changes: 56 additions & 12 deletions test/model/infinite_list_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@ void main() {
expect(list.isEmpty, isTrue);
});

test('removeAt()', () {
const list = InfiniteList<int>(items: [1, 2, 3]);

expect(list.removeAt(1).items, [1, 3]);
});

test('addItems()', () {
const list = InfiniteList<int>();

expect(list.addItems([1, 2, 3, 4, 5]).items, [1, 2, 3, 4, 5]);
});

group('reset()', () {
test('clears all state to initial', () {
const list = InfiniteList<int>(
Expand All @@ -40,4 +28,60 @@ void main() {
expect(list.reset(), const InfiniteList<int>());
});
});

group('insert()', () {
test('inserts a item at the idx', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.insert(1, 0).items, [1, 0, 2, 3]);
});

test('inserts at the end if idx is greater than the length', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.insert(10, 0).items, [1, 2, 3, 0]);

const list2 = InfiniteList<int>(items: [1, 2, 3]);
expect(list2.insert(3, 1).items, [1, 2, 3, 1]);
});
});

test('addItem()', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.addItem(5).items, [1, 2, 3, 5]);
});

test('addItems()', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.addItems([7, 8, 9]).items, [1, 2, 3, 7, 8, 9]);
});

group('replace()', () {
test('replaces `before` to an `after`', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.replace(2, 4).items, [1, 4, 3]);
});

test('affects nothing if there is no `before` in the list', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.replace(4, 5).items, [1, 2, 3]);
});
});

group('replaceAt()', () {
test('replaces an item at the idx with the `item`', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.replaceAt(1, 4).items, [1, 4, 3]);
});

test('happens nothing if idx is not bound with items', () {
const list = InfiniteList<int>(items: [1, 2, 3]);
expect(list.replaceAt(10, 4).items, [1, 2, 3]);
expect(list.replaceAt(-1, 4).items, [1, 2, 3]);
});
});

test('removeAt()', () {
const list = InfiniteList<int>(items: [1, 2, 3]);

expect(list.removeAt(1).items, [1, 3]);
});
}