implemented container site assignements & improved routing
This commit is contained in:
@@ -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:convert';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
@@ -24,6 +25,7 @@ import 'package:weblibre/features/geckoview/features/tabs/data/database/daos/con
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/database.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/definitions.drift.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/site_assignment.dart';
|
||||
|
||||
@DriftAccessor()
|
||||
class ContainerDao extends DatabaseAccessor<TabDatabase>
|
||||
@@ -115,7 +117,7 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
|
||||
);
|
||||
}
|
||||
|
||||
SingleSelectable<String> generateOrderKeyAfterTabId(
|
||||
SingleOrNullSelectable<String> generateOrderKeyAfterTabId(
|
||||
String? containerId,
|
||||
String tabId,
|
||||
) {
|
||||
@@ -134,4 +136,26 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
|
||||
tabId: tabId,
|
||||
);
|
||||
}
|
||||
|
||||
SingleSelectable<bool> isSiteAssignedToContainer(Uri uri) {
|
||||
return db.definitionsDrift.isSiteAssignedToContainer(uri: uri.origin);
|
||||
}
|
||||
|
||||
SingleSelectable<bool> areSitesAvailable(
|
||||
Iterable<Uri> origins,
|
||||
String ignoredContainerId,
|
||||
) {
|
||||
return db.definitionsDrift.areSitesAvailable(
|
||||
ignoreContainerId: ignoredContainerId,
|
||||
uriList: jsonEncode(origins.map((value) => value.origin).toList()),
|
||||
);
|
||||
}
|
||||
|
||||
SingleSelectable<String> siteAssignedContainerId(Uri uri) {
|
||||
return db.definitionsDrift.siteAssignedContainerId(uri: uri.origin);
|
||||
}
|
||||
|
||||
Selectable<SiteAssignment> allAssignedSites() {
|
||||
return db.definitionsDrift.allAssignedSites();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,11 +62,14 @@ class TabDao extends DatabaseAccessor<TabDatabase> with $TabDaoMixin {
|
||||
Future<String> _generateOrderKey({
|
||||
required Value<String?> parentId,
|
||||
required Value<String?> containerId,
|
||||
}) {
|
||||
}) async {
|
||||
if (parentId.value.isNotEmpty) {
|
||||
return db.containerDao
|
||||
.generateOrderKeyAfterTabId(containerId.value, parentId.value!)
|
||||
.getSingle();
|
||||
return await db.containerDao
|
||||
.generateOrderKeyAfterTabId(containerId.value, parentId.value!)
|
||||
.getSingleOrNull() ??
|
||||
await db.containerDao
|
||||
.generateLeadingOrderKey(containerId.value)
|
||||
.getSingle();
|
||||
} else {
|
||||
return db.containerDao
|
||||
.generateLeadingOrderKey(containerId.value)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:weblibre/data/database/converters/color.dart';
|
||||
import 'package:weblibre/data/database/converters/uri.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/site_assignment.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/tab_query_result.dart';
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/converters/container_metadata_converter.dart';
|
||||
|
||||
@@ -273,3 +274,41 @@ nextTabByOrderKey(:tab_id AS TEXT, :container_id AS TEXT OR NULL, :skip_containe
|
||||
SELECT next_tab_id
|
||||
FROM ranked_tabs
|
||||
WHERE id = :tab_id;
|
||||
|
||||
isSiteAssignedToContainer:
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM container
|
||||
CROSS JOIN json_each(container.metadata, '$.assignedSites')
|
||||
WHERE json_each.value = :uri
|
||||
) AS existing;
|
||||
|
||||
areSitesAvailable:
|
||||
SELECT NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM container
|
||||
CROSS JOIN json_each(container.metadata, '$.assignedSites')
|
||||
WHERE json_each.value IN (
|
||||
SELECT value
|
||||
FROM json_each(:uriList)
|
||||
) AND
|
||||
container.id IS NOT :ignore_container_id
|
||||
) AS existing;
|
||||
|
||||
siteAssignedContainerId:
|
||||
SELECT id
|
||||
FROM container
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM json_each(container.metadata, '$.assignedSites')
|
||||
WHERE value = :uri
|
||||
);
|
||||
|
||||
allAssignedSites WITH SiteAssignment:
|
||||
SELECT
|
||||
container.id,
|
||||
COALESCE(container.metadata ->> '$.contextualIdentity', 'general') AS contextualIdentity,
|
||||
value AS assigned_site
|
||||
FROM container
|
||||
CROSS JOIN json_each(container.metadata, '$.assignedSites')
|
||||
WHERE value IS NOT NULL;
|
||||
|
||||
@@ -13,6 +13,8 @@ import 'package:weblibre/data/database/converters/uri.dart' as i6;
|
||||
import 'package:drift/internal/modular.dart' as i7;
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/tab_query_result.dart'
|
||||
as i8;
|
||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/site_assignment.dart'
|
||||
as i9;
|
||||
|
||||
typedef $ContainerCreateCompanionBuilder =
|
||||
i3.ContainerCompanion Function({
|
||||
@@ -2277,6 +2279,50 @@ class DefinitionsDrift extends i7.ModularAccessor {
|
||||
).map((i0.QueryRow row) => row.readNullable<String>('next_tab_id'));
|
||||
}
|
||||
|
||||
i0.Selectable<bool> isSiteAssignedToContainer({String? uri}) {
|
||||
return customSelect(
|
||||
'SELECT EXISTS (SELECT 1 AS _c0 FROM container CROSS JOIN json_each(container.metadata, \'\$.assignedSites\')WHERE json_each.value = ?1) AS existing',
|
||||
variables: [i0.Variable<String>(uri)],
|
||||
readsFrom: {container},
|
||||
).map((i0.QueryRow row) => row.read<bool>('existing'));
|
||||
}
|
||||
|
||||
i0.Selectable<bool> areSitesAvailable({
|
||||
required String uriList,
|
||||
required String ignoreContainerId,
|
||||
}) {
|
||||
return customSelect(
|
||||
'SELECT NOT EXISTS (SELECT 1 AS _c0 FROM container CROSS JOIN json_each(container.metadata, \'\$.assignedSites\')WHERE json_each.value IN (SELECT value FROM json_each(?1)) AND container.id IS NOT ?2) AS existing',
|
||||
variables: [
|
||||
i0.Variable<String>(uriList),
|
||||
i0.Variable<String>(ignoreContainerId),
|
||||
],
|
||||
readsFrom: {container},
|
||||
).map((i0.QueryRow row) => row.read<bool>('existing'));
|
||||
}
|
||||
|
||||
i0.Selectable<String> siteAssignedContainerId({String? uri}) {
|
||||
return customSelect(
|
||||
'SELECT id FROM container WHERE EXISTS (SELECT 1 AS _c0 FROM json_each(container.metadata, \'\$.assignedSites\')WHERE value = ?1)',
|
||||
variables: [i0.Variable<String>(uri)],
|
||||
readsFrom: {container},
|
||||
).map((i0.QueryRow row) => row.read<String>('id'));
|
||||
}
|
||||
|
||||
i0.Selectable<i9.SiteAssignment> allAssignedSites() {
|
||||
return customSelect(
|
||||
'SELECT container.id, COALESCE(container.metadata ->> \'\$.contextualIdentity\', \'general\') AS contextualIdentity, value AS assigned_site FROM container CROSS JOIN json_each(container.metadata, \'\$.assignedSites\')WHERE value IS NOT NULL',
|
||||
variables: [],
|
||||
readsFrom: {container},
|
||||
).map(
|
||||
(i0.QueryRow row) => i9.SiteAssignment(
|
||||
id: row.read<String>('id'),
|
||||
contextualIdentity: row.read<String>('contextualIdentity'),
|
||||
assignedSite: row.readNullable<String>('assigned_site'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
i3.TabFts get tabFts => i7.ReadDatabaseContainer(
|
||||
attachedDatabase,
|
||||
).resultSet<i3.TabFts>('tab_fts');
|
||||
|
||||
@@ -21,6 +21,7 @@ import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:weblibre/data/database/converters/color.dart';
|
||||
import 'package:weblibre/data/database/converters/icon_data.dart';
|
||||
|
||||
part 'container_data.g.dart';
|
||||
@@ -72,11 +73,14 @@ class ContainerMetadata with FastEquatable {
|
||||
@JsonKey(defaultValue: false)
|
||||
final bool useProxy;
|
||||
|
||||
final List<Uri>? assignedSites;
|
||||
|
||||
ContainerMetadata({
|
||||
required this.iconData,
|
||||
required this.contextualIdentity,
|
||||
required this.authSettings,
|
||||
required this.useProxy,
|
||||
required this.assignedSites,
|
||||
});
|
||||
|
||||
ContainerMetadata.withDefaults({
|
||||
@@ -84,11 +88,13 @@ class ContainerMetadata with FastEquatable {
|
||||
String? contextualIdentity,
|
||||
ContainerAuthSettings? authSettings,
|
||||
bool? useProxy,
|
||||
List<Uri>? assignedSites,
|
||||
}) : this(
|
||||
iconData: iconData,
|
||||
contextualIdentity: contextualIdentity,
|
||||
authSettings: authSettings ?? ContainerAuthSettings.withDefaults(),
|
||||
useProxy: useProxy ?? false,
|
||||
assignedSites: assignedSites,
|
||||
);
|
||||
|
||||
factory ContainerMetadata.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -102,13 +108,16 @@ class ContainerMetadata with FastEquatable {
|
||||
contextualIdentity,
|
||||
authSettings,
|
||||
useProxy,
|
||||
assignedSites,
|
||||
];
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
@CopyWith()
|
||||
class ContainerData with FastEquatable {
|
||||
final String id;
|
||||
final String? name;
|
||||
@ColorJsonConverter()
|
||||
final Color color;
|
||||
final ContainerMetadata metadata;
|
||||
|
||||
@@ -119,10 +128,16 @@ class ContainerData with FastEquatable {
|
||||
ContainerMetadata? metadata,
|
||||
}) : metadata = metadata ?? ContainerMetadata.withDefaults();
|
||||
|
||||
factory ContainerData.fromJson(Map<String, dynamic> json) =>
|
||||
_$ContainerDataFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$ContainerDataToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [id, name, color, metadata];
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class ContainerDataWithCount extends ContainerData {
|
||||
final int? tabCount;
|
||||
|
||||
@@ -134,6 +149,12 @@ class ContainerDataWithCount extends ContainerData {
|
||||
required this.tabCount,
|
||||
});
|
||||
|
||||
factory ContainerDataWithCount.fromJson(Map<String, dynamic> json) =>
|
||||
_$ContainerDataWithCountFromJson(json);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() => _$ContainerDataWithCountToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [...super.hashParameters, tabCount];
|
||||
}
|
||||
|
||||
@@ -98,6 +98,8 @@ abstract class _$ContainerMetadataCWProxy {
|
||||
|
||||
ContainerMetadata useProxy(bool useProxy);
|
||||
|
||||
ContainerMetadata assignedSites(List<Uri>? assignedSites);
|
||||
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `ContainerMetadata(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
@@ -110,6 +112,7 @@ abstract class _$ContainerMetadataCWProxy {
|
||||
String? contextualIdentity,
|
||||
ContainerAuthSettings authSettings,
|
||||
bool useProxy,
|
||||
List<Uri>? assignedSites,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -134,6 +137,10 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
||||
@override
|
||||
ContainerMetadata useProxy(bool useProxy) => call(useProxy: useProxy);
|
||||
|
||||
@override
|
||||
ContainerMetadata assignedSites(List<Uri>? assignedSites) =>
|
||||
call(assignedSites: assignedSites);
|
||||
|
||||
@override
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `ContainerMetadata(...).copyWith.fieldName(value)`.
|
||||
@@ -147,6 +154,7 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
||||
Object? contextualIdentity = const $CopyWithPlaceholder(),
|
||||
Object? authSettings = const $CopyWithPlaceholder(),
|
||||
Object? useProxy = const $CopyWithPlaceholder(),
|
||||
Object? assignedSites = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return ContainerMetadata(
|
||||
iconData: iconData == const $CopyWithPlaceholder()
|
||||
@@ -166,6 +174,10 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
||||
? _value.useProxy
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: useProxy as bool,
|
||||
assignedSites: assignedSites == const $CopyWithPlaceholder()
|
||||
? _value.assignedSites
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: assignedSites as List<Uri>?,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -299,18 +311,23 @@ ContainerMetadata _$ContainerMetadataFromJson(Map<String, dynamic> json) =>
|
||||
json['authSettings'] as Map<String, dynamic>,
|
||||
),
|
||||
useProxy: json['useProxy'] as bool? ?? false,
|
||||
assignedSites: (json['assignedSites'] as List<dynamic>?)
|
||||
?.map((e) => Uri.parse(e as String))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ContainerMetadataToJson(ContainerMetadata instance) =>
|
||||
<String, dynamic>{
|
||||
'iconData': _$JsonConverterToJson<Map<String, dynamic>, IconData>(
|
||||
instance.iconData,
|
||||
const IconDataJsonConverter().toJson,
|
||||
),
|
||||
'contextualIdentity': instance.contextualIdentity,
|
||||
'authSettings': instance.authSettings.toJson(),
|
||||
'useProxy': instance.useProxy,
|
||||
};
|
||||
Map<String, dynamic> _$ContainerMetadataToJson(
|
||||
ContainerMetadata instance,
|
||||
) => <String, dynamic>{
|
||||
'iconData': _$JsonConverterToJson<Map<String, dynamic>, IconData>(
|
||||
instance.iconData,
|
||||
const IconDataJsonConverter().toJson,
|
||||
),
|
||||
'contextualIdentity': instance.contextualIdentity,
|
||||
'authSettings': instance.authSettings.toJson(),
|
||||
'useProxy': instance.useProxy,
|
||||
'assignedSites': instance.assignedSites?.map((e) => e.toString()).toList(),
|
||||
};
|
||||
|
||||
Value? _$JsonConverterFromJson<Json, Value>(
|
||||
Object? json,
|
||||
@@ -321,3 +338,44 @@ Json? _$JsonConverterToJson<Json, Value>(
|
||||
Value? value,
|
||||
Json? Function(Value value) toJson,
|
||||
) => value == null ? null : toJson(value);
|
||||
|
||||
ContainerData _$ContainerDataFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => ContainerData(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String?,
|
||||
color: const ColorJsonConverter().fromJson((json['color'] as num).toInt()),
|
||||
metadata: json['metadata'] == null
|
||||
? null
|
||||
: ContainerMetadata.fromJson(json['metadata'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ContainerDataToJson(ContainerData instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'color': const ColorJsonConverter().toJson(instance.color),
|
||||
'metadata': instance.metadata.toJson(),
|
||||
};
|
||||
|
||||
ContainerDataWithCount _$ContainerDataWithCountFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => ContainerDataWithCount(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String?,
|
||||
color: const ColorJsonConverter().fromJson((json['color'] as num).toInt()),
|
||||
metadata: json['metadata'] == null
|
||||
? null
|
||||
: ContainerMetadata.fromJson(json['metadata'] as Map<String, dynamic>),
|
||||
tabCount: (json['tabCount'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ContainerDataWithCountToJson(
|
||||
ContainerDataWithCount instance,
|
||||
) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'color': const ColorJsonConverter().toJson(instance.color),
|
||||
'metadata': instance.metadata.toJson(),
|
||||
'tabCount': instance.tabCount,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
|
||||
class SiteAssignment with FastEquatable {
|
||||
final String id;
|
||||
final String? contextualIdentity;
|
||||
final Uri assignedSite;
|
||||
|
||||
SiteAssignment({
|
||||
required this.id,
|
||||
required this.contextualIdentity,
|
||||
//Drift type casting issue should be non null
|
||||
required String? assignedSite,
|
||||
}) : assignedSite = Uri.parse(assignedSite!);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [id, contextualIdentity, assignedSite];
|
||||
}
|
||||
Reference in New Issue
Block a user