Load bookmarks

This commit is contained in:
Juan Gilsanz Polo
2024-02-22 18:32:20 +01:00
parent 926147edbd
commit 1521fd0f7e
17 changed files with 429 additions and 286 deletions

View File

@@ -1,9 +1,9 @@
import 'package:my_linkding/constants/enums.dart';
class LinksModel {
final LoadStatus loadStatus;
int currentPage;
int limit;
LinksModel({
required this.loadStatus,
this.currentPage = 0,
this.limit = 100,
});
}

View File

@@ -1 +1,31 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:my_linkding/screens/links/model/links.model.dart';
import 'package:my_linkding/models/api_response.dart';
import 'package:my_linkding/models/data/bookmarks.dart';
import 'package:my_linkding/providers/api_client_provider.dart';
part 'links.provider.g.dart';
@riverpod
FutureOr<ApiResponse<Bookmarks>> linksRequest(LinksRequestRef ref) async {
final result = await ref.watch(apiClientProviderProvider)!.fetchBookmarks();
return result;
}
@riverpod
class Links extends _$Links {
@override
LinksModel build() {
return LinksModel();
}
void setCurrentPage(int page) {
state.currentPage = page;
}
void setLimit(int limit) {
state.limit = limit;
}
}

View File

@@ -0,0 +1,39 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'links.provider.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$linksRequestHash() => r'be41da02d669705dc7723ba9fbab0c5b59fd233b';
/// See also [linksRequest].
@ProviderFor(linksRequest)
final linksRequestProvider =
AutoDisposeFutureProvider<ApiResponse<Bookmarks>>.internal(
linksRequest,
name: r'linksRequestProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$linksRequestHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef LinksRequestRef = AutoDisposeFutureProviderRef<ApiResponse<Bookmarks>>;
String _$linksHash() => r'a609009ca33885b1efd0d7c1122bee8299b98af3';
/// See also [Links].
@ProviderFor(Links)
final linksProvider = AutoDisposeNotifierProvider<Links, LinksModel>.internal(
Links.new,
name: r'linksProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$linksHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$Links = AutoDisposeNotifier<LinksModel>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -1,12 +1,40 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_linkding/screens/links/provider/links.provider.dart';
import 'package:my_linkding/i18n/strings.g.dart';
class Links extends StatelessWidget {
class Links extends ConsumerWidget {
const Links({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final bookmarks = ref.watch(linksRequestProvider);
String validateStrings(String? string1, String? string2) {
if (string1 != null && string1.isNotEmpty) {
return string1;
} else if (string2 != null && string2.isNotEmpty) {
return string2;
} else {
return "";
}
}
String dateString(DateTime date) {
final today = DateTime.now();
final yesterday = DateTime.now().subtract(const Duration(days: 1));
if (date.day == today.day && date.month == today.month && date.year == today.year) {
return t.links.dates.todayAt(time: "${date.hour}:${date.minute}");
} else if (date.day == yesterday.day && date.month == yesterday.month && date.year == yesterday.year) {
return t.links.dates.yesterdayAt(time: "${date.hour}:${date.minute}");
} else {
return "${date.day}/${date.month}/${date.year} - ${date.hour}:${date.minute}";
}
}
return Material(
color: Colors.transparent,
child: NestedScrollView(
@@ -26,65 +54,109 @@ class Links extends StatelessWidget {
top: false,
bottom: false,
child: Builder(
builder: (context) => CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverList.builder(
itemCount: 40,
itemBuilder: (context, index) => ListTile(
title: Text(
"This is the title",
style: TextStyle(
fontSize: 18,
color: Theme.of(context).colorScheme.onSurface,
builder: (context) => RefreshIndicator(
displacement: 95,
onRefresh: () => ref.refresh(linksRequestProvider.future),
child: CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
if (bookmarks.isLoading && !bookmarks.isRefreshing)
const SliverFillRemaining(
child: Center(child: CircularProgressIndicator()),
),
if (bookmarks.value != null && bookmarks.value!.successful == false)
const SliverFillRemaining(
child: Center(
child: Icon(Icons.error),
),
),
subtitle: Column(
children: [
Text(
"All the speed he took, all the turns hed taken and the dripping chassis of a skyscraper canyon. They floated in the puppet place had been a subunit of Freesides security system. The knives seemed to move of their own accord, gliding with a ritual lack of urgency through the center of his closed left eyelid. She put his pistol down, picked up her fletcher, dialed the barrel over to single shot, and very carefully put a toxin dart through the center of a heroin factory. Still it was a square of faint light. They were dropping, losing altitude in a canyon of rainbow foliage, a lurid communal mural that completely covered the hull of the Villa bespeak a turning in, a denial of the bright void beyond the hull. A narrow wedge of light from a half-open service hatch at the clinic, Molly took him to the simple Chinese hollow points Shin had sold him. Still it was a steady pulse of pain midway down his spine. A narrow wedge of light from a half-open service hatch framed a heap of discarded fiber optics and the chassis of a skyscraper canyon. The Tessier-Ashpool ice shattered, peeling away from the banks of every computer in the center of his closed left eyelid.",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 4),
Row(
if (bookmarks.value != null &&
bookmarks.value!.successful == true &&
bookmarks.value!.content!.results!.isEmpty)
const SliverFillRemaining(
child: Center(
child: Text("No bookmarks"),
),
),
if (bookmarks.value != null &&
bookmarks.value!.successful == true &&
bookmarks.value!.content!.results!.isNotEmpty)
SliverList.builder(
itemCount: bookmarks.value?.content?.results?.length,
itemBuilder: (context, index) {
final link = bookmarks.value?.content?.results?[index];
return Column(
children: [
Expanded(
child: Text(
"#flutter #apps",
ListTile(
isThreeLine: true,
title: Text(
validateStrings(link?.title, link?.websiteTitle),
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
fontSize: 18,
color: Theme.of(context).colorScheme.onSurface,
),
),
),
Container(
width: 1,
height: 12,
margin: const EdgeInsets.symmetric(horizontal: 8),
color: Theme.of(context).dividerColor,
),
Text(
"1 week ago",
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
subtitle: Column(
children: [
Text(
validateStrings(link?.description, link?.websiteDescription),
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 4),
Row(
children: [
Expanded(
child: Text(
link?.tagNames?.map((tag) => "#$tag").join(" ") ?? '',
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
if (link?.dateModified != null) ...[
Container(
width: 1,
height: 12,
margin: const EdgeInsets.symmetric(horizontal: 8),
color: Theme.of(context).colorScheme.tertiary.withOpacity(0.3),
),
Text(
dateString(link!.dateModified!),
style: TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
],
),
],
),
),
if (index < bookmarks.value!.content!.results!.length - 1)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Divider(
color: Theme.of(context).colorScheme.tertiary.withOpacity(0.3),
),
),
],
),
],
);
},
),
),
),
],
],
),
),
),
),

View File

@@ -1,3 +1,4 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -5,6 +6,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:my_linkding/screens/onboarding/model/connect.model.dart';
import 'package:my_linkding/providers/server_instances_provider.dart';
import 'package:my_linkding/utils/api_base_url.dart';
import 'package:my_linkding/i18n/strings.g.dart';
import 'package:my_linkding/constants/enums.dart';
import 'package:my_linkding/utils/process_modal.dart';
@@ -88,7 +90,17 @@ FutureOr<bool> connectToServer(ConnectToServerRef ref) async {
token: ref.watch(connectProvider).tokenController.text,
);
final apiClient = ApiClient(serverInstance: serverInstance);
final apiClient = ApiClient(
serverInstance: serverInstance,
dioInstance: Dio(
BaseOptions(
baseUrl: apiBaseUrl(serverInstance),
headers: {
"Authorization": "Token ${serverInstance.token}",
},
),
),
);
final processModal = ProcessModal();
processModal.open(t.onboarding.connecting);

View File

@@ -6,7 +6,7 @@ part of 'connect.provider.dart';
// RiverpodGenerator
// **************************************************************************
String _$connectToServerHash() => r'85925cb547fccb708e657d263e39a46867ac2bca';
String _$connectToServerHash() => r'a12cade5244ec6d3b19897053380bd6c5fcf1265';
/// See also [connectToServer].
@ProviderFor(connectToServer)