fix lints

This commit is contained in:
Fabian Freund
2026-05-22 19:16:03 +02:00
parent a5974617aa
commit ee99d28379
72 changed files with 244 additions and 274 deletions
+1
View File
@@ -11,6 +11,7 @@ linter:
analyzer:
errors:
avoid_redundant_argument_values: ignore
experimental_member_use: ignore
exclude:
- "**.g.dart"
@@ -17,6 +17,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'dart:async';
import 'dart:io';
import 'dart:ui';
@@ -193,7 +194,7 @@ class GenericWebsiteService extends _$GenericWebsiteService {
inFlight = _fetchAndCacheDdgIcon(url, cacheMissing: cacheMissing)
.whenComplete(() {
if (identical(_inFlightIconFetches[origin], inFlight)) {
_inFlightIconFetches.remove(origin);
_inFlightIconFetches.remove(origin)?.ignore();
}
});
@@ -37,7 +37,6 @@ class AccountAuthState with FastEquatable {
// states for the same user but with distinct client instances are still
// semantically equal — including identityHashCode here would defeat
// Riverpod's caching by treating every reissued state as different.
// ignore: missing_field_in_equatable_props
final SupabaseClient? client;
AccountAuthState({
@@ -64,5 +63,6 @@ class AccountAuthState with FastEquatable {
userId,
lastError,
syncKey,
client,
];
}
@@ -271,7 +271,6 @@ class AccountAuthRepository extends _$AccountAuthRepository {
// Clear the pending code verifier so a late browser callback is rejected.
final data = await _store.read();
// ignore: avoid_redundant_argument_values
await _store.write(data.copyWith(pendingCodeVerifier: null));
state = AsyncData(AccountAuthState());
@@ -327,7 +326,6 @@ class AccountAuthRepository extends _$AccountAuthRepository {
displayName:
(user?['user_metadata'] as Map<String, dynamic>?)?['display_name']
as String?,
// ignore: avoid_redundant_argument_values
pendingCodeVerifier: null,
),
);
@@ -372,9 +370,7 @@ class AccountAuthRepository extends _$AccountAuthRepository {
Future<void> clearSyncKey() async {
final data = await _store.read();
// ignore: avoid_redundant_argument_values
await _store.write(data.copyWith(syncKey: null));
// ignore: avoid_redundant_argument_values
state = AsyncData(_currentOrEmpty.copyWith(syncKey: null));
}
@@ -15,7 +15,7 @@ final webSearchBang = BangData(
searxngApi: false,
);
final webSearchBangKey = BangKey(
const webSearchBangKey = BangKey(
group: BangGroup.weblibre,
trigger: webSearchBangTrigger,
);
@@ -17,14 +17,12 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'dart:async';
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/features/bangs/data/models/bang_data.dart';
import 'package:weblibre/features/bangs/domain/providers/search.dart';
import 'package:weblibre/features/bangs/presentation/widgets/bang_details.dart';
import 'package:weblibre/features/user/domain/providers.dart';
@@ -1033,7 +1033,7 @@ EquatableValue<List<TabListItemEntity>> groupedTabListItems(
final unpinned = children
.where((c) => !pinnedTabIds.contains(c.row.id))
.toList();
final cmp = (_GroupedRow a, _GroupedRow b) =>
int cmp(_GroupedRow a, _GroupedRow b) =>
a.row.orderKey.compareTo(b.row.orderKey);
final directionCmp = tabListDirection == TabDirection.newestFirst
? (_GroupedRow a, _GroupedRow b) => -cmp(a, b)
@@ -85,12 +85,10 @@ class TabViewFilterController extends _$TabViewFilterController {
}
void setDateRange(DateTimeRange<DateTime>? range) {
// ignore: avoid_redundant_argument_values
state = state.copyWith(dateRange: range, quickInterval: null);
}
void setQuickInterval(TabQuickInterval? interval) {
// ignore: avoid_redundant_argument_values
state = state.copyWith(quickInterval: interval, dateRange: null);
}
@@ -1194,13 +1194,11 @@ class _Browser extends HookConsumerWidget {
switch (promptOnBackBehavior) {
case BackgroundAppTabBackPromptBehavior():
await moveToBackground();
break;
case ReturnToSearchTabBackPromptBehavior(:final tabType):
ref
.read(searchAutofocusSuppressionProvider.notifier)
.suppressNext();
await SearchRoute(tabType: tabType).push(context);
break;
}
return true;
@@ -219,7 +219,7 @@ List<String> _orderedIdsForStorageAnchors(
required String movingPartitionRootId,
required bool sortPinnedFirst,
}) {
var storageOrderedIds = tabListDirection == TabDirection.newestFirst
final storageOrderedIds = tabListDirection == TabDirection.newestFirst
// Rendering flips root group order for newest-first; convert the
// display order back to storage order before choosing anchors.
? orderedTabIds.reversed.toList()
@@ -580,11 +580,13 @@ class QuickTabSwitcher extends HookConsumerWidget {
WidgetsBinding.instance.addPostFrameCallback((_) {
final context = activeItemKey.value.currentContext;
if (context != null) {
Scrollable.ensureVisible(
context,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
unawaited(
Scrollable.ensureVisible(
context,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
} else if (chipScrollController.hasClients) {
final activeIndex = availableItems.indexWhere(
@@ -602,11 +604,13 @@ class QuickTabSwitcher extends HookConsumerWidget {
WidgetsBinding.instance.addPostFrameCallback((_) {
final retryContext = activeItemKey.value.currentContext;
if (retryContext != null) {
Scrollable.ensureVisible(
retryContext,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
unawaited(
Scrollable.ensureVisible(
retryContext,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
}
});
@@ -717,8 +721,6 @@ class QuickTabSwitcherView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appColors = AppColors.of(context);
final colorScheme = Theme.of(context).colorScheme;
if (availableItems.isEmpty) {
return const SizedBox.shrink();
}
@@ -27,8 +27,8 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_migrations.dart';
import 'package:weblibre/features/geckoview/features/preferences/data/models/preference_setting.dart';
import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_migrations.dart';
import 'package:weblibre/features/geckoview/features/tabs/utils/setting_groups_serializer.dart';
import 'package:weblibre/features/user/data/providers.dart';
@@ -107,7 +107,7 @@ Future<bool> installCurrentWebApp(
Ref ref, {
String? overrideName,
String? contextId,
}) async {
}) {
final selectedTabId = ref.read(selectedTabProvider);
if (selectedTabId == null) {
@@ -156,7 +156,7 @@ Future<bool> installBasicShortcut(
Ref ref, {
String? overrideName,
String? contextId,
}) async {
}) {
final selectedTabId = ref.read(selectedTabProvider);
if (selectedTabId == null) {
@@ -801,8 +801,8 @@ class _WebSearchOptionsRow extends StatelessWidget {
controller: controller,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Row(
children: const [
child: const Row(
children: [
WebSearchStatusChip(),
RouteThroughTorToggle(),
SizedBox(width: 8),
@@ -60,9 +60,7 @@ class FeedSearch extends HookConsumerWidget {
.read(articleSearchProvider(null).notifier)
.search(
searchTextNotifier.value.text,
// ignore: avoid_redundant_argument_values dont break things
matchPrefix: _matchPrefix,
// ignore: avoid_redundant_argument_values dont break things
matchSuffix: _matchSuffix,
);
},
@@ -123,9 +123,7 @@ class TabSearch extends HookConsumerWidget {
)
.addQuery(
searchTextListenable.value.text,
// ignore: avoid_redundant_argument_values dont break things
matchPrefix: _matchPrefix,
// ignore: avoid_redundant_argument_values dont break things
matchSuffix: _matchSuffix,
);
}
@@ -18,6 +18,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
@@ -181,7 +183,7 @@ class ContainerEditScreen extends HookConsumerWidget {
title: const Text('Change Color'),
onTap: () {
Navigator.of(context).pop();
openColorPicker();
unawaited(openColorPicker());
},
),
ListTile(
@@ -189,7 +191,7 @@ class ContainerEditScreen extends HookConsumerWidget {
title: const Text('Change Icon'),
onTap: () {
Navigator.of(context).pop();
openIconPicker();
unawaited(openIconPicker());
},
),
const SizedBox(height: 8),
@@ -105,7 +105,6 @@ class ContainerListScreen extends HookConsumerWidget {
.read(selectedContainerProvider.notifier)
.clearContainer()
: () => setSelectedContainer(container),
// onDelete: () => repository.deleteContainer(container.id),
),
);
},
@@ -168,7 +167,6 @@ class _ContainerCard extends HookConsumerWidget {
required this.isSelected,
required this.onTap,
required this.onSelect,
this.onDelete,
});
final ContainerDataWithCount container;
@@ -176,7 +174,6 @@ class _ContainerCard extends HookConsumerWidget {
final bool isSelected;
final VoidCallback onTap;
final VoidCallback onSelect;
final VoidCallback? onDelete;
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -299,15 +296,6 @@ class _ContainerCard extends HookConsumerWidget {
else
const SizedBox.shrink(),
const Spacer(),
if (onDelete != null) ...[
IconButton(
tooltip: 'Delete',
color: colorScheme.error,
onPressed: onDelete,
icon: const Icon(Icons.delete_outline),
),
const SizedBox(width: 4),
],
FilledButton.tonalIcon(
onPressed: onSelect,
icon: Icon(isSelected ? Icons.close : Icons.check),
@@ -20,9 +20,7 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/entities/container_selection_result.dart';
@@ -35,7 +35,6 @@ import 'package:weblibre/features/geckoview/features/tabs/data/models/container_
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_chip_content.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_title.dart';
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/tab_drag_container_target.dart';
import 'package:weblibre/features/geckoview/features/tabs/utils/container_colors.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
@@ -303,11 +302,13 @@ class ContainerChips extends HookConsumerWidget {
WidgetsBinding.instance.addPostFrameCallback((_) {
final activeContext = activeItemKey.value.currentContext;
if (activeContext != null) {
Scrollable.ensureVisible(
activeContext,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
unawaited(
Scrollable.ensureVisible(
activeContext,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
return;
}
@@ -336,11 +337,13 @@ class ContainerChips extends HookConsumerWidget {
WidgetsBinding.instance.addPostFrameCallback((_) {
final retryContext = activeItemKey.value.currentContext;
if (retryContext != null) {
Scrollable.ensureVisible(
retryContext,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
unawaited(
Scrollable.ensureVisible(
retryContext,
alignment: 0.5,
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
),
);
}
});
@@ -17,9 +17,9 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/design/app_colors.dart';
@@ -18,7 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:fast_equatable/fast_equatable.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/features/proxy/data/models/singbox_proxy_profile.dart';
@@ -18,7 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/core/uuid.dart';
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_credentials.dart';
@@ -127,7 +127,7 @@ class SingboxProxyRuntimeRepository extends _$SingboxProxyRuntimeRepository {
Future<SingboxProxyRuntimeState> startProfile(
String profileId, {
SingboxProxyRuntimeOptions? options,
}) async {
}) {
return _lock.synchronized(() async {
final currentState = await _stateSnapshotUnlocked();
final activeProfileIds = _activeProfileIds(currentState);
@@ -320,7 +320,7 @@ class SingboxProxyRuntimeRepository extends _$SingboxProxyRuntimeRepository {
final profileMap = {for (final profile in profiles) profile.id: profile};
return Future.wait(
profileIds.map((profileId) async {
profileIds.map((profileId) {
final profile = profileMap[profileId];
if (profile == null) {
throw StateError('Unknown sing-box proxy profile: $profileId');
@@ -340,15 +340,15 @@ class SingboxProxyRuntimeRepository extends _$SingboxProxyRuntimeRepository {
}
@override
Future<SingboxProxyRuntimeState> build() async {
Future<SingboxProxyRuntimeState> build() {
final plugin = ref.watch(singboxProxyClientProvider);
final stateSubscription = plugin.stateStream.listen((nextState) {
state = AsyncData(nextState);
});
ref.onDispose(() async {
await stateSubscription.cancel();
await plugin.dispose();
ref.onDispose(() {
unawaited(stateSubscription.cancel());
unawaited(plugin.dispose());
});
return plugin.getState();
@@ -17,6 +17,8 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'dart:async';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -104,13 +106,15 @@ class SingboxProxyEndpointSync extends _$SingboxProxyEndpointSync {
(previous, next) {
final runtimeState = next.value;
if (runtimeState == null) return;
_sync(runtimeState).catchError((Object error, StackTrace stackTrace) {
logger.e(
'Failed to sync sing-box proxy endpoints to Gecko',
error: error,
stackTrace: stackTrace,
);
});
unawaited(
_sync(runtimeState).catchError((Object error, StackTrace stackTrace) {
logger.e(
'Failed to sync sing-box proxy endpoints to Gecko',
error: error,
stackTrace: stackTrace,
);
}),
);
},
);
}
@@ -17,25 +17,23 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
abstract final class SearchBackendConfig {
/// Clearnet origin used when the user has not opted to route search through
/// Tor. Must be a normal HTTPS URL (or HTTP for dev).
static const searchBackendOrigin = String.fromEnvironment(
'SEARCH_BACKEND_ORIGIN',
defaultValue: 'https://search.weblibre.eu',
);
/// Clearnet origin used when the user has not opted to route search through
/// Tor. Must be a normal HTTPS URL (or HTTP for dev).
const searchBackendOrigin = String.fromEnvironment(
'SEARCH_BACKEND_ORIGIN',
defaultValue: 'https://search.weblibre.eu',
);
/// Origin used when the user opts to route search through Tor. Should be
/// the WebLibre search service's onion address so the Tor circuit
/// terminates inside the Tor network instead of exiting back to the
/// clearnet. Falls back to the clearnet origin when no onion address is
/// configured at build time.
static const searchBackendOriginTor = String.fromEnvironment(
'SEARCH_BACKEND_ORIGIN_TOR',
defaultValue:
'http://eyipgwt32zaejr2xwblaswp2ur4qikapofunbqus5dklvf7jxkncirad.onion',
);
/// Origin used when the user opts to route search through Tor. Should be
/// the WebLibre search service's onion address so the Tor circuit
/// terminates inside the Tor network instead of exiting back to the
/// clearnet. Falls back to the clearnet origin when no onion address is
/// configured at build time.
const searchBackendOriginTor = String.fromEnvironment(
'SEARCH_BACKEND_ORIGIN_TOR',
defaultValue:
'http://eyipgwt32zaejr2xwblaswp2ur4qikapofunbqus5dklvf7jxkncirad.onion',
);
static Uri get originUri => Uri.parse(searchBackendOrigin);
static Uri get torOriginUri => Uri.parse(searchBackendOriginTor);
}
Uri get searchBackendOriginUri => Uri.parse(searchBackendOrigin);
Uri get searchBackendTorOriginUri => Uri.parse(searchBackendOriginTor);
@@ -33,9 +33,7 @@ BackendEndpoints searchBackendEndpoints(Ref ref) {
webSearchSettingsControllerProvider.select((s) => s.routeThroughTor),
);
return BackendEndpoints.fromOrigin(
routeThroughTor
? SearchBackendConfig.torOriginUri
: SearchBackendConfig.originUri,
routeThroughTor ? searchBackendTorOriginUri : searchBackendOriginUri,
);
}
@@ -99,8 +99,8 @@ class ContextualToolbarSettingsScreen extends HookConsumerWidget {
pinned: true,
delegate: _ToolbarPreviewDelegate(configs: configs.value),
),
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
const SliverPadding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 0),
sliver: SliverToBoxAdapter(child: _SectionLabel(label: 'Enabled')),
),
if (visibleConfigs.isEmpty)
@@ -137,8 +137,8 @@ class ContextualToolbarSettingsScreen extends HookConsumerWidget {
);
},
),
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 20, 16, 0),
const SliverPadding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 0),
sliver: SliverToBoxAdapter(child: _SectionLabel(label: 'Disabled')),
),
if (hiddenConfigs.isEmpty)
@@ -25,7 +25,6 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_addon.dart';
import 'package:weblibre/features/settings/presentation/widgets/custom_list_tile.dart';
import 'package:weblibre/features/settings/presentation/widgets/settings_detail.dart';
const List<SettingsSectionDefinition> extensionsSettingsSections = [
@@ -17,7 +17,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'
@@ -41,6 +41,7 @@ const _alwaysAllowPackageExtra = 'eu.weblibre.gatekeeper.always_allow_package';
StreamTransformer<Intent, ReceivedIntentParameter>
_buildSharingIntentTransformer(
IntentGatekeeper gatekeeper,
GeneralSettingsRepository settingsRepository,
) => StreamTransformer<Intent, ReceivedIntentParameter>.fromHandlers(
handleData: (intent, sink) async {
if (_extractAccountCallback(intent) != null) {
@@ -53,14 +54,12 @@ _buildSharingIntentTransformer(
final alwaysAllowPackage =
intent.extra[_alwaysAllowPackageExtra] as String?;
if (alwaysAllowPackage != null) {
await gatekeeper.ref
.read(generalSettingsRepositoryProvider.notifier)
.updateSettings(
(current) => current.copyWith.externalAppIntentPolicies({
...current.externalAppIntentPolicies,
alwaysAllowPackage: IntentSourcePolicy.allow,
}),
);
await settingsRepository.updateSettings(
(current) => current.copyWith.externalAppIntentPolicies({
...current.externalAppIntentPolicies,
alwaysAllowPackage: IntentSourcePolicy.allow,
}),
);
}
final shortcutContextId = intent.action == 'android.intent.action.VIEW'
@@ -197,10 +196,13 @@ Raw<Stream<T>> _consumeIntents<T>(
Raw<Stream<ReceivedIntentParameter>> sharingIntentStream(Ref ref) {
final receiver = ref.watch(intentReceiverProvider);
final gatekeeper = ref.watch(intentGatekeeperProvider.notifier);
final settingsRepository = ref.watch(
generalSettingsRepositoryProvider.notifier,
);
return _consumeIntents(
ref,
receiver,
_buildSharingIntentTransformer(gatekeeper),
_buildSharingIntentTransformer(gatekeeper, settingsRepository),
);
}
@@ -1,5 +1,3 @@
// ignore_for_file: avoid_redundant_argument_values
/*
* Copyright (c) 2024-2026 Fabian Freund.
*
@@ -315,21 +315,15 @@ class SyncRepository extends _$SyncRepository {
_syncStartedSub = syncStateService.syncStartedEvents.listen((_) {
_update(
(s) => s.copyWith(
lastSyncEvent: SyncEvent.started,
// ignore: avoid_redundant_argument_values
lastSyncError: null,
),
(s) =>
s.copyWith(lastSyncEvent: SyncEvent.started, lastSyncError: null),
);
});
_syncCompletedSub = syncStateService.syncCompletedEvents.listen((_) {
_update(
(s) => s.copyWith(
lastSyncEvent: SyncEvent.completed,
// ignore: avoid_redundant_argument_values
lastSyncError: null,
),
(s) =>
s.copyWith(lastSyncEvent: SyncEvent.completed, lastSyncError: null),
);
unawaited(Future.wait([_refreshTabs(), _refreshDevices()]));
});
@@ -19,10 +19,10 @@
*/
import 'package:drift/drift.dart';
import 'package:weblibre/data/database/extensions/database_table_size.dart';
import 'package:weblibre/features/user/data/icon_cache_marker.dart';
import 'package:weblibre/features/user/data/database/daos/cache.drift.dart';
import 'package:weblibre/features/user/data/database/database.dart';
import 'package:weblibre/features/user/data/database/definitions.drift.dart';
import 'package:weblibre/features/user/data/icon_cache_marker.dart';
@DriftAccessor()
class CacheDao extends DatabaseAccessor<UserDatabase> with $CacheDaoMixin {
@@ -67,7 +67,7 @@ class SearchTokensDao extends DatabaseAccessor<UserDatabase> {
/// Release any reservation older than [maxAge] — covers crashes mid-flight.
/// Returns the number of rows released.
Future<int> releaseStaleReservations(Duration maxAge) async {
Future<int> releaseStaleReservations(Duration maxAge) {
final cutoff = DateTime.now().subtract(maxAge);
return (update(db.searchTokens)..where(
(t) =>
@@ -261,7 +261,7 @@ class UBlockAssetsRegistry {
return count;
}
static UBlockAssetsRegistry fromJson(Map<String, dynamic> json) {
factory UBlockAssetsRegistry.fromJson(Map<String, dynamic> json) {
final entries = <String, UBlockAssetEntry>{};
for (final entry in json.entries) {
if (entry.value is Map<String, dynamic>) {
@@ -17,16 +17,15 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
library;
/// Curated picker options for the web-search language and country selectors.
///
/// Codes intersect what Brave (`search_lang`/`country`) and Mojeek
/// (`lb`/`rb`) accept; English display names so the menu doesn't need a
/// per-locale translation pass. The protocol carries just the primary
/// ISO 639-1 / ISO 3166-1 alpha-2 codes — region qualifiers (`en-gb`,
/// `pt-br`, etc.) are reconstructed server-side from the language+country
/// pair.
// Curated picker options for the web-search language and country selectors.
//
// Codes intersect what Brave (`search_lang`/`country`) and Mojeek
// (`lb`/`rb`) accept; English display names so the menu doesn't need a
// per-locale translation pass. The protocol carries just the primary
// ISO 639-1 / ISO 3166-1 alpha-2 codes — region qualifiers (`en-gb`,
// `pt-br`, etc.) are reconstructed server-side from the language+country
// pair.
class LanguageOption {
final String code; // ISO 639-1
@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'
as fmc;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:search_client/search_client.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
@@ -18,9 +17,9 @@ import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_mode
import 'package:weblibre/features/geckoview/features/tabs/data/providers.dart';
import 'package:weblibre/features/search_credits/domain/controllers/search_token_issuance_controller.dart';
import 'package:weblibre/features/search_credits/domain/providers.dart';
import 'package:weblibre/features/search_credits/domain/providers/proxy_client.dart';
import 'package:weblibre/features/search_credits/domain/repositories/search_token_stash_repository.dart';
import 'package:weblibre/features/user/domain/repositories/cache.dart';
import 'package:weblibre/features/search_credits/domain/providers/proxy_client.dart';
import 'package:weblibre/features/web_search/domain/services/capture_artifact_downloader.dart';
import 'package:weblibre/features/web_search/domain/services/capture_server.dart';
import 'package:weblibre/features/web_search/domain/services/sandbox_capture_store.dart';
@@ -114,7 +113,7 @@ SandboxCaptureStore sandboxCaptureStore(Ref ref) => SandboxCaptureStore();
@Riverpod(keepAlive: true)
Stream<SandboxCaptureError> sandboxCaptureErrors(Ref ref) {
final ctrl = ref.watch(sandboxCaptureControllerProvider.notifier);
return ctrl.errors;
return ctrl.errorStream();
}
/// Orchestrates sandbox capture browsing:
@@ -148,10 +147,7 @@ class SandboxCaptureController extends _$SandboxCaptureController {
static const _defaultMethod = 'singlefile';
static const _defaultVariant = 'balanced';
StreamSubscription<List<CaptureTabData>>? _captureTabSub;
StreamSubscription<RetryRequest>? _retrySub;
Stream<SandboxCaptureError> get errors => _errors.stream;
Stream<SandboxCaptureError> errorStream() => _errors.stream;
@override
void build() {
@@ -159,9 +155,9 @@ class SandboxCaptureController extends _$SandboxCaptureController {
fmc.SandboxCaptureHostEvents.setUp(_hostEvents);
final dao = ref.read(tabDatabaseProvider).captureTabDao;
_captureTabSub = dao.watchAll().listen(_onCaptureTabChange);
final captureTabSub = dao.watchAll().listen(_onCaptureTabChange);
_retrySub = ref
final retrySub = ref
.read(captureServerProvider)
.retryRequests
.listen(_onRetryRequest);
@@ -173,10 +169,8 @@ class SandboxCaptureController extends _$SandboxCaptureController {
// stream — otherwise an in-flight Pigeon event or DAO emit could
// race with handler cleanup, or _markFailed could try to add to a
// closed _errors sink.
final captureTabSub = _captureTabSub;
final retrySub = _retrySub;
if (captureTabSub != null) unawaited(captureTabSub.cancel());
if (retrySub != null) unawaited(retrySub.cancel());
unawaited(captureTabSub.cancel());
unawaited(retrySub.cancel());
fmc.SandboxCaptureHostEvents.setUp(null);
_hostEvents.controller = null;
unawaited(_errors.close());
@@ -4,7 +4,6 @@ import 'dart:typed_data';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:search_client/search_client.dart';
import 'package:weblibre/domain/services/generic_website.dart';
import 'package:weblibre/features/search_credits/domain/controllers/search_token_issuance_controller.dart';
@@ -1226,7 +1225,10 @@ class WebSearchScrollOffset extends _$WebSearchScrollOffset {
@override
double build() => 0;
void update(double offset) => state = offset;
void update(double offset) {
if (state == offset) return;
state = offset;
}
void reset() => state = 0;
}
@@ -5,7 +5,6 @@ import 'dart:ui';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:search_protocol/search_protocol.dart';
import 'package:search_client/search_client.dart';
import 'package:weblibre/core/filesystem.dart';
import 'package:weblibre/features/search_credits/domain/providers.dart';
@@ -361,14 +361,14 @@ class CaptureServer {
final tabIdJson = jsonEncode(tabId);
final captureIdJson = jsonEncode(captureId);
final html =
'<!doctype html><html><head><meta charset="utf-8">'
'<meta name="viewport" content="width=device-width,initial-scale=1">'
'<title>Capturing…</title><style>$_loaderStyles</style>'
'</head><body>$body'
'<script>window.__TAB_ID__=$tabIdJson;'
'window.__CAPTURE_ID__=$captureIdJson;</script>'
'<script>$_loaderScript</script>'
'</body></html>';
'''
<!doctype html><html><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Capturing</title><style>$_loaderStyles</style>
</head><body>$body
<script>window.__TAB_ID__=$tabIdJson;window.__CAPTURE_ID__=$captureIdJson;</script>
<script>$_loaderScript</script>
</body></html>''';
request.response.write(html);
await request.response.close();
}
@@ -510,35 +510,24 @@ class CaptureServer {
"base-uri 'none'; "
"form-action 'self'";
static const _loaderStyles =
'html,body{height:100%}'
'body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,'
'sans-serif;display:flex;align-items:center;justify-content:center;'
'min-height:100vh;margin:0;background:#0b0d10;color:#e6e8eb}'
'main{text-align:center;max-width:480px;padding:32px;opacity:0;'
'animation:fade .35s ease-out forwards}'
'@keyframes fade{to{opacity:1}}'
'.spinner{width:56px;height:56px;margin:0 auto 20px;border-radius:50%;'
'border:3px solid rgba(255,255,255,.08);border-top-color:#7aa2f7;'
'animation:spin 1s linear infinite}'
'@keyframes spin{to{transform:rotate(360deg)}}'
'h1{font-size:18px;font-weight:600;margin:0 0 8px;letter-spacing:.2px}'
'p{margin:0;color:#9aa1a8;font-size:14px;line-height:1.5}'
'.dots::after{display:inline-block;width:1.2em;text-align:left;'
'animation:dots 1.4s steps(4,end) infinite;content:""}'
'@keyframes dots{0%{content:""}25%{content:"."}50%{content:".."}'
'75%{content:"..."}100%{content:""}}'
'.error h1{color:#f7768e}'
'.actions{margin-top:20px;display:flex;gap:8px;justify-content:center}'
'.btn{padding:10px 16px;border:0;border-radius:10px;cursor:pointer;'
'font:inherit;font-weight:600;background:#7aa2f7;color:#0b0d10}'
'.btn.secondary{background:transparent;color:#9aa1a8;'
'border:1px solid rgba(255,255,255,.12)}'
'.hidden{display:none}'
'@media(prefers-color-scheme:light){body{background:#f5f6f8;'
'color:#1a1d22}.spinner{border-color:rgba(0,0,0,.08);'
'border-top-color:#3b82f6}.btn{background:#3b82f6;color:white}'
'p{color:#52606b}}';
static const _loaderStyles = '''
html,body{height:100%}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#0b0d10;color:#e6e8eb}
main{text-align:center;max-width:480px;padding:32px;opacity:0;animation:fade .35s ease-out forwards}
@keyframes fade{to{opacity:1}}
.spinner{width:56px;height:56px;margin:0 auto 20px;border-radius:50%;border:3px solid rgba(255,255,255,.08);border-top-color:#7aa2f7;animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
h1{font-size:18px;font-weight:600;margin:0 0 8px;letter-spacing:.2px}
p{margin:0;color:#9aa1a8;font-size:14px;line-height:1.5}
.dots::after{display:inline-block;width:1.2em;text-align:left;animation:dots 1.4s steps(4,end) infinite;content:""}
@keyframes dots{0%{content:""}25%{content:"."}50%{content:".."}75%{content:"..."}100%{content:""}}
.error h1{color:#f7768e}
.actions{margin-top:20px;display:flex;gap:8px;justify-content:center}
.btn{padding:10px 16px;border:0;border-radius:10px;cursor:pointer;font:inherit;font-weight:600;background:#7aa2f7;color:#0b0d10}
.btn.secondary{background:transparent;color:#9aa1a8;border:1px solid rgba(255,255,255,.12)}
.hidden{display:none}
@media(prefers-color-scheme:light){body{background:#f5f6f8;color:#1a1d22}.spinner{border-color:rgba(0,0,0,.08);border-top-color:#3b82f6}.btn{background:#3b82f6;color:white}p{color:#52606b}}
''';
// Inline script for the loader shell. Reads window.__TAB_ID__ and
// window.__CAPTURE_ID__, long-polls /loader/wait, then either redirects
@@ -548,7 +537,7 @@ class CaptureServer {
// service has gone away doesn't busy-loop forever — after the cap the
// loader gives up and shows the error pane with a Retry button (which
// resets the counter via showPending → poll()).
static const _loaderScript = r'''
static const _loaderScript = '''
(function(){
var pending=document.getElementById('pending');
var error=document.getElementById('error');
@@ -606,18 +595,19 @@ class CaptureServer {
String _loaderShellBody({required bool initialError}) {
final pendingClass = initialError ? 'hidden' : '';
final errorClass = initialError ? 'error' : 'error hidden';
return '<main id="pending" class="$pendingClass">'
'<div class="spinner"></div>'
'<h1>Capturing page<span class="dots"></span></h1>'
'<p>Saving an offline copy. This usually takes a few seconds.</p>'
'</main>'
'<main id="error" class="$errorClass">'
'<h1>Capture failed</h1>'
'<p>The page could not be saved. Check the notification for details.</p>'
'<div class="actions">'
'<button id="retry" class="btn" type="button">Retry</button>'
'</div>'
'</main>';
return '''
<main id="pending" class="$pendingClass">
<div class="spinner"></div>
<h1>Capturing page<span class="dots"></span></h1>
<p>Saving an offline copy. This usually takes a few seconds.</p>
</main>
<main id="error" class="$errorClass">
<h1>Capture failed</h1>
<p>The page could not be saved. Check the notification for details.</p>
<div class="actions">
<button id="retry" class="btn" type="button">Retry</button>
</div>
</main>''';
}
}
@@ -1,6 +1,5 @@
import 'dart:typed_data';
import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -211,7 +210,7 @@ class _ContentRow extends StatelessWidget {
/// instances on every state mutation). Equality is driven instead by scalar
/// signatures derived from those collections, which is enough to detect any
/// change a single result card cares about.
class _FooterState with FastEquatable {
class _FooterState {
final bool hasOpenSession;
final bool isFetching;
final bool isFetched;
@@ -260,14 +259,26 @@ class _FooterState with FastEquatable {
}
@override
List<Object?> get hashParameters => [
bool operator ==(Object other) {
return identical(this, other) ||
other is _FooterState &&
hasOpenSession == other.hasOpenSession &&
isFetching == other.isFetching &&
isFetched == other.isFetched &&
fetchError == other.fetchError &&
_capturingSignature == other._capturingSignature &&
_capturesSignature == other._capturesSignature;
}
@override
int get hashCode => Object.hash(
hasOpenSession,
isFetching,
isFetched,
fetchError,
_capturingSignature,
_capturesSignature,
];
);
}
class _FetchFooter extends ConsumerWidget {
@@ -151,10 +151,10 @@ bool _languageMatchesQuery(String resultLanguage, String? queryTag) {
final result = resultLanguage
.trim()
.toLowerCase()
.split(RegExp(r'[-_]'))
.split(RegExp('[-_]'))
.first;
final query = queryTag.trim().toLowerCase().split(RegExp(r'[-_]')).first;
final query = queryTag.trim().toLowerCase().split(RegExp('[-_]')).first;
if (result.isEmpty || query.isEmpty) return false;
+10 -10
View File
@@ -16,7 +16,6 @@ dependencies:
country_codes: ^3.3.0
country_flags: ^4.1.2
crypto: ^3.0.7
xxh3: ^1.2.0
cryptography_flutter: ^2.3.4
device_info_plus: ^12.4.0
drift: ^2.31.0
@@ -31,15 +30,15 @@ dependencies:
sdk: flutter
flutter_auto_size_text: ^5.0.0
flutter_hooks: ^0.21.3+1
flutter_secure_storage: ^10.1.0
flutter_markdown: ^0.7.7+1
flutter_material_design_icons: ^3.1.0+7447
flutter_mozilla_components:
path: ../../packages/flutter_mozilla_components
flutter_reorderable_grid_view: ^5.6.0
flutter_slidable: ^4.0.3
flutter_secure_storage: ^10.1.0
flutter_singbox_proxy:
path: ../../packages/flutter_singbox_proxy
flutter_slidable: ^4.0.3
flutter_svg: ^2.3.0
flutter_tor:
path: ../../packages/flutter_tor
@@ -67,13 +66,9 @@ dependencies:
path: ^1.9.1
path_provider: ^2.1.5
permission_handler: ^12.0.1
pretty_qr_code: ^3.6.0
privacypass_client:
path: ../../../weblibre_account/packages/privacypass_client
search_client:
path: ../../../weblibre_account/packages/search_client
search_protocol:
path: ../../../weblibre_account/packages/search_protocol
pretty_qr_code: ^3.6.0
qr_code_scanner_plus: ^2.1.1
quick_actions: ^1.1.0
riverpod: ^3.2.1
@@ -82,6 +77,10 @@ dependencies:
rxdart: ^0.28.0
saf_stream: ^2.0.0
saf_util: ^2.1.0
search_client:
path: ../../../weblibre_account/packages/search_client
search_protocol:
path: ../../../weblibre_account/packages/search_protocol
secure_archive:
git:
url: https://github.com/FaFre/secure_archive.git
@@ -97,8 +96,8 @@ dependencies:
speech_to_text_dialog:
path: ../../packages/speech_to_text_dialog
sqlite3: ^2.9.4
supabase: ^2.10.6
sqlite3_flutter_libs: "0.5.41"
supabase: ^2.10.6
synchronized: ^3.4.0+1
text_scroll: ^0.2.1
timeago: ^3.7.1
@@ -106,8 +105,9 @@ dependencies:
git:
url: https://github.com/FaFre/uri-to-file.git
url_launcher: ^6.3.2
web_socket_channel: ^3.0.3
uuid: ^4.5.3
web_socket_channel: ^3.0.3
xxh3: ^1.2.0
dev_dependencies:
build_runner: ^2.15.0
@@ -1,12 +1,11 @@
import 'dart:typed_data';
import 'dart:ui';
import 'package:drift/drift.dart' show Variable;
import 'package:drift/native.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:riverpod/riverpod.dart';
import 'package:weblibre/data/database/functions/lexo_rank_functions.dart';
import 'package:weblibre/domain/services/favicon_resolver.dart';
@@ -222,23 +221,26 @@ final _generatorResult = IconResult(
);
final _cachedSvgBytes = Uint8List.fromList(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">'
'<rect width="16" height="16" fill="#ff0000"/>'
'</svg>'
'''
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<rect width="16" height="16" fill="#ff0000"/>
</svg>'''
.codeUnits,
);
final _updatedSvgBytes = Uint8List.fromList(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">'
'<circle cx="8" cy="8" r="8" fill="#0000ff"/>'
'</svg>'
'''
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<circle cx="8" cy="8" r="8" fill="#0000ff"/>
</svg>'''
.codeUnits,
);
final _generatedSvgBytes = Uint8List.fromList(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">'
'<rect width="16" height="16" rx="3" fill="#5B8DEF"/>'
'<text x="8" y="11" text-anchor="middle" font-size="8" fill="#fff">W</text>'
'</svg>'
'''
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<rect width="16" height="16" rx="3" fill="#5B8DEF"/>
<text x="8" y="11" text-anchor="middle" font-size="8" fill="#fff">W</text>
</svg>'''
.codeUnits,
);
@@ -83,19 +83,21 @@ void main() {
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
await tester.pump();
expect(repository.refreshCount, 1);
expect(repository.refreshCount(), 1);
},
);
}
class _FakeSubscriptionRepository extends SubscriptionRepository {
int refreshCount = 0;
int _refreshCount = 0;
int refreshCount() => _refreshCount;
@override
Future<SubscriptionStatus> build() async => SubscriptionStatus.inactive;
@override
Future<void> refresh() async {
refreshCount++;
_refreshCount++;
}
}
@@ -18,8 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// ignore_for_file: avoid_redundant_argument_values
import 'dart:io';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
@@ -12,7 +12,6 @@ import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i5;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// ignore_for_file: avoid_redundant_argument_values, avoid_dynamic_calls
// ignore_for_file: avoid_dynamic_calls
import 'dart:convert';
import 'dart:io';
@@ -12,7 +12,6 @@ import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i5;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
@@ -28,7 +28,7 @@ void main() {
test(
'container assignments ensure sing-box profiles are running before syncing',
() async {
final profileId = SingboxProxyConnectionId('profile-1');
const profileId = SingboxProxyConnectionId('profile-1');
final assignedContainer = _container(
id: 'container-1',
contextId: 'context-a',
@@ -439,7 +439,6 @@ void main() {
UrlCleanerRule(
name: 'test',
data: UrlCleanerRuleData(
// ignore: avoid_redundant_argument_values
completeProvider: false,
urlPattern: r'^https?://example\.com',
rules: ['track'],
@@ -24,7 +24,7 @@ void main() {
color: Colors.blue,
orderKey: 'a',
metadata: ContainerMetadata.withDefaults(
proxyConnectionId: SingboxProxyConnectionId('missing-proxy'),
proxyConnectionId: const SingboxProxyConnectionId('missing-proxy'),
),
),
),
@@ -438,7 +438,7 @@ class _FakeContainerRepository extends ContainerRepository {
class _FakeProxyRoutingSettingsRepository
extends ProxyRoutingSettingsRepository {
ProxyRoutingSettings? _settings;
final ProxyRoutingSettings? _settings;
_FakeProxyRoutingSettingsRepository([this._settings]);
@@ -127,16 +127,18 @@ void main() {
test('buildGroupedParentTree creates correct hierarchy', () {
final registry = _makeRegistry();
final tree = registry.buildGroupedParentTree();
final defaultGroup = tree[UBlockAssetGroup.$default];
final adsGroup = tree[UBlockAssetGroup.ads];
expect(tree, contains(UBlockAssetGroup.$default));
expect(tree[UBlockAssetGroup.$default]!, contains('uBlock filters'));
expect(defaultGroup, contains('uBlock filters'));
expect(
tree[UBlockAssetGroup.$default]!['uBlock filters'],
defaultGroup?['uBlock filters'],
containsAll(['ublock-filters', 'ublock-privacy']),
);
expect(tree, contains(UBlockAssetGroup.ads));
expect(tree[UBlockAssetGroup.ads]!, contains(null));
expect(tree[UBlockAssetGroup.ads]![null], contains('easylist'));
expect(adsGroup, contains(null));
expect(adsGroup?[null], contains('easylist'));
expect(tree, contains(UBlockAssetGroup.regions));
});
@@ -34,7 +34,6 @@ void main() {
);
test('forces animations off when the app setting is enabled', () {
// ignore: avoid_redundant_argument_values
const mediaQuery = MediaQueryData(disableAnimations: false);
final result = applyAppMediaQueryOverrides(
@@ -3,9 +3,9 @@ import 'dart:typed_data';
import 'package:drift/native.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/data/database/functions/lexo_rank_functions.dart';
import 'package:weblibre/domain/services/favicon_resolver.dart';
@@ -128,7 +128,7 @@ void main() {
final class _NeverCalledResolver implements FaviconResolver {
@override
Future<FaviconResolveResult> resolve(Uri url, {int? proxyPort}) async {
Future<FaviconResolveResult> resolve(Uri url, {int? proxyPort}) {
throw UnimplementedError('cacheOnly should not hit the resolver');
}
}
@@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(const MyApp());
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatefulWidget {
@@ -14,6 +14,7 @@ dependencies:
flutter_mozilla_components:
path: ../
hooks_riverpod: ^3.3.1
dev_dependencies:
flutter_test:
@@ -69,8 +69,8 @@ export 'src/pigeons/gecko.g.dart'
GeckoDeleteBrowsingDataController,
GeckoEngineSettings,
GeckoFetchResponse,
GeckoProxySettings,
GeckoPref,
GeckoProxySettings,
GeckoPublicSuffixListApi,
GeckoPwaApi,
GeckoSitePermissionsApi,
@@ -6,11 +6,10 @@
// For more information about Flutter integration tests, please see
// https://flutter.dev/to/integration-testing
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(const MyApp());
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
@@ -15,6 +15,7 @@ environment:
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
cupertino_icons: ^1.0.8
flutter:
sdk: flutter
@@ -25,24 +26,20 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
hooks_riverpod: ^3.3.1
dev_dependencies:
integration_test:
sdk: flutter
flutter_lints: ^6.0.0
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -6,9 +6,8 @@
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_singbox_proxy_example/main.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('shows proxy runtime status label', (WidgetTester tester) async {
@@ -1,6 +1,6 @@
import 'dart:async';
import 'src/singbox_proxy_api.g.dart';
import 'package:flutter_singbox_proxy/src/singbox_proxy_api.g.dart';
export 'src/singbox_proxy_api.g.dart'
show
@@ -1,5 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_singbox_proxy/flutter_singbox_proxy.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('profile model keeps generic sing-box config boundary', () {
+2 -1
View File
@@ -6,10 +6,11 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_tor/flutter_tor.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:socks5_proxy/socks_client.dart';
void main() {
runApp(const MyApp());
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
@@ -19,6 +19,7 @@ dependencies:
sdk: flutter
flutter_tor:
path: ../
hooks_riverpod: ^3.3.1
# HTTP client for testing Tor connectivity
http: ^1.6.0
@@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:logger/logger.dart';
import 'package:simple_intent_receiver/simple_intent_receiver.dart';
@@ -34,7 +35,7 @@ void main() {
logger.d(data.action);
});
runApp(const MyApp());
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
@@ -17,6 +17,7 @@ environment:
dependencies:
flutter:
sdk: flutter
hooks_riverpod: ^3.3.1
logger: ^2.7.0
simple_intent_receiver:
# When depending on this package from a real application you should use:
@@ -1,10 +1,11 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:speech_to_text_dialog/speech_to_text_dialog.dart';
void main() {
runApp(const MyApp());
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatefulWidget {
@@ -17,6 +17,7 @@ environment:
dependencies:
flutter:
sdk: flutter
hooks_riverpod: ^3.3.1
speech_to_text_dialog:
# When depending on this package from a real application you should use: