prepare for multiple apps
@@ -0,0 +1,108 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
assets/quotes/quotes.db
|
||||
assets/bangs/
|
||||
assets/preferences/builtin-bridges.json
|
||||
assets/preferences/url-shortener-list.json
|
||||
assets/preferences/url_cleaner_data.minify.json
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
.gradle/
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
**/android/key.properties
|
||||
*.jks
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/.last_build_id
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/ephemeral
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# macOS
|
||||
**/Flutter/ephemeral/
|
||||
**/Pods/
|
||||
**/macos/Flutter/GeneratedPluginRegistrant.swift
|
||||
**/macos/Flutter/ephemeral
|
||||
**/xcuserdata/
|
||||
|
||||
# Windows
|
||||
**/windows/flutter/ephemeral/
|
||||
**/windows/flutter/generated_plugin_registrant.cc
|
||||
**/windows/flutter/generated_plugin_registrant.h
|
||||
**/windows/flutter/generated_plugins.cmake
|
||||
|
||||
# Linux
|
||||
**/linux/flutter/ephemeral/
|
||||
**/linux/flutter/generated_plugin_registrant.cc
|
||||
**/linux/flutter/generated_plugin_registrant.h
|
||||
**/linux/flutter/generated_plugins.cmake
|
||||
c
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
|
||||
# Android Studio will place build artifacts here
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
@@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
|
||||
channel: "stable"
|
||||
|
||||
project_type: app
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
- platform: android
|
||||
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||
@@ -0,0 +1 @@
|
||||
../../COPYING
|
||||
@@ -0,0 +1 @@
|
||||
../../README.md
|
||||
@@ -0,0 +1,4 @@
|
||||
# Analysis options for the WebLibre app
|
||||
# Includes root configuration with app-specific overrides
|
||||
|
||||
include: ../../analysis_options.yaml
|
||||
@@ -0,0 +1,14 @@
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
GeneratedPluginRegistrant.java
|
||||
.cxx
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
@@ -0,0 +1,121 @@
|
||||
plugins {
|
||||
id "com.android.application"
|
||||
id "kotlin-android"
|
||||
id "org.jetbrains.kotlin.plugin.compose" version "2.3.10"
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id "dev.flutter.flutter-gradle-plugin"
|
||||
}
|
||||
|
||||
def localProperties = new Properties()
|
||||
def localPropertiesFile = rootProject.file("local.properties")
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.withReader("UTF-8") { reader ->
|
||||
localProperties.load(reader)
|
||||
}
|
||||
}
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = "1"
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty("flutter.versionName")
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = "1.0"
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "eu.weblibre.gecko"
|
||||
compileSdk = 36
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '17'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "eu.weblibre.gecko"
|
||||
// https://api.flutter.dev/flutter/widgets/AndroidView-class.html
|
||||
minSdkVersion = 26
|
||||
targetSdk = 36
|
||||
versionCode = flutterVersionCode.toInteger()
|
||||
versionName = flutterVersionName
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.glance:glance-appwidget:1.1.1'
|
||||
implementation 'com.google.android.material:material:1.13.0'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose true
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias System.getenv("KEY_ALIAS")
|
||||
keyPassword System.getenv("KEY_PASSWORD")
|
||||
storeFile System.getenv("KEY_PATH") ? file(System.getenv("KEY_PATH")) : null
|
||||
storePassword System.getenv("KEY_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.release
|
||||
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs)
|
||||
}
|
||||
}
|
||||
|
||||
bundle {
|
||||
// Profiler issues require us to temporarily package native code compressed to
|
||||
// match the previous APK packaging.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1865634
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
it.useLegacyPackaging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
useLegacyPackaging true
|
||||
}
|
||||
}
|
||||
|
||||
//Exclude dependenciesInfo for f-droid
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs.
|
||||
includeInApk = false
|
||||
// Disables dependency metadata when building Android App Bundles.
|
||||
includeInBundle = false
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
||||
//Override version code for f-droid
|
||||
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
|
||||
import com.android.build.OutputFile
|
||||
android.applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
|
||||
if (abiVersionCode != null) {
|
||||
output.versionCodeOverride = variant.versionCode * 10 + abiVersionCode
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
-dontobfuscate
|
||||
|
||||
####################################################################################################
|
||||
# Android and GeckoView built-ins
|
||||
####################################################################################################
|
||||
|
||||
-dontwarn android.**
|
||||
-dontwarn androidx.**
|
||||
-dontwarn com.google.**
|
||||
-dontwarn org.mozilla.geckoview.**
|
||||
|
||||
# Raptor now writes a *-config.yaml file to specify Gecko runtime settings (e.g. the profile dir). This
|
||||
# file gets deserialized into a DebugConfig object, which is why we need to keep this class
|
||||
# and its members.
|
||||
-keep class org.mozilla.gecko.util.DebugConfig { *; }
|
||||
|
||||
####################################################################################################
|
||||
# kotlinx.coroutines: use the fast service loader to init MainDispatcherLoader by including a rule
|
||||
# to rewrite this property to return true:
|
||||
# https://github.com/Kotlin/kotlinx.coroutines/blob/8c98180f177bbe4b26f1ed9685a9280fea648b9c/kotlinx-coroutines-core/jvm/src/internal/MainDispatchers.kt#L19
|
||||
#
|
||||
# R8 is expected to optimize the default implementation to avoid a performance issue but a bug in R8
|
||||
# as bundled with AGP v7.0.0 causes this optimization to fail so we use the fast service loader instead. See:
|
||||
# https://github.com/mozilla-mobile/focus-android/issues/5102#issuecomment-897854121
|
||||
#
|
||||
# The fast service loader appears to be as performant as the R8 optimization so it's not worth the
|
||||
# churn to later remove this workaround. If needed, the upstream fix is being handled in
|
||||
# https://issuetracker.google.com/issues/196302685
|
||||
####################################################################################################
|
||||
-assumenosideeffects class kotlinx.coroutines.internal.MainDispatcherLoader {
|
||||
boolean FAST_SERVICE_LOADER_ENABLED return true;
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
# REMOVE all Log messages except warnings and errors
|
||||
####################################################################################################
|
||||
-assumenosideeffects class android.util.Log {
|
||||
public static boolean isLoggable(java.lang.String, int);
|
||||
public static int v(...);
|
||||
public static int i(...);
|
||||
public static int d(...);
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
# Mozilla Application Services
|
||||
####################################################################################################
|
||||
|
||||
-keep class mozilla.appservices.** { *; }
|
||||
|
||||
####################################################################################################
|
||||
# ViewModels
|
||||
####################################################################################################
|
||||
|
||||
-keep class org.mozilla.fenix.**ViewModel { *; }
|
||||
|
||||
# Keep Android Lifecycle methods
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1596302
|
||||
-keep class androidx.lifecycle.** { *; }
|
||||
|
||||
-dontwarn java.beans.BeanInfo
|
||||
-dontwarn java.beans.FeatureDescriptor
|
||||
-dontwarn java.beans.IntrospectionException
|
||||
-dontwarn java.beans.Introspector
|
||||
-dontwarn java.beans.PropertyDescriptor
|
||||
|
||||
####################################################################################################
|
||||
# Checker Framework
|
||||
####################################################################################################
|
||||
|
||||
-dontwarn org.checkerframework.checker.nullness.qual.EnsuresNonNull
|
||||
-dontwarn org.checkerframework.checker.nullness.qual.EnsuresNonNullIf
|
||||
-dontwarn org.checkerframework.checker.nullness.qual.RequiresNonNull
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,289 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<!-- This is needed because the android.permission.CAMERA above automatically
|
||||
adds a requirements for camera hardware and we don't want add those restrictions -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.autofocus"
|
||||
android:required="false" />
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
|
||||
<!-- Fenix only uses this in debug manifest -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- Needed for uploading media files on devices with Android 13 and later. -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
|
||||
|
||||
<!-- Needed to interact with all apps installed on a device -->
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
|
||||
<!-- Needed to support Credential Manager with Android 14 and later. -->
|
||||
<uses-permission android:name="android.permission.CREDENTIAL_MANAGER_SET_ORIGIN" />
|
||||
<uses-permission android:name="android.permission.CREDENTIAL_MANAGER_QUERY_CANDIDATE_CREDENTIALS" />
|
||||
|
||||
|
||||
<application
|
||||
android:label="WebLibre"
|
||||
android:name=".MyApplication"
|
||||
android:icon="@mipmap/launcher_icon"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.NotificationActivity"
|
||||
android:theme="@style/Theme.AppCompat.Translucent"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTop" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="WebLibre"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTask"
|
||||
android:taskAffinity="eu.weblibre.gecko.task"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- We do want to ahndle "open with" by ourselfs. Prevents automatically pushing routes. -->
|
||||
<meta-data android:name="flutter_deeplinking_enabled" android:value="false" />
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<category android:name="android.intent.category.APP_BROWSER"/>
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="es.antonborri.home_widget.action.LAUNCH" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:mimeType="text/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.WEB_SEARCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Intent router: receives all VIEW intents and routes Custom Tab / PWA to
|
||||
ExternalAppBrowserActivity, regular VIEW intents to MainActivity -->
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.IntentReceiverActivity"
|
||||
android:theme="@style/Theme.AppCompat.Translucent"
|
||||
android:exported="true"
|
||||
android:excludeFromRecents="true"
|
||||
android:relinquishTaskIdentity="true"
|
||||
android:taskAffinity="">
|
||||
|
||||
<!-- Required exactly like this to appear in both default app choosers and when explicitly browsing -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="mozilla.components.pwa.category.SHORTCUT" />
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
<!-- like above but specifying additional schemes. Not qualified for app chooser. -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:mimeType="text/html" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="application/xhtml+xml" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:mimeType="application/pdf" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- PWA launch from home screen shortcut -->
|
||||
<intent-filter>
|
||||
<action android:name="mozilla.components.feature.pwa.VIEW_PWA" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Native activity for Custom Tabs and PWA browsing (non-Flutter) -->
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.ExternalAppBrowserActivity"
|
||||
android:exported="false"
|
||||
android:taskAffinity=""
|
||||
android:documentLaunchMode="always"
|
||||
android:autoRemoveFromRecents="false"
|
||||
android:theme="@style/ExternalAppBrowserTheme"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:resizeableActivity="true"
|
||||
android:persistableMode="persistNever"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.AuthCustomTabActivity"
|
||||
android:exported="false"
|
||||
android:taskAffinity=""
|
||||
android:autoRemoveFromRecents="false"
|
||||
android:theme="@style/ExternalAppBrowserTheme"
|
||||
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize|layoutDirection|smallestScreenSize|screenLayout"
|
||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.activities.AuthIntentReceiverActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/AddonsActivityTheme"
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:exported="false"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
|
||||
<activity
|
||||
android:theme="@style/AppTheme"
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonDetailsActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/mozac_feature_addons_addons" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.InstalledAddonDetailsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:parentActivityName="eu.weblibre.flutter_mozilla_components.addons.AddonsActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.PermissionsDetailsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.AddonInternalSettingsActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:exported="false"
|
||||
android:theme="@style/AppTheme" />
|
||||
|
||||
<activity
|
||||
android:name="eu.weblibre.flutter_mozilla_components.addons.WebExtensionActionPopupActivity"
|
||||
android:label="@string/mozac_feature_addons_addons"
|
||||
android:theme="@style/AppTheme"/>
|
||||
|
||||
<receiver
|
||||
android:name=".SearchBarWidgetReceiver"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/search_bar_widget_info" />
|
||||
</receiver>
|
||||
|
||||
|
||||
<service
|
||||
android:name="eu.weblibre.flutter_mozilla_components.services.CustomTabsService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="eu.weblibre.flutter_mozilla_components.services.DownloadService"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:exported="false" />
|
||||
|
||||
<service android:name="eu.weblibre.flutter_mozilla_components.services.MediaSessionService"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
android:exported="false" />
|
||||
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="*" />
|
||||
</intent>
|
||||
</queries>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<!-- This will show a lot of app actions in context menu when selecting text
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries> -->
|
||||
</manifest>
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
package eu.weblibre.gecko
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import io.flutter.embedding.android.FlutterFragmentActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.embedding.engine.FlutterEngineCache
|
||||
import io.flutter.embedding.engine.FlutterJNI
|
||||
import io.flutter.embedding.engine.dart.DartExecutor
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class MainActivity: FlutterFragmentActivity() {
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
}
|
||||
|
||||
private val TRIM_MEMORY_CHANNEL = "eu.weblibre.flutter_mozilla_components/trim_memory"
|
||||
private val ACTIVITY_CHANNEL = "eu.weblibre.gecko/activity"
|
||||
private val ENGINE_ID = "engine_id"
|
||||
private var trimMemoryChannel: MethodChannel? = null
|
||||
|
||||
private fun engineTag(engine: FlutterEngine?): String {
|
||||
return engine?.let { "0x${System.identityHashCode(it).toString(16)}" } ?: "null"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.d(TAG, "onCreate: savedInstanceState=${savedInstanceState != null}, " +
|
||||
"cachedEngine=${engineTag(FlutterEngineCache.getInstance().get(ENGINE_ID))}")
|
||||
|
||||
super.onCreate(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the FlutterEngine's native JNI layer is still attached.
|
||||
* Note: binaryMessenger.send() does NOT throw when JNI is detached — it just
|
||||
* logs a warning. We must use reflection to access FlutterJNI.isAttachedToJni().
|
||||
*/
|
||||
private fun isEngineNativeAlive(engine: FlutterEngine): Boolean {
|
||||
return try {
|
||||
val field = FlutterEngine::class.java.getDeclaredField("flutterJNI")
|
||||
field.isAccessible = true
|
||||
val jni = field.get(engine) as FlutterJNI
|
||||
jni.isAttached
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Could not check JNI attachment state: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
|
||||
trimMemoryChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, TRIM_MEMORY_CHANNEL)
|
||||
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, ACTIVITY_CHANNEL).setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"moveTaskToBack" -> {
|
||||
moveTaskToBack(true)
|
||||
result.success(null)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
Log.d(TAG, "onPause")
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d(TAG, "onDestroy: isFinishing=$isFinishing, " +
|
||||
"cachedEngine=${engineTag(FlutterEngineCache.getInstance().get(ENGINE_ID))}")
|
||||
super.onDestroy()
|
||||
|
||||
if (!isFinishing) {
|
||||
val cache = FlutterEngineCache.getInstance()
|
||||
val engine = cache.get(ENGINE_ID)
|
||||
if (engine != null) {
|
||||
Log.d(TAG, "onDestroy: system-initiated destroy, clearing stale engine")
|
||||
cache.remove(ENGINE_ID)
|
||||
try {
|
||||
engine.destroy()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Error destroying engine in onDestroy", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
super.onTrimMemory(level)
|
||||
trimMemoryChannel?.invokeMethod("onTrimMemory", level)
|
||||
}
|
||||
|
||||
override fun provideFlutterEngine(context: Context): FlutterEngine {
|
||||
val cache = FlutterEngineCache.getInstance()
|
||||
val cachedEngine = cache.get(ENGINE_ID)
|
||||
if (cachedEngine != null) {
|
||||
val isHealthy = try {
|
||||
cachedEngine.dartExecutor.isExecutingDart && isEngineNativeAlive(cachedEngine)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Cached engine health check failed", e)
|
||||
false
|
||||
}
|
||||
|
||||
if (isHealthy) {
|
||||
Log.d(TAG, "provideFlutterEngine: reusing cached engine ${engineTag(cachedEngine)}")
|
||||
return cachedEngine
|
||||
}
|
||||
|
||||
Log.w(TAG, "provideFlutterEngine: cached engine ${engineTag(cachedEngine)} is stale, creating fresh")
|
||||
cache.remove(ENGINE_ID)
|
||||
try {
|
||||
cachedEngine.destroy()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Error destroying stale engine", e)
|
||||
}
|
||||
}
|
||||
|
||||
val flutterEngine = FlutterEngine(context.applicationContext)
|
||||
flutterEngine.navigationChannel.setInitialRoute("/")
|
||||
flutterEngine.dartExecutor.executeDartEntrypoint(
|
||||
DartExecutor.DartEntrypoint.createDefault()
|
||||
)
|
||||
cache.put(ENGINE_ID, flutterEngine)
|
||||
|
||||
Log.d(TAG, "provideFlutterEngine: created new engine ${engineTag(flutterEngine)}")
|
||||
return flutterEngine
|
||||
}
|
||||
|
||||
override fun shouldDestroyEngineWithHost(): Boolean {
|
||||
return false // Keep engine alive when activity is destroyed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
package eu.weblibre.gecko
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import eu.weblibre.flutter_mozilla_components.ActiveProfile
|
||||
import eu.weblibre.flutter_mozilla_components.MegazordSetup
|
||||
|
||||
class MyApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
MegazordSetup.setupEarlyMainProcess()
|
||||
|
||||
// Resolve active profile EARLY so cold-start WorkManager workers
|
||||
// get profile-prefixed SharedPreferences
|
||||
ActiveProfile.resolveFromDisk(this)
|
||||
}
|
||||
|
||||
override fun getSharedPreferences(name: String, mode: Int): SharedPreferences {
|
||||
val pfx = ActiveProfile.prefix
|
||||
if (pfx != null && name in ActiveProfile.FXA_SHARED_PREFERENCE_NAMES) {
|
||||
return super.getSharedPreferences("${pfx}_$name", mode)
|
||||
}
|
||||
return super.getSharedPreferences(name, mode)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package eu.weblibre.gecko
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.GlanceModifier
|
||||
import androidx.glance.Image
|
||||
import androidx.glance.ImageProvider
|
||||
import androidx.glance.action.clickable
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
import androidx.glance.appwidget.GlanceAppWidgetReceiver
|
||||
import androidx.glance.appwidget.provideContent
|
||||
import androidx.glance.background
|
||||
import androidx.glance.layout.Alignment
|
||||
import androidx.glance.layout.Row
|
||||
import androidx.glance.layout.fillMaxWidth
|
||||
import androidx.glance.layout.padding
|
||||
import androidx.glance.layout.wrapContentHeight
|
||||
import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import androidx.glance.unit.ColorProvider
|
||||
import es.antonborri.home_widget.HomeWidgetLaunchIntent
|
||||
import android.net.Uri
|
||||
import es.antonborri.home_widget.actionStartActivity
|
||||
import androidx.core.net.toUri
|
||||
|
||||
class SearchBarGlanceWidget : GlanceAppWidget() {
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
provideContent {
|
||||
SearchBarContent(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchBarContent(context: Context) {
|
||||
Row(
|
||||
modifier = GlanceModifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.background(ImageProvider(R.drawable.search_text_field))
|
||||
.padding(12.dp)
|
||||
.clickable(onClick = actionStartActivity<MainActivity>(context,
|
||||
"widget://search".toUri())),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Start icon
|
||||
Image(
|
||||
provider = ImageProvider(R.drawable.icon_with_size),
|
||||
contentDescription = "Search icon",
|
||||
modifier = GlanceModifier.padding(end = 8.dp)
|
||||
)
|
||||
|
||||
// Search text
|
||||
Text(
|
||||
text = "Search with WebLibre...",
|
||||
style = TextStyle(
|
||||
color = ColorProvider(Color(0xFF848388)),
|
||||
fontSize = 16.sp
|
||||
),
|
||||
modifier = GlanceModifier.defaultWeight()
|
||||
)
|
||||
|
||||
// End icon (microphone)
|
||||
Image(
|
||||
provider = ImageProvider(R.drawable.mdi_icon_microphone_tint),
|
||||
contentDescription = "Microphone icon",
|
||||
modifier = GlanceModifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SearchBarWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = SearchBarGlanceWidget()
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 661 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#FFFFFF"/> <!-- Background color -->
|
||||
<corners android:radius="18dp"/> <!-- Corner radius -->
|
||||
</shape>
|
||||
@@ -0,0 +1,10 @@
|
||||
<!-- res/drawable/icon_with_size.xml -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:width="24dp"
|
||||
android:height="24dp">
|
||||
<bitmap
|
||||
android:src="@mipmap/launcher_icon"
|
||||
android:width="24dp"
|
||||
android:height="24dp"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/brain.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M21.33,12.91C21.42,14.46 20.71,15.95 19.44,16.86L20.21,18.35C20.44,18.8 20.47,19.33 20.27,19.8C20.08,20.27 19.69,20.64 19.21,20.8L18.42,21.05C18.25,21.11 18.06,21.14 17.88,21.14C17.37,21.14 16.89,20.91 16.56,20.5L14.44,18C13.55,17.85 12.71,17.47 12,16.9C11.5,17.05 11,17.13 10.5,17.13C9.62,17.13 8.74,16.86 8,16.34C7.47,16.5 6.93,16.57 6.38,16.56C5.59,16.57 4.81,16.41 4.08,16.11C2.65,15.47 1.7,14.07 1.65,12.5C1.57,11.78 1.69,11.05 2,10.39C1.71,9.64 1.68,8.82 1.93,8.06C2.3,7.11 3,6.32 3.87,5.82C4.45,4.13 6.08,3 7.87,3.12C9.47,1.62 11.92,1.46 13.7,2.75C14.12,2.64 14.56,2.58 15,2.58C16.36,2.55 17.65,3.15 18.5,4.22C20.54,4.75 22,6.57 22.08,8.69C22.13,9.8 21.83,10.89 21.22,11.82C21.29,12.18 21.33,12.54 21.33,12.91M16.33,11.5C16.9,11.57 17.35,12 17.35,12.57A1,1 0 0,1 16.35,13.57H15.72C15.4,14.47 14.84,15.26 14.1,15.86C14.35,15.95 14.61,16 14.87,16.07C20,16 19.4,12.87 19.4,12.82C19.34,11.39 18.14,10.27 16.71,10.33A1,1 0 0,1 15.71,9.33A1,1 0 0,1 16.71,8.33C17.94,8.36 19.12,8.82 20.04,9.63C20.09,9.34 20.12,9.04 20.12,8.74C20.06,7.5 19.5,6.42 17.25,6.21C16,3.25 12.85,4.89 12.85,5.81V5.81C12.82,6.04 13.06,6.53 13.1,6.56A1,1 0 0,1 14.1,7.56C14.1,8.11 13.65,8.56 13.1,8.56V8.56C12.57,8.54 12.07,8.34 11.67,8C11.19,8.31 10.64,8.5 10.07,8.56V8.56C9.5,8.61 9.03,8.21 9,7.66C8.92,7.1 9.33,6.61 9.88,6.56C10.04,6.54 10.82,6.42 10.82,5.79V5.79C10.82,5.13 11.07,4.5 11.5,4C10.58,3.75 9.59,4.08 8.59,5.29C6.75,5 6,5.25 5.45,7.2C4.5,7.67 4,8 3.78,9C4.86,8.78 5.97,8.87 7,9.25C7.5,9.44 7.78,10 7.59,10.54C7.4,11.06 6.82,11.32 6.3,11.13C5.57,10.81 4.75,10.79 4,11.07C3.68,11.34 3.68,11.9 3.68,12.34C3.68,13.08 4.05,13.77 4.68,14.17C5.21,14.44 5.8,14.58 6.39,14.57C6.24,14.31 6.11,14.04 6,13.76C5.81,13.22 6.1,12.63 6.64,12.44C7.18,12.25 7.77,12.54 7.96,13.08C8.36,14.22 9.38,15 10.58,15.13C11.95,15.06 13.17,14.25 13.77,13C14,11.62 15.11,11.5 16.33,11.5M18.33,18.97L17.71,17.67L17,17.83L18,19.08L18.33,18.97M13.68,10.36C13.7,9.83 13.3,9.38 12.77,9.33C12.06,9.29 11.37,9.53 10.84,10C10.27,10.58 9.97,11.38 10,12.19A1,1 0 0,0 11,13.19C11.57,13.19 12,12.74 12,12.19C12,11.92 12.07,11.65 12.23,11.43C12.35,11.33 12.5,11.28 12.66,11.28C13.21,11.31 13.68,10.9 13.68,10.36Z" /></vector>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/microphone.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#848388" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z" /></vector>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/search_web.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M15.5,14L20.5,19L19,20.5L14,15.5V14.71L13.73,14.43C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.43,13.73L14.71,14H15.5M9.5,4.5L8.95,4.53C8.71,5.05 8.34,5.93 8.07,7H10.93C10.66,5.93 10.29,5.05 10.05,4.53C9.87,4.5 9.69,4.5 9.5,4.5M13.83,7C13.24,5.97 12.29,5.17 11.15,4.78C11.39,5.31 11.7,6.08 11.93,7H13.83M5.17,7H7.07C7.3,6.08 7.61,5.31 7.85,4.78C6.71,5.17 5.76,5.97 5.17,7M4.5,9.5C4.5,10 4.58,10.53 4.73,11H6.87L6.75,9.5L6.87,8H4.73C4.58,8.47 4.5,9 4.5,9.5M14.27,11C14.42,10.53 14.5,10 14.5,9.5C14.5,9 14.42,8.47 14.27,8H12.13C12.21,8.5 12.25,9 12.25,9.5C12.25,10 12.21,10.5 12.13,11H14.27M7.87,8L7.75,9.5L7.87,11H11.13C11.21,10.5 11.25,10 11.25,9.5C11.25,9 11.21,8.5 11.13,8H7.87M9.5,14.5C9.68,14.5 9.86,14.5 10.03,14.47C10.28,13.95 10.66,13.07 10.93,12H8.07C8.34,13.07 8.72,13.95 8.97,14.47L9.5,14.5M13.83,12H11.93C11.7,12.92 11.39,13.69 11.15,14.22C12.29,13.83 13.24,13.03 13.83,12M5.17,12C5.76,13.03 6.71,13.83 7.85,14.22C7.61,13.69 7.3,12.92 7.07,12H5.17Z" /></vector>
|
||||
@@ -0,0 +1 @@
|
||||
<!-- drawable/text.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24"><path android:fillColor="#000000" android:pathData="M21,6V8H3V6H21M3,18H12V16H3V18M3,13H21V11H3V13Z" /></vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#333237"/> <!-- Background color -->
|
||||
<corners android:radius="32dp"/> <!-- Rounded corners -->
|
||||
<stroke android:width="1dp" android:color="#BDBDBD"/> <!-- Border -->
|
||||
</shape>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_foreground"
|
||||
android:inset="25%" />
|
||||
</foreground>
|
||||
<monochrome>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_monochrome"
|
||||
android:inset="25%" />
|
||||
</monochrome>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<resources>
|
||||
<declare-styleable name="AppWidgetAttrs">
|
||||
<attr name="appWidgetPadding" format="dimension" />
|
||||
<attr name="appWidgetInnerRadius" format="dimension" />
|
||||
<attr name="appWidgetRadius" format="dimension" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="light_blue_50">#FFE1F5FE</color>
|
||||
<color name="light_blue_200">#FF81D4FA</color>
|
||||
<color name="light_blue_600">#FF039BE5</color>
|
||||
<color name="light_blue_900">#FF01579B</color>
|
||||
<color name="ic_launcher_background">#FF000000</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<!--
|
||||
Refer to App Widget Documentation for margin information
|
||||
http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
|
||||
-->
|
||||
<dimen name="widget_margin">0dp</dimen>
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.WebLibreWidget.AppWidget.Container" parent="android:Widget">
|
||||
<item name="android:id">@android:id/background</item>
|
||||
<item name="android:background">?android:attr/colorBackground</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.WebLibreWidget.AppWidget.InnerView" parent="android:Widget">
|
||||
<item name="android:background">?android:attr/colorBackground</item>
|
||||
<item name="android:textColor">?android:attr/textColorPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
|
||||
</style>
|
||||
|
||||
<style name="ExternalAppBrowserTheme" parent="Theme.Material3.DynamicColors.DayNight.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="AddonsActivityTheme" parent="AppTheme">
|
||||
<item name="mozac_primary_text_color">@color/photonDarkGrey90</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,31 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.WebLibreWidget" parent="@style/Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
<style name="Theme.WebLibreWidget.AppWidgetContainerParent" parent="@android:style/Theme.DeviceDefault">
|
||||
<!-- Radius of the outer bound of widgets to make the rounded corners -->
|
||||
<item name="appWidgetRadius">16dp</item>
|
||||
<!--
|
||||
Radius of the inner view's bound of widgets to make the rounded corners.
|
||||
It needs to be 8dp or less than the value of appWidgetRadius
|
||||
-->
|
||||
<item name="appWidgetInnerRadius">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.WebLibreWidget.AppWidgetContainer" parent="Theme.WebLibreWidget.AppWidgetContainerParent">
|
||||
<!-- Apply padding to avoid the content of the widget colliding with the rounded corners -->
|
||||
<item name="appWidgetPadding">16dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/glance_default_loading_layout"
|
||||
android:resizeMode="none"
|
||||
android:targetCellWidth="5"
|
||||
android:targetCellHeight="1"
|
||||
android:widgetCategory="home_screen" />
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- The INTERNET permission is required for development. Specifically,
|
||||
the Flutter tool needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
</manifest>
|
||||
@@ -0,0 +1,37 @@
|
||||
//Required by background_fetch build
|
||||
ext {
|
||||
compileSdkVersion = 36
|
||||
targetSdkVersion = 36
|
||||
}
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '2.3.10'
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
//Required by background_fetch build
|
||||
maven { url "${project(':background_fetch').projectDir}/libs" }
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.buildDir = "../build"
|
||||
subprojects {
|
||||
project.buildDir = "${rootProject.buildDir}/${project.name}"
|
||||
afterEvaluate {
|
||||
if (it.hasProperty("android")) {
|
||||
android {
|
||||
compileSdkVersion 36
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
subprojects {
|
||||
project.evaluationDependsOn(":app")
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
|
||||
@@ -0,0 +1,25 @@
|
||||
pluginManagement {
|
||||
def flutterSdkPath = {
|
||||
def properties = new Properties()
|
||||
file("local.properties").withInputStream { properties.load(it) }
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
return flutterSdkPath
|
||||
}()
|
||||
|
||||
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||
id "com.android.application" version '8.13.2' apply false
|
||||
id "org.jetbrains.kotlin.android" version "2.3.10" apply false
|
||||
}
|
||||
|
||||
include ":app"
|
||||
@@ -0,0 +1 @@
|
||||
../../../../font/src/assets/generated-icons/WebLibre.ttf
|
||||
|
After Width: | Height: | Size: 23 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg width="147" height="106" viewBox="0 0 147 106" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M146.187 4.16996C146.608 2.00885 144.953 0 142.751 0H116.196C114.521 0 113.081 1.18632 112.761 2.83005L93.5631 101.268C93.1417 103.429 94.7966 105.438 96.9984 105.438H123.554C125.229 105.438 126.669 104.251 126.989 102.608L146.187 4.16996Z" fill="#A7A7A7"/>
|
||||
<foreignObject x="20.7803" y="18.1592" width="106.094" height="90.7813"><div xmlns="http://www.w3.org/1999/xhtml" style="backdrop-filter:blur(1.75px);height:100%;width:100%"></div></foreignObject><path data-figma-bg-blur-radius="3.50001" d="M106.53 105.441C106.53 71.9717 98.6042 38.5029 74.1832 38.5029C49.7622 38.5029 41.124 71.3155 41.124 105.441" stroke="#FBDC6B" stroke-opacity="0.8" stroke-width="33.6875"/>
|
||||
<path d="M0.813229 4.16996C0.391767 2.00885 2.04669 0 4.24852 0H30.8042C32.4789 0 33.9189 1.18632 34.2395 2.83005L53.4369 101.268C53.8583 103.429 52.2034 105.438 50.0016 105.438H23.4459C21.7712 105.438 20.3312 104.251 20.0106 102.608L0.813229 4.16996Z" fill="#9C83F8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
viewBox="0 0 128 128"
|
||||
height="128"
|
||||
width="128">
|
||||
<g
|
||||
style="display:inline;opacity:1">
|
||||
<g
|
||||
style="fill:#800000;fill-opacity:1;stroke:#ffffff;stroke-width:1.62100744;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
|
||||
transform="matrix(0.6778654,0,0,0.56141828,-241.07537,-247.27712)" />
|
||||
<g
|
||||
transform="matrix(-0.6945203,0,0,0.56109687,375.02964,-247.42947)"
|
||||
style="fill:#800000;fill-opacity:1;stroke:#ffffff;stroke-width:1.60191178000000001;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;stroke-linejoin:round">
|
||||
<path
|
||||
d="m 447.83376,669.09921 c -80.63119,-57.03115 -80.63119,-57.03115 -80.63119,-199.60903 34.55623,0 46.07497,0 80.63119,-28.51558 m 0,228.12461 c 80.6312,-57.03115 80.6312,-57.03115 80.6312,-199.60903 -34.55623,0 -46.07497,0 -80.6312,-28.51558"
|
||||
style="fill:#800000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1.60191178;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
transform="translate(-17.872548,-17.32535)"
|
||||
style="display:inline">
|
||||
<ellipse
|
||||
transform="matrix(1.3333335,0,0,1.3333334,-42.290862,-31.108461)"
|
||||
ry="12"
|
||||
rx="11.999999"
|
||||
cy="81.325356"
|
||||
cx="102.12254"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:5.99999952;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline;" />
|
||||
<g
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.99999996999999996;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="scale(1.0018026,0.99820067)">
|
||||
<path
|
||||
d="m 81.72523,81.471945 c 0,11.019828 -4.991003,16.028841 -15.97121,16.028841 -10.980207,0 -15.97121,-5.009013 -15.97121,-16.028841 l 0,-24.043262 7.985605,0 0,24.043262 c 0,7.012618 0.9982,8.014421 7.985605,8.014421 6.987404,0 7.985605,-1.001803 7.985605,-8.014421 l 0,-24.043262 7.985605,0 z"
|
||||
style="fill:#ffffff;stroke:#ffffff;stroke-width:0;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1 @@
|
||||
../../../../CHANGELOG.md
|
||||
@@ -0,0 +1,228 @@
|
||||
# WebLibre End User License Agreement (EULA)
|
||||
|
||||
**Version 1.2**
|
||||
**Last Updated:** 2026-03-15
|
||||
|
||||
**Licensor:**
|
||||
OnDevice UG (haftungsbeschränkt)
|
||||
Romerstrasse 21
|
||||
D-71540 Murrhardt
|
||||
Germany
|
||||
info@ondevice.eu
|
||||
|
||||
---
|
||||
|
||||
## 1. Scope and Acceptance
|
||||
|
||||
This End User License Agreement ("Agreement") governs your use of the WebLibre application, related software, updates, branding, websites, and any related services that OnDevice UG may make available in connection with WebLibre (together, the "Software").
|
||||
|
||||
By downloading, installing, accessing, or using the Software, you agree to this Agreement. If you do not agree, do not use the Software.
|
||||
|
||||
---
|
||||
|
||||
## 2. Open Source License
|
||||
|
||||
The WebLibre source code is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). The AGPL-3.0 governs your rights to use, copy, modify, and redistribute the source code and any covered versions of the Software. The full license text is available at: https://www.gnu.org/licenses/agpl-3.0.html
|
||||
|
||||
If this Agreement conflicts with the AGPL-3.0 regarding rights granted under that license, the AGPL-3.0 controls to the extent of that conflict.
|
||||
|
||||
WebLibre incorporates third-party open source components, each subject to its own license terms. Key components include (non-exhaustive):
|
||||
|
||||
- **GeckoView / Mozilla Android Components** — MPL-2.0 (Mozilla Public License 2.0)
|
||||
- **Tor libraries** — various open source licenses
|
||||
- **ClearURLs catalog** — LGPL-3.0
|
||||
- **Flutter framework** — BSD-3-Clause
|
||||
|
||||
A complete list of components and their licenses is available in the source code repository and within the app's about/licenses screen.
|
||||
|
||||
The WebLibre name, logo, and branding are not licensed under the AGPL-3.0 except to the limited extent required by applicable law.
|
||||
|
||||
---
|
||||
|
||||
## 3. Availability and Fees
|
||||
|
||||
The Software is currently made available without a license fee, unless otherwise stated by OnDevice UG or a distribution platform. OnDevice UG may change how the Software or related services are offered in the future, subject to applicable law and any mandatory consumer information obligations.
|
||||
|
||||
---
|
||||
|
||||
## 4. Acceptable Use
|
||||
|
||||
You agree to use the Software only in compliance with applicable law and not to:
|
||||
|
||||
- use the Software for unlawful, harmful, fraudulent, or abusive purposes;
|
||||
- interfere with, disrupt, or attempt unauthorized access to websites, services, networks, or devices;
|
||||
- use the Software to violate the rights of others, including intellectual property, privacy, or publicity rights;
|
||||
- misrepresent the origin of the Software or remove notices that must be preserved under applicable license terms.
|
||||
|
||||
You are solely responsible for your use of the Software, the content you access or transmit, the services you connect to, and the configuration choices you make.
|
||||
|
||||
---
|
||||
|
||||
## 5. Privacy and Security
|
||||
|
||||
Our privacy practices are described in the Privacy Policy available at https://docs.weblibre.eu/weblibre/privacy-policy.html.
|
||||
|
||||
The Software may include privacy, security, filtering, backup, synchronization, routing, or other protective features. These features are provided on a best-effort basis only. They are not guarantees of anonymity, confidentiality, security, legality, availability, or fitness for any particular purpose. Their effectiveness depends on factors outside OnDevice UG's control, including device security, third-party services, network conditions, regional restrictions, user settings, and modified builds.
|
||||
|
||||
If you need assured confidentiality, anonymity, uptime, compliance, retention, or incident response, you should not rely solely on the Software.
|
||||
|
||||
---
|
||||
|
||||
## 6. Third-Party Content and Services
|
||||
|
||||
The Software can access or interact with third-party websites, content, services, extensions, accounts, network providers, and infrastructure. OnDevice UG does not control and is not responsible for third-party content, terms, privacy practices, functionality, legality, availability, security, or changes.
|
||||
|
||||
Your use of third-party offerings is solely between you and the relevant third party. Third-party offerings may fail, change, be removed, be blocked, or become unavailable at any time without notice.
|
||||
|
||||
### 6.1 Browser Extensions
|
||||
|
||||
WebLibre supports Firefox-compatible browser extensions (WebExtensions). Extensions may be obtained from Mozilla Add-ons (addons.mozilla.org), configured add-on collections, or installed locally from `.xpi` files. Third-party extensions are not developed, reviewed, or endorsed by OnDevice UG. Installing extensions may grant them access to your browsing data and other information as described in their respective permission dialogs. You are responsible for reviewing extension permissions before installation.
|
||||
|
||||
### 6.2 Search Engines
|
||||
|
||||
When you perform a search, your query is sent directly to your selected search engine provider (e.g., DuckDuckGo, Brave Search, Kagi, Qwant, or others you configure). WebLibre does not intermediate or log these queries.
|
||||
|
||||
### 6.3 Mozilla Services
|
||||
|
||||
WebLibre supports optional integration with Mozilla services, including Firefox Sync via Mozilla accounts. When enabled, data is synchronized with Mozilla's servers under Mozilla's privacy policy. WebLibre also supports custom server overrides, in which case synchronization data is transmitted to the operator of the server endpoints you configure.
|
||||
|
||||
### 6.4 Safe Browsing
|
||||
|
||||
When Google Safe Browsing or similar remote reputation features are active, URL-derived lookup data may be transmitted to Google LLC or the relevant provider to detect dangerous websites or downloads.
|
||||
|
||||
### 6.5 DNS Services
|
||||
|
||||
WebLibre supports DNS-over-HTTPS (DoH) and custom DNS resolvers. When enabled, DNS queries are sent to the selected provider instead of your default network resolver.
|
||||
|
||||
### 6.6 Tor Network
|
||||
|
||||
WebLibre integrates optional Tor routing for anonymous browsing. OnDevice UG is not affiliated with The Tor Project. Your use of the Tor network is governed by The Tor Project's policies (https://www.torproject.org/about/privacy/). You are responsible for ensuring that your use of Tor complies with applicable laws in your jurisdiction.
|
||||
|
||||
### 6.7 URL Services
|
||||
|
||||
WebLibre may use the `unshorten.me` service to expand shortened URLs and the ClearURLs catalog to strip tracking parameters from URLs.
|
||||
|
||||
### 6.8 General
|
||||
|
||||
OnDevice UG is not liable for damages caused by third-party content, services, or extensions, except to the extent that such liability arises from OnDevice UG's own intentional misconduct or gross negligence, or cannot be excluded under mandatory law.
|
||||
|
||||
---
|
||||
|
||||
## 7. Intellectual Property and Feedback
|
||||
|
||||
Except for rights granted under applicable open source licenses, OnDevice UG and its licensors retain all rights, title, and interest in the Software, including branding and other proprietary elements.
|
||||
|
||||
You retain rights in your own content. If you voluntarily provide suggestions, ideas, or feedback, you grant OnDevice UG a non-exclusive, worldwide, royalty-free, perpetual, irrevocable license to use, reproduce, modify, publish, distribute, and otherwise exploit that feedback for any purpose without compensation or obligation.
|
||||
|
||||
---
|
||||
|
||||
## 8. Your Data, Backups, and Local Storage
|
||||
|
||||
The Software may store data locally on your device and may allow import, export, backup, restore, or synchronization features. You are solely responsible for maintaining backups, protecting exported files, safeguarding credentials and passwords, and verifying the integrity of imported or restored data.
|
||||
|
||||
OnDevice UG is not responsible for loss of data, corruption, failed restores, forgotten passwords, incompatible backups, or unauthorized access to files stored on your device or in locations you choose.
|
||||
|
||||
---
|
||||
|
||||
## 9. Updates, Changes, and Support
|
||||
|
||||
OnDevice UG may release updates, patches, or new versions, but has no obligation to do so. OnDevice UG may add, modify, suspend, restrict, or remove any feature, service, integration, or compatibility at any time. Where reasonably practicable, OnDevice UG will provide notice of material changes through the source code repository or app update notes. As the Software is provided free of charge and is open-source, no compensation or continued availability of specific features is owed.
|
||||
|
||||
The Software may include experimental, preview, beta, or unsupported features. Those features may contain errors, may be modified or removed without notice, and are not subject to the same level of testing or support as stable features. Use of experimental features is at your own risk, subject to the limitations of liability in Section 11.
|
||||
|
||||
No support, maintenance, uptime, compatibility, or continued availability is promised.
|
||||
|
||||
---
|
||||
|
||||
## 10. Disclaimer of Warranties
|
||||
|
||||
**TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS IS" AND "AS AVAILABLE," WITH ALL FAULTS AND WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, ACCURACY, QUIET ENJOYMENT, SECURITY, AVAILABILITY, OR RESULTS.**
|
||||
|
||||
OnDevice UG does not warrant that the Software will be uninterrupted, error-free, secure, compatible with your device or environment, free from harmful components, or that defects will be corrected.
|
||||
|
||||
---
|
||||
|
||||
## 11. Limitation of Liability
|
||||
|
||||
**TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, ONDEVICE UG SHALL NOT BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR ANY LOSS OF PROFITS, REVENUE, BUSINESS, GOODWILL, USE, DATA, CONTENT, SAVINGS, OR OPPORTUNITY, ARISING OUT OF OR RELATING TO THE SOFTWARE OR THIS AGREEMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.**
|
||||
|
||||
To the maximum extent permitted by applicable law, OnDevice UG shall also not be liable for damages arising from or relating to:
|
||||
|
||||
- third-party websites, services, extensions, infrastructure, or content;
|
||||
- your device, network, operating system, firmware, ROM, security posture, or user configuration;
|
||||
- censorship, filtering, blocking, outages, routing failures, DNS failures, certificate issues, or other internet or infrastructure problems;
|
||||
- unauthorized access, malware, data loss, corruption, failed backups, failed restores, or loss of credentials;
|
||||
- changes to or discontinuation of the Software or any third-party dependency.
|
||||
|
||||
If liability cannot be excluded, it is limited to the minimum amount required by law. Nothing in this Agreement excludes or limits liability that cannot lawfully be excluded or limited, including liability for intent (Vorsatz), gross negligence (grobe Fahrlässigkeit) where required, or mandatory liability under applicable product liability law (Produkthaftungsgesetz).
|
||||
|
||||
---
|
||||
|
||||
## 12. Force Majeure
|
||||
|
||||
OnDevice UG shall not be liable for any failure or delay in performance resulting from causes beyond its reasonable control, including but not limited to natural disasters, war, government actions, pandemics, network or infrastructure failures, or third-party service disruptions. This does not affect liability under mandatory law, including Section 11 of this Agreement.
|
||||
|
||||
---
|
||||
|
||||
## 13. Indemnification
|
||||
|
||||
To the extent permitted by applicable law, you agree to defend, indemnify, and hold harmless OnDevice UG and its affiliates, officers, employees, and contractors from and against claims, losses, liabilities, damages, judgments, and expenses (including reasonable attorneys' fees) arising out of or related to:
|
||||
|
||||
- your intentional misuse of the Software;
|
||||
- content you intentionally create, distribute, or transmit using the Software;
|
||||
- your intentional violation of this Agreement or applicable law;
|
||||
- your intentional violation of any third-party rights.
|
||||
|
||||
This section does not apply to the extent a claim results from OnDevice UG's own misconduct or negligence, or to the extent that it would be unenforceable under mandatory consumer protection law.
|
||||
|
||||
---
|
||||
|
||||
## 14. Term and Termination
|
||||
|
||||
This Agreement applies from first use of the Software until terminated.
|
||||
|
||||
You may terminate this Agreement at any time by stopping use of the Software and deleting your copies, subject to any continuing rights you may have under the AGPL-3.0.
|
||||
|
||||
OnDevice UG may suspend or terminate access to any related service, website, account feature, or distribution channel at any time. If you materially breach this Agreement, OnDevice UG may terminate your rights under this Agreement upon notice, to the extent permitted by law. Termination does not affect provisions that by their nature should survive, including Sections 7 through 17.
|
||||
|
||||
---
|
||||
|
||||
## 15. Export, Sanctions, and Local Law
|
||||
|
||||
The Software may include encryption, routing, or other regulated functionality. You are solely responsible for complying with applicable export controls, sanctions, import laws, and local laws relating to possession, download, use, or transmission of the Software and any connected service.
|
||||
|
||||
OnDevice UG makes no representation that the Software is lawful, available, or appropriate in any jurisdiction.
|
||||
|
||||
---
|
||||
|
||||
## 16. Governing Law and Venue
|
||||
|
||||
This Agreement is governed by the laws of the Federal Republic of Germany, excluding conflict of law rules and the United Nations Convention on Contracts for the International Sale of Goods (CISG).
|
||||
|
||||
If you are a merchant, legal entity under public law, or special fund under public law, the exclusive place of jurisdiction is Stuttgart, Germany.
|
||||
|
||||
If you are a consumer, mandatory consumer protection laws and mandatory venue rules remain unaffected. In particular, if the law of the country of your habitual residence grants you protections that are more favorable than the non-mandatory provisions of German law, those protections remain unaffected.
|
||||
|
||||
The European Commission provides an online dispute resolution platform at https://ec.europa.eu/odr/. OnDevice UG is not obligated and does not agree to participate in consumer arbitration proceedings unless required by law.
|
||||
|
||||
---
|
||||
|
||||
## 17. General Terms
|
||||
|
||||
If any provision of this Agreement is held invalid or unenforceable, the remaining provisions remain in effect, and the invalid provision shall be enforced to the maximum extent permitted by law.
|
||||
|
||||
This Agreement, together with the Privacy Policy and applicable open source licenses, is the entire agreement between you and OnDevice UG regarding the Software, unless a separate written agreement expressly applies.
|
||||
|
||||
No waiver is effective unless in writing. Failure to enforce any provision is not a waiver.
|
||||
|
||||
---
|
||||
|
||||
## 18. Contact
|
||||
|
||||
If you have questions about this Agreement, please contact:
|
||||
|
||||
OnDevice UG (haftungsbeschränkt)
|
||||
Romerstrasse 21
|
||||
D-71540 Murrhardt
|
||||
Germany
|
||||
Email: info@ondevice.eu
|
||||
@@ -0,0 +1,304 @@
|
||||
# WebLibre Privacy Policy
|
||||
|
||||
**Version 1.3**
|
||||
**Last Updated:** 2026-03-22
|
||||
|
||||
**Controller:**
|
||||
OnDevice UG (haftungsbeschränkt)
|
||||
Romerstrasse 21
|
||||
D-71540 Murrhardt
|
||||
Germany
|
||||
info@ondevice.eu
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
This Privacy Policy explains how OnDevice UG handles personal data in connection with WebLibre.
|
||||
|
||||
WebLibre is designed so that browsing and app data are primarily processed on your device rather than sent to OnDevice UG. WebLibre may also connect you to third-party websites, services, networks, platform features, and content providers that process data under their own terms and privacy policies.
|
||||
|
||||
Unless otherwise stated, references to "we," "us," or "our" mean OnDevice UG.
|
||||
|
||||
---
|
||||
|
||||
## 2. Data Processed Locally on Your Device
|
||||
|
||||
Depending on the features you use, WebLibre may store or process data locally on your device, such as browsing history, bookmarks, cookies and site data, cached content, downloads, tab state, search history, settings, extension data, account-related data, feed content, and backup or export files.
|
||||
|
||||
Some files you create or export may be stored outside the app sandbox in locations you choose or that Android makes available for file access.
|
||||
|
||||
Local storage on your device is generally under your control. You can manage or delete certain data through Android and in-app settings, but some data may remain until you remove it manually, clear app storage, or uninstall the app.
|
||||
|
||||
---
|
||||
|
||||
## 3. Data That May Be Sent to Third Parties
|
||||
|
||||
When you use the web or optional features, your device may communicate directly with third parties. All such features are either user-initiated or can be disabled. OnDevice UG does not control third-party processing and is not responsible for third-party privacy or security practices.
|
||||
|
||||
### 3.1 Web Browsing
|
||||
|
||||
When you visit a website, your device connects directly to that website's servers. The website receives your IP address, browser user agent string, HTTP headers, cookies, and any data you submit. This is inherent to how the web works and is not controlled by WebLibre. WebLibre provides tools to mitigate tracking (see Section 7).
|
||||
|
||||
**Legal basis:** Contract performance (Art. 6(1)(b) GDPR) — fulfilling your request to access a website.
|
||||
|
||||
### 3.2 Search Suggestions (Optional)
|
||||
|
||||
When enabled, partial search queries are sent to the search suggestion provider you select (for example DuckDuckGo, Brave Search, Kagi, Qwant, or another provider you configure). Each provider's privacy policy governs their handling of this data. You can disable this feature entirely in settings.
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to enable this feature and select the provider.
|
||||
|
||||
### 3.3 Search Engines
|
||||
|
||||
When you perform a search, your full query is sent to your selected search engine (for example DuckDuckGo, Brave Search, Kagi, Qwant, or another provider you configure). This is a direct interaction between your device and the search engine provider. WebLibre does not intermediate or log these queries.
|
||||
|
||||
**Legal basis:** Contract performance (Art. 6(1)(b) GDPR) — fulfilling your request to perform a search.
|
||||
|
||||
### 3.4 Firefox Sync (Optional)
|
||||
|
||||
If you sign in with a Mozilla account, WebLibre can synchronize data (bookmarks, browsing history, open tabs, device information) with Mozilla's Firefox Sync servers. This feature is entirely opt-in. WebLibre also supports custom server overrides, in which case synchronization data is transmitted to the operator of the server endpoints you configure instead of Mozilla's default infrastructure.
|
||||
|
||||
**Data controller for Firefox Sync:** Mozilla Corporation for the default service, or the operator of the endpoints you configure if you override the defaults. Mozilla's privacy policy: https://www.mozilla.org/privacy/firefox/
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you actively sign in and choose to sync.
|
||||
|
||||
### 3.5 DNS-over-HTTPS (Optional)
|
||||
|
||||
If you enable DNS-over-HTTPS (DoH) or configure a custom DNS resolver, DNS queries are sent to the selected provider instead of your default network resolver.
|
||||
|
||||
**Data sent:** DNS lookup requests and related network metadata necessary for name resolution.
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to enable DoH or configure the provider.
|
||||
|
||||
### 3.6 Enhanced Tracking Protection and Safe Browsing
|
||||
|
||||
WebLibre uses GeckoView's built-in tracking protection and security features to block known trackers, fingerprinters, and malicious sites. When Google Safe Browsing or similar remote reputation features are active, URL-derived lookup data or other security-related request data may be transmitted to Google LLC or the relevant provider to detect dangerous websites or downloads.
|
||||
|
||||
**Data controller for Safe Browsing:** Google LLC. Google's privacy policy: https://policies.google.com/privacy
|
||||
|
||||
**Legal basis:** Legitimate interest (Art. 6(1)(f) GDPR) — protecting users from malicious websites.
|
||||
|
||||
### 3.7 Browser Extensions (User-Initiated)
|
||||
|
||||
When you install a browser extension, WebLibre may contact Mozilla Add-ons (addons.mozilla.org), a configured add-on collection, or another source to retrieve extension metadata or downloads. WebLibre also supports installation from local `.xpi` files, which does not require contact with a remote source.
|
||||
|
||||
**Data sent:** Extension identifiers and related request metadata required by the selected source.
|
||||
|
||||
Installed extensions may independently communicate with their own servers according to their own privacy policies.
|
||||
|
||||
**Legal basis:** Contract performance (Art. 6(1)(b) GDPR) — fulfilling your request to install an extension.
|
||||
|
||||
### 3.8 URL Unshortening (Optional)
|
||||
|
||||
When enabled, WebLibre can expand shortened URLs (e.g., bit.ly links) to reveal the actual destination before navigation.
|
||||
|
||||
**Data sent:** The shortened URL.
|
||||
**Server:** `unshorten.me`. The service may cache redirect results. Avoid using this feature for private or sensitive URLs unless you accept third-party handling.
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to enable this feature.
|
||||
|
||||
### 3.9 Tor Network (Optional)
|
||||
|
||||
When Tor mode is enabled, your web traffic is routed through the Tor network via multiple relays for anonymity. OnDevice UG is not affiliated with The Tor Project.
|
||||
|
||||
**Data sent:** Encrypted traffic via the standard Tor protocol.
|
||||
|
||||
**Pluggable transports:** Depending on your settings, WebLibre may use pluggable transports (such as Snowflake, obfs4, meek, or webtunnel) to connect to the Tor network. When using these transports, your device may additionally communicate with transport brokers, CDN front domains, and STUN/TURN servers operated by third parties. **These connections may expose your IP address to the respective transport operators.**
|
||||
|
||||
Bridge configuration may be bundled with the app, manually entered by you, or fetched remotely (e.g., via the Tor Project's Moat API).
|
||||
|
||||
The Tor network's operation is governed by the Tor Project's policies: https://www.torproject.org/about/privacy/
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to enable Tor mode.
|
||||
|
||||
### 3.10 RSS/Feed Fetching (User-Initiated)
|
||||
|
||||
When you subscribe to an RSS or Atom feed, WebLibre periodically fetches content from the feed URL. In release builds, feeds are automatically refreshed approximately every 15 minutes using Android's background fetch mechanism, including when the app is not actively running and after device restarts.
|
||||
|
||||
**Data sent:** HTTP requests to the feed URLs (your IP address is visible to the feed servers).
|
||||
|
||||
**Legal basis:** Contract performance (Art. 6(1)(b) GDPR) — fulfilling your request to subscribe to a feed.
|
||||
|
||||
### 3.11 URL Cleaner Catalog Updates (Optional)
|
||||
|
||||
WebLibre includes a locally cached URL-cleaning catalog (ClearURLs) to strip tracking parameters from URLs. Depending on your settings, the app may also fetch updated catalog data and related hash/check files from remote sources at runtime.
|
||||
|
||||
**Data sent:** Standard HTTP(S) requests to the configured catalog and hash URLs. URL cleaning itself is performed locally.
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to enable automatic catalog updates.
|
||||
|
||||
### 3.12 Speech Recognition / Voice Input (Optional)
|
||||
|
||||
If you use speech-to-text or voice-input features, WebLibre invokes Android's speech recognition facilities.
|
||||
|
||||
**Data sent:** Audio, recognized text, and related request metadata may be processed by your device vendor, OS provider, or configured speech-recognition service.
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to invoke the feature.
|
||||
|
||||
### 3.13 Small Web Discovery (Optional)
|
||||
|
||||
When you use Small Web mode, WebLibre fetches content from third-party sources to help you discover independent websites and personal blogs.
|
||||
|
||||
**Kagi Small Web:** WebLibre fetches publicly available Atom feeds from Kagi (`kagi.com`) to retrieve curated lists of personal blog posts, indie videos, code projects, and web comics. Feed content is cached locally on your device.
|
||||
|
||||
**Wander:** WebLibre fetches small JavaScript files (`wander.js`) from independently operated personal websites that participate in the Wander network. These files contain lists of recommended pages and links to other participating sites. Your device connects directly to each console host.
|
||||
|
||||
**Data sent:** Standard HTTP(S) requests to Kagi's feed endpoints and to Wander console hosts. Your IP address is visible to these servers. Discovered content (URLs, titles, authors, summaries) is stored locally on your device. No account or personal data is sent beyond what is included in standard HTTP requests.
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to enter Small Web mode and initiate discovery.
|
||||
|
||||
### 3.14 QR Scanning and Camera-Based Pairing (Optional)
|
||||
|
||||
If you use QR scanning features (such as pairing a Firefox Sync account from a desktop browser), the app uses the device camera locally to scan the code.
|
||||
|
||||
QR image data is processed on-device. If you proceed with the action encoded in the QR code, related third-party communications may then occur as part of the chosen feature (e.g., Firefox Sync sign-in).
|
||||
|
||||
**Legal basis:** Consent (Art. 6(1)(a) GDPR) — you choose to invoke the feature.
|
||||
|
||||
---
|
||||
|
||||
## 4. Data We Receive
|
||||
|
||||
WebLibre is not designed to send your browsing history, page content, search queries, or app usage analytics to OnDevice UG by default. To be explicit, in the ordinary operation of the app as provided by OnDevice UG, OnDevice UG does **not**:
|
||||
|
||||
- operate telemetry, analytics, crash reporting, advertising, fingerprinting, or profiling services;
|
||||
- intentionally collect browsing behavior, search queries, or app usage data;
|
||||
- use advertising networks or ad identifiers;
|
||||
- sell, rent, or share your personal data;
|
||||
- intentionally operate servers that receive personal browsing data from WebLibre installations.
|
||||
|
||||
However, OnDevice UG may receive limited personal data in situations outside or around the app itself, for example if you contact us, submit a legal request, access our websites or code hosting pages, use a service we operate, or interact with a platform or distribution channel that shares operational information with us.
|
||||
|
||||
We may also process information where necessary to comply with law, protect rights and security, prevent abuse, or operate and improve our own services.
|
||||
|
||||
---
|
||||
|
||||
## 5. Purposes and Legal Bases
|
||||
|
||||
Where applicable under the GDPR, we process personal data on one or more of the following legal bases. The specific legal basis for each feature is stated in Section 3 alongside that feature's data flow description.
|
||||
|
||||
**Summary of legal bases:**
|
||||
|
||||
- **Consent** (Art. 6(1)(a) GDPR) — for optional features you choose to enable, such as search suggestions (3.2), Firefox Sync (3.4), DNS-over-HTTPS (3.5), URL unshortening (3.8), Tor routing (3.9), URL cleaner catalog updates (3.11), voice input (3.12), Small Web discovery (3.13), and QR scanning (3.14);
|
||||
- **Contract performance** (Art. 6(1)(b) GDPR) — for features inherent to the service you requested, such as web browsing (3.1), search queries (3.3), extension installation (3.7), and feed fetching (3.10);
|
||||
- **Legitimate interest** (Art. 6(1)(f) GDPR) — for security and protection features such as tracking protection and safe browsing (3.6);
|
||||
- **Legal obligation** (Art. 6(1)(c) GDPR) — where required by applicable law;
|
||||
- any other legal basis permitted under applicable law.
|
||||
|
||||
For optional third-party features you choose to enable or configure, the relevant third party may act as an independent controller or processor under its own privacy terms.
|
||||
|
||||
---
|
||||
|
||||
## 6. What We Do Not Intentionally Do Through the App
|
||||
|
||||
WebLibre is not intended to operate as a telemetry, advertising, or profiling product for OnDevice UG. We do not intentionally design the app to send us browsing content, browsing history, search terms, or app usage analytics for tracking or advertising purposes.
|
||||
|
||||
This section applies to the app's intended first-party behavior. It does not cover third-party services, modified builds, custom endpoints, user-configured services, platform-level processing, or data you provide to us separately.
|
||||
|
||||
---
|
||||
|
||||
## 7. Privacy Features and User Controls
|
||||
|
||||
WebLibre provides extensive privacy controls that you can configure through the app's privacy and security settings. Key controls include:
|
||||
|
||||
- **Enhanced Tracking Protection (ETP)** — blocks known trackers, fingerprinters, cryptominers, and social tracking content
|
||||
- **Bounce Tracking Protection** — mitigates cross-site tracking via redirect chains
|
||||
- **Query Parameter Stripping** — removes known tracking parameters from URLs
|
||||
- **Fingerprinting Resistance** — reduces browser fingerprinting surface
|
||||
- **Global Privacy Control (GPC)** — sends a signal to websites requesting they do not sell or share your data
|
||||
- **Multi-Account Containers** — isolate browsing contexts to prevent cross-site tracking, with optional clear-on-exit per container
|
||||
- **HTTPS-Only Mode** — upgrades connections to HTTPS where possible
|
||||
- **DNS-over-HTTPS** — encrypts DNS queries to configured providers
|
||||
- **Site Isolation** — isolates website processes for security
|
||||
- **Local Network Blocking** — prevents websites from accessing local network resources
|
||||
- **Tor Integration** — routes traffic through the Tor network for anonymity
|
||||
- **URL Cleaning** — strips tracking parameters from URLs using locally cached catalogs
|
||||
|
||||
For a complete list of available controls and their current state, see the app's privacy and security settings.
|
||||
|
||||
---
|
||||
|
||||
## 8. Permissions and Device Features
|
||||
|
||||
WebLibre may request or use device permissions and platform features depending on the functionality you use, such as internet access, storage or media access, notifications, camera, microphone, location, biometric or local authentication, credential services, and interaction with other apps.
|
||||
|
||||
These permissions are used to provide requested functionality. Data obtained through those permissions may be processed locally on your device or sent to relevant third parties depending on the feature you use and your device or platform configuration.
|
||||
|
||||
---
|
||||
|
||||
## 9. Children
|
||||
|
||||
WebLibre is not directed to children, and we do not knowingly collect personal data from children through the app. Parents and guardians are responsible for supervising a child's use of the internet, apps, and connected services.
|
||||
|
||||
---
|
||||
|
||||
## 10. Retention
|
||||
|
||||
Data stored locally by the app generally remains on your device until you delete it, overwrite it, clear app storage, uninstall the app, or remove external files. Certain categories of local data may also be deleted earlier through in-app deletion features, clear-on-exit settings, or Android storage management.
|
||||
|
||||
If OnDevice UG receives personal data directly (for example through support requests or legal inquiries), we retain it only for as long as reasonably necessary for the purpose for which it was collected, including legal, operational, security, documentation, and dispute-resolution purposes, and no longer than required by applicable law.
|
||||
|
||||
---
|
||||
|
||||
## 11. Security
|
||||
|
||||
We use reasonable measures for personal data we control. Local app data is protected by Android's application sandboxing. WebLibre supports encrypted backups for profile data. The app's local-only storage model means that most user data never leaves your device unless you choose to use features that transmit data (as described in Section 3).
|
||||
|
||||
However, no software, device, network, or transmission method is completely secure. You are responsible for the security of your device, backups, exported files, credentials, accounts, and any third-party services or custom configurations you choose to use.
|
||||
|
||||
---
|
||||
|
||||
## 12. International Transfers
|
||||
|
||||
When you access websites or use optional features, third parties may process data in countries outside your own, including outside the European Economic Area. Those transfers are governed by the relevant third party's terms and privacy practices.
|
||||
|
||||
If OnDevice UG transfers personal data internationally, we will do so in accordance with applicable law.
|
||||
|
||||
---
|
||||
|
||||
## 13. Your Rights
|
||||
|
||||
Subject to applicable law, you may have rights including access, rectification, erasure, restriction, portability, objection, and the right to withdraw consent where processing is based on consent.
|
||||
|
||||
Because WebLibre is designed to store most data locally on your device, OnDevice UG may not be able to identify you from app use alone or respond to requests regarding data that we do not possess or control.
|
||||
|
||||
You also have the right to lodge a complaint with a competent data protection authority, in particular in the EU Member State of your habitual residence, place of work, or the place of the alleged infringement, subject to applicable law.
|
||||
|
||||
To exercise your rights regarding data controlled by OnDevice UG, contact: info@ondevice.eu
|
||||
|
||||
Supervisory authority:
|
||||
Der Landesbeauftragte fur den Datenschutz und die Informationsfreiheit Baden-Wurttemberg (LfDI BW), https://www.baden-wuerttemberg.datenschutz.de/
|
||||
|
||||
---
|
||||
|
||||
## 14. Third-Party Services and Open Source
|
||||
|
||||
WebLibre includes or interoperates with third-party and open source components. Those components, services, and endpoints may change over time and may be governed by separate privacy notices, licenses, or terms.
|
||||
|
||||
WebLibre is open source. Its source code is publicly available to support transparency and independent review:
|
||||
|
||||
Source code: https://github.com/FaFre/WebLibre
|
||||
|
||||
---
|
||||
|
||||
## 15. Changes to This Privacy Policy
|
||||
|
||||
We may update this Privacy Policy from time to time. We will publish the updated version and revise the version number and last updated date. Material changes may also be communicated through app release notes, repositories, websites, or other appropriate channels.
|
||||
|
||||
---
|
||||
|
||||
## 16. Contact
|
||||
|
||||
If you have questions about this Privacy Policy, please contact:
|
||||
|
||||
OnDevice UG (haftungsbeschränkt)
|
||||
Romerstrasse 21
|
||||
D-71540 Murrhardt
|
||||
Germany
|
||||
Email: info@ondevice.eu
|
||||
|
||||
---
|
||||
|
||||
## 17. Governing Law
|
||||
|
||||
This Privacy Policy is governed by the laws of the Federal Republic of Germany, except where mandatory law requires otherwise.
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"categories": {
|
||||
"ai": {
|
||||
"label": "AI",
|
||||
"description": "LLMs · agents · prompts · AI ethics · AI safety",
|
||||
"emoji": "✨"
|
||||
},
|
||||
"programming": {
|
||||
"label": "Programming",
|
||||
"description": "Code · frameworks · devtools · APIs · tutorials · open source",
|
||||
"emoji": "🧩"
|
||||
},
|
||||
"tech": {
|
||||
"label": "Technology",
|
||||
"description": "Tech news · gadgets · apps · platforms · social media",
|
||||
"emoji": "📱"
|
||||
},
|
||||
"infra": {
|
||||
"label": "Sysadmin & Security",
|
||||
"description": "Servers · cloud · containers · networking · infosec · privacy",
|
||||
"emoji": "🛡️"
|
||||
},
|
||||
"web": {
|
||||
"label": "Web & Internet",
|
||||
"description": "The open web · RSS · blogging · IndieWeb · web standards",
|
||||
"emoji": "🕸️"
|
||||
},
|
||||
"hardware": {
|
||||
"label": "Hardware",
|
||||
"description": "Electronics · home lab · PCB design · keyboards · audio gear",
|
||||
"emoji": "🎛️"
|
||||
},
|
||||
"diy": {
|
||||
"label": "DIY & Making",
|
||||
"description": "Woodworking · metalworking · 3D printing · home · maker",
|
||||
"emoji": "🛠️"
|
||||
},
|
||||
"retro": {
|
||||
"label": "Retro",
|
||||
"description": "Vintage computers · DOS · BBS · demoscene · old software",
|
||||
"emoji": "💾"
|
||||
},
|
||||
"science": {
|
||||
"label": "Science",
|
||||
"description": "Physics · biology · math · space · climate · research",
|
||||
"emoji": "⚛️"
|
||||
},
|
||||
"humanities": {
|
||||
"label": "Humanities",
|
||||
"description": "History · philosophy · linguistics · archaeology · classics",
|
||||
"emoji": "🏺"
|
||||
},
|
||||
"essays": {
|
||||
"label": "Essays",
|
||||
"description": "Long-form reflective writing that defies topic categories",
|
||||
"emoji": "🪶"
|
||||
},
|
||||
"art": {
|
||||
"label": "Art & Design",
|
||||
"description": "Visual art · illustration · typography · creative writing",
|
||||
"emoji": "🌊"
|
||||
},
|
||||
"photography": {
|
||||
"label": "Photography",
|
||||
"description": "Photographic craft · technique · gear · film · photo essays",
|
||||
"emoji": "🌄"
|
||||
},
|
||||
"culture": {
|
||||
"label": "Pop Culture",
|
||||
"description": "Film · TV · music · books · comics",
|
||||
"emoji": "🎭"
|
||||
},
|
||||
"gaming": {
|
||||
"label": "Gaming",
|
||||
"description": "Video games · game dev · modding · interactive fiction",
|
||||
"emoji": "🕹️"
|
||||
},
|
||||
"politics": {
|
||||
"label": "Politics",
|
||||
"description": "Government · legislation · international relations",
|
||||
"emoji": "🎤"
|
||||
},
|
||||
"economy": {
|
||||
"label": "Economy",
|
||||
"description": "Markets · finance · labor economics · trade · analysis",
|
||||
"emoji": "🎲"
|
||||
},
|
||||
"society": {
|
||||
"label": "Society",
|
||||
"description": "Discrimination · civil rights · social structure",
|
||||
"emoji": "👥"
|
||||
},
|
||||
"life": {
|
||||
"label": "Life & Personal",
|
||||
"description": "Diary · weeknotes · parenting · pets · link roundups",
|
||||
"emoji": "☀️"
|
||||
},
|
||||
"food": {
|
||||
"label": "Food & Drink",
|
||||
"description": "Recipes · cooking · restaurants · coffee · wine · baking",
|
||||
"emoji": "🧑🍳"
|
||||
},
|
||||
"travel": {
|
||||
"label": "Travel & Outdoors",
|
||||
"description": "Trip reports · hiking · nature · birdwatching · gardening",
|
||||
"emoji": "✈️"
|
||||
},
|
||||
"health": {
|
||||
"label": "Health & Fitness",
|
||||
"description": "Fitness · nutrition · mental health · longevity",
|
||||
"emoji": "🏃"
|
||||
}
|
||||
},
|
||||
"groups": {
|
||||
"Tech & Science": [
|
||||
"ai",
|
||||
"science",
|
||||
"programming",
|
||||
"diy",
|
||||
"tech",
|
||||
"hardware",
|
||||
"infra",
|
||||
"web"
|
||||
],
|
||||
"Culture & Creative": [
|
||||
"health",
|
||||
"art",
|
||||
"essays",
|
||||
"humanities",
|
||||
"retro",
|
||||
"photography",
|
||||
"culture",
|
||||
"gaming"
|
||||
],
|
||||
"Life & World": [
|
||||
"society",
|
||||
"life",
|
||||
"food",
|
||||
"travel",
|
||||
"politics",
|
||||
"economy"
|
||||
]
|
||||
},
|
||||
"remap": {
|
||||
"sysadmin": "infra",
|
||||
"security": "infra"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
targets:
|
||||
$default:
|
||||
builders:
|
||||
drift_dev:
|
||||
# disable drift's default builder, we're using the modular setup
|
||||
# instead.
|
||||
enabled: false
|
||||
|
||||
# Instead, enable drift_dev:analyzer and drift_dev:modular manually:
|
||||
drift_dev:analyzer:
|
||||
enabled: true
|
||||
options: &options
|
||||
skip_verification_code: true
|
||||
data_class_to_companions: false
|
||||
named_parameters: true
|
||||
databases:
|
||||
bangs: lib/features/bangs/data/database/database.dart
|
||||
quotes: lib/features/quotes/data/database/database.dart
|
||||
tabs: lib/features/geckoview/features/tabs/data/database/database.dart
|
||||
user: lib/features/user/data/database/database.dart
|
||||
web_feed: lib/features/web_feed/data/database/database.dart
|
||||
sql:
|
||||
dialect: sqlite
|
||||
options:
|
||||
version: "3.45"
|
||||
known_functions:
|
||||
lexo_rank_next: "text (int, text null)"
|
||||
lexo_rank_previous: "text (int, text null)"
|
||||
lexo_rank_reorder_after: "text (text null, text null)"
|
||||
lexo_rank_reorder_before: "text (text null, text null)"
|
||||
modules:
|
||||
- json1
|
||||
- fts5
|
||||
drift_dev:modular:
|
||||
enabled: true
|
||||
# We use yaml anchors to give the two builders the same options
|
||||
options: *options
|
||||
json_serializable:
|
||||
options:
|
||||
explicit_to_json: true
|
||||
|
||||
global_options:
|
||||
drift_dev:modular:
|
||||
runs_before:
|
||||
- riverpod_generator
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
- drift: true
|
||||
@@ -0,0 +1,892 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "website_name",
|
||||
"getter_name": "websiteName",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "domain",
|
||||
"getter_name": "domain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url_template",
|
||||
"getter_name": "urlTemplate",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"getter_name": "category",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "sub_category",
|
||||
"getter_name": "subCategory",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"getter_name": "format",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const BangFormatConverter()",
|
||||
"dart_type_name": "Set<BangFormat>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "additional_triggers",
|
||||
"getter_name": "additionalTriggers",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const TriggerListConverter()",
|
||||
"dart_type_name": "Set<String>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "searxng_api",
|
||||
"getter_name": "searxngApi",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL DEFAULT FALSE",
|
||||
"default_dart": "const CustomExpression('FALSE')",
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"PRIMARY KEY(\"trigger\", \"group\")"
|
||||
],
|
||||
"explicit_pk": [
|
||||
"trigger",
|
||||
"group"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_triggers",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "additional_trigger",
|
||||
"getter_name": "additionalTrigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"PRIMARY KEY(\"trigger\", \"group\", additional_trigger)",
|
||||
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
|
||||
],
|
||||
"explicit_pk": [
|
||||
"trigger",
|
||||
"group",
|
||||
"additional_trigger"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "index",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"name": "idx_bang_triggers_lookup",
|
||||
"sql": "CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, \"group\");",
|
||||
"unique": false,
|
||||
"columns": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"name": "bang_triggers_after_insert",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang \nWHEN new.additional_triggers IS NOT NULL\nBEGIN\n INSERT INTO bang_triggers(\"trigger\", \"group\", additional_trigger)\n SELECT \n new.\"trigger\",\n new.\"group\",\n json_each.value\n FROM json_each(new.additional_triggers);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"name": "bang_triggers_after_update",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN\n -- Delete old additional triggers\n DELETE FROM bang_triggers \n WHERE \"trigger\" = old.\"trigger\" AND \"group\" = old.\"group\";\n\n -- Insert new additional triggers if they exist\n INSERT INTO bang_triggers(\"trigger\", \"group\", additional_trigger)\n SELECT \n new.\"trigger\",\n new.\"group\",\n json_each.value\n FROM json_each(new.additional_triggers)\n WHERE new.additional_triggers IS NOT NULL;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_sync",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "last_sync",
|
||||
"getter_name": "lastSync",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_frequency",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "frequency",
|
||||
"getter_name": "frequency",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_used",
|
||||
"getter_name": "lastUsed",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"PRIMARY KEY(\"trigger\", \"group\")",
|
||||
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
|
||||
],
|
||||
"explicit_pk": [
|
||||
"trigger",
|
||||
"group"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_history",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "search_query",
|
||||
"getter_name": "searchQuery",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "UNIQUE NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"unique"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "search_date",
|
||||
"getter_name": "searchDate",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "website_name",
|
||||
"getter_name": "websiteName",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"bang_fts\" USING fts5(trigger, website_name, content=bang, prefix='2 3')",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_triggers_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "additional_trigger",
|
||||
"getter_name": "additionalTrigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"bang_triggers_fts\" USING fts5(additional_trigger, content=bang_triggers, prefix='2 3')",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"references": [
|
||||
0,
|
||||
6
|
||||
],
|
||||
"type": "view",
|
||||
"data": {
|
||||
"name": "bang_data_view",
|
||||
"sql": "CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used FROM bang AS b LEFT JOIN bang_frequency AS bf ON b.\"trigger\" = bf.\"trigger\" AND b.\"group\" = bf.\"group\"",
|
||||
"dart_info_name": "BangDataView",
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "website_name",
|
||||
"getter_name": "websiteName",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "domain",
|
||||
"getter_name": "domain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url_template",
|
||||
"getter_name": "urlTemplate",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"getter_name": "category",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "sub_category",
|
||||
"getter_name": "subCategory",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"getter_name": "format",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const BangFormatConverter()",
|
||||
"dart_type_name": "Set<BangFormat>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "additional_triggers",
|
||||
"getter_name": "additionalTriggers",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const TriggerListConverter()",
|
||||
"dart_type_name": "Set<String>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "searxng_api",
|
||||
"getter_name": "searxngApi",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"defaultConstraints": "CHECK (\"searxng_api\" IN (0, 1))",
|
||||
"dialectAwareDefaultConstraints": {
|
||||
"sqlite": "CHECK (\"searxng_api\" IN (0, 1))"
|
||||
},
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "frequency",
|
||||
"getter_name": "frequency",
|
||||
"moor_type": "int",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_used",
|
||||
"getter_name": "lastUsed",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"references": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"name": "bang_after_insert",
|
||||
"sql": "CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN\n INSERT INTO \n bang_fts(rowid, \"trigger\", website_name) \n VALUES (new.rowid, new.\"trigger\", new.website_name);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"references": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"name": "bang_after_delete",
|
||||
"sql": "CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN\n INSERT INTO \n bang_fts(bang_fts, rowid, \"trigger\", website_name) \n VALUES('delete', old.rowid, old.\"trigger\", old.website_name);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"references": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"name": "bang_after_update",
|
||||
"sql": "CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN\n INSERT INTO \n bang_fts(bang_fts, rowid, \"trigger\", website_name) \n VALUES('delete', old.rowid, old.\"trigger\", old.website_name);\n INSERT INTO \n bang_fts(rowid, \"trigger\", website_name) \n VALUES (new.rowid, new.\"trigger\", new.website_name);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"references": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"name": "bang_triggers_after_insert_fts",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(rowid, additional_trigger) \n VALUES (new.rowid, new.additional_trigger);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"references": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"name": "bang_triggers_after_delete_fts",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) \n VALUES('delete', old.rowid, old.additional_trigger);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"references": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"name": "bang_triggers_after_update_fts",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) \n VALUES('delete', old.rowid, old.additional_trigger);\n INSERT INTO \n bang_triggers_fts(rowid, additional_trigger) \n VALUES (new.rowid, new.additional_trigger);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "bang",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"website_name\" TEXT NOT NULL, \"domain\" TEXT NOT NULL, \"url_template\" TEXT NOT NULL, \"category\" TEXT, \"sub_category\" TEXT, \"format\" TEXT, \"additional_triggers\" TEXT, \"searxng_api\" INTEGER NOT NULL DEFAULT FALSE, PRIMARY KEY(\"trigger\", \"group\"));"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_triggers\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"additional_trigger\" TEXT NOT NULL, PRIMARY KEY(\"trigger\", \"group\", additional_trigger), FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_bang_triggers_lookup",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, \"group\")"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang WHEN new.additional_triggers IS NOT NULL BEGIN INSERT INTO bang_triggers (\"trigger\", \"group\", additional_trigger) SELECT new.\"trigger\", new.\"group\", json_each.value FROM json_each(new.additional_triggers);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN DELETE FROM bang_triggers WHERE \"trigger\" = old.\"trigger\" AND \"group\" = old.\"group\";INSERT INTO bang_triggers (\"trigger\", \"group\", additional_trigger) SELECT new.\"trigger\", new.\"group\", json_each.value FROM json_each(new.additional_triggers)WHERE new.additional_triggers IS NOT NULL;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_sync",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_sync\" (\"group\" INTEGER PRIMARY KEY NOT NULL, \"last_sync\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_frequency",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_frequency\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"frequency\" INTEGER NOT NULL, \"last_used\" INTEGER NOT NULL, PRIMARY KEY(\"trigger\", \"group\"), FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_history",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_history\" (\"search_query\" TEXT UNIQUE NOT NULL, \"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"search_date\" INTEGER NOT NULL, FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"bang_fts\" USING fts5(trigger, website_name, content=bang, prefix='2 3');"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"bang_triggers_fts\" USING fts5(additional_trigger, content=bang_triggers, prefix='2 3');"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_data_view",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used FROM bang AS b LEFT JOIN bang_frequency AS bf ON b.\"trigger\" = bf.\"trigger\" AND b.\"group\" = bf.\"group\""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN INSERT INTO bang_fts (\"rowid\", \"trigger\", website_name) VALUES (new.\"rowid\", new.\"trigger\", new.website_name);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN INSERT INTO bang_fts (bang_fts, \"rowid\", \"trigger\", website_name) VALUES ('delete', old.\"rowid\", old.\"trigger\", old.website_name);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN INSERT INTO bang_fts (bang_fts, \"rowid\", \"trigger\", website_name) VALUES ('delete', old.\"rowid\", old.\"trigger\", old.website_name);INSERT INTO bang_fts (\"rowid\", \"trigger\", website_name) VALUES (new.\"rowid\", new.\"trigger\", new.website_name);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_insert_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (\"rowid\", additional_trigger) VALUES (new.\"rowid\", new.additional_trigger);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_delete_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, \"rowid\", additional_trigger) VALUES ('delete', old.\"rowid\", old.additional_trigger);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_update_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, \"rowid\", additional_trigger) VALUES ('delete', old.\"rowid\", old.additional_trigger);INSERT INTO bang_triggers_fts (\"rowid\", additional_trigger) VALUES (new.\"rowid\", new.additional_trigger);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,912 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "website_name",
|
||||
"getter_name": "websiteName",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "domain",
|
||||
"getter_name": "domain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url_template",
|
||||
"getter_name": "urlTemplate",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"getter_name": "category",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "sub_category",
|
||||
"getter_name": "subCategory",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"getter_name": "format",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const BangFormatConverter()",
|
||||
"dart_type_name": "Set<BangFormat>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "additional_triggers",
|
||||
"getter_name": "additionalTriggers",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const TriggerListConverter()",
|
||||
"dart_type_name": "Set<String>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "searxng_api",
|
||||
"getter_name": "searxngApi",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL DEFAULT FALSE",
|
||||
"default_dart": "const CustomExpression('FALSE')",
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "snap_domain",
|
||||
"getter_name": "snapDomain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"PRIMARY KEY(\"trigger\", \"group\")"
|
||||
],
|
||||
"explicit_pk": [
|
||||
"trigger",
|
||||
"group"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_triggers",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "additional_trigger",
|
||||
"getter_name": "additionalTrigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"PRIMARY KEY(\"trigger\", \"group\", additional_trigger)",
|
||||
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
|
||||
],
|
||||
"explicit_pk": [
|
||||
"trigger",
|
||||
"group",
|
||||
"additional_trigger"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "index",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"name": "idx_bang_triggers_lookup",
|
||||
"sql": "CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, \"group\");",
|
||||
"unique": false,
|
||||
"columns": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"name": "bang_triggers_after_insert",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang \nWHEN new.additional_triggers IS NOT NULL\nBEGIN\n INSERT INTO bang_triggers(\"trigger\", \"group\", additional_trigger)\n SELECT \n new.\"trigger\",\n new.\"group\",\n json_each.value\n FROM json_each(new.additional_triggers);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"name": "bang_triggers_after_update",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN\n -- Delete old additional triggers\n DELETE FROM bang_triggers \n WHERE \"trigger\" = old.\"trigger\" AND \"group\" = old.\"group\";\n\n -- Insert new additional triggers if they exist\n INSERT INTO bang_triggers(\"trigger\", \"group\", additional_trigger)\n SELECT \n new.\"trigger\",\n new.\"group\",\n json_each.value\n FROM json_each(new.additional_triggers)\n WHERE new.additional_triggers IS NOT NULL;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_sync",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "last_sync",
|
||||
"getter_name": "lastSync",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_frequency",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "frequency",
|
||||
"getter_name": "frequency",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_used",
|
||||
"getter_name": "lastUsed",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"PRIMARY KEY(\"trigger\", \"group\")",
|
||||
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
|
||||
],
|
||||
"explicit_pk": [
|
||||
"trigger",
|
||||
"group"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_history",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "search_query",
|
||||
"getter_name": "searchQuery",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "UNIQUE NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"unique"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "search_date",
|
||||
"getter_name": "searchDate",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "website_name",
|
||||
"getter_name": "websiteName",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"bang_fts\" USING fts5(trigger, website_name, content=bang, prefix='2 3')",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "bang_triggers_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "additional_trigger",
|
||||
"getter_name": "additionalTrigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"bang_triggers_fts\" USING fts5(additional_trigger, content=bang_triggers, prefix='2 3')",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"references": [
|
||||
0,
|
||||
6
|
||||
],
|
||||
"type": "view",
|
||||
"data": {
|
||||
"name": "bang_data_view",
|
||||
"sql": "CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used FROM bang AS b LEFT JOIN bang_frequency AS bf ON b.\"trigger\" = bf.\"trigger\" AND b.\"group\" = bf.\"group\"",
|
||||
"dart_info_name": "BangDataView",
|
||||
"columns": [
|
||||
{
|
||||
"name": "trigger",
|
||||
"getter_name": "trigger",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "group",
|
||||
"getter_name": "group",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<BangGroup>(BangGroup.values)",
|
||||
"dart_type_name": "BangGroup"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "website_name",
|
||||
"getter_name": "websiteName",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "domain",
|
||||
"getter_name": "domain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url_template",
|
||||
"getter_name": "urlTemplate",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "category",
|
||||
"getter_name": "category",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "sub_category",
|
||||
"getter_name": "subCategory",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"getter_name": "format",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const BangFormatConverter()",
|
||||
"dart_type_name": "Set<BangFormat>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "additional_triggers",
|
||||
"getter_name": "additionalTriggers",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const TriggerListConverter()",
|
||||
"dart_type_name": "Set<String>?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "searxng_api",
|
||||
"getter_name": "searxngApi",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"defaultConstraints": "CHECK (\"searxng_api\" IN (0, 1))",
|
||||
"dialectAwareDefaultConstraints": {
|
||||
"sqlite": "CHECK (\"searxng_api\" IN (0, 1))"
|
||||
},
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "snap_domain",
|
||||
"getter_name": "snapDomain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "frequency",
|
||||
"getter_name": "frequency",
|
||||
"moor_type": "int",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_used",
|
||||
"getter_name": "lastUsed",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"references": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"name": "bang_after_insert",
|
||||
"sql": "CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN\n INSERT INTO \n bang_fts(rowid, \"trigger\", website_name) \n VALUES (new.rowid, new.\"trigger\", new.website_name);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"references": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"name": "bang_after_delete",
|
||||
"sql": "CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN\n INSERT INTO \n bang_fts(bang_fts, rowid, \"trigger\", website_name) \n VALUES('delete', old.rowid, old.\"trigger\", old.website_name);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"references": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 0,
|
||||
"references_in_body": [
|
||||
0,
|
||||
8
|
||||
],
|
||||
"name": "bang_after_update",
|
||||
"sql": "CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN\n INSERT INTO \n bang_fts(bang_fts, rowid, \"trigger\", website_name) \n VALUES('delete', old.rowid, old.\"trigger\", old.website_name);\n INSERT INTO \n bang_fts(rowid, \"trigger\", website_name) \n VALUES (new.rowid, new.\"trigger\", new.website_name);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"references": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"name": "bang_triggers_after_insert_fts",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(rowid, additional_trigger) \n VALUES (new.rowid, new.additional_trigger);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"references": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"name": "bang_triggers_after_delete_fts",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) \n VALUES('delete', old.rowid, old.additional_trigger);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"references": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
9
|
||||
],
|
||||
"name": "bang_triggers_after_update_fts",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN\n INSERT INTO \n bang_triggers_fts(bang_triggers_fts, rowid, additional_trigger) \n VALUES('delete', old.rowid, old.additional_trigger);\n INSERT INTO \n bang_triggers_fts(rowid, additional_trigger) \n VALUES (new.rowid, new.additional_trigger);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "bang",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"website_name\" TEXT NOT NULL, \"domain\" TEXT NOT NULL, \"url_template\" TEXT NOT NULL, \"category\" TEXT, \"sub_category\" TEXT, \"format\" TEXT, \"additional_triggers\" TEXT, \"searxng_api\" INTEGER NOT NULL DEFAULT FALSE, \"snap_domain\" TEXT, PRIMARY KEY(\"trigger\", \"group\"));"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_triggers\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"additional_trigger\" TEXT NOT NULL, PRIMARY KEY(\"trigger\", \"group\", additional_trigger), FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_bang_triggers_lookup",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE INDEX idx_bang_triggers_lookup ON bang_triggers (additional_trigger, \"group\")"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert AFTER INSERT ON bang WHEN new.additional_triggers IS NOT NULL BEGIN INSERT INTO bang_triggers (\"trigger\", \"group\", additional_trigger) SELECT new.\"trigger\", new.\"group\", json_each.value FROM json_each(new.additional_triggers);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update AFTER UPDATE ON bang BEGIN DELETE FROM bang_triggers WHERE \"trigger\" = old.\"trigger\" AND \"group\" = old.\"group\";INSERT INTO bang_triggers (\"trigger\", \"group\", additional_trigger) SELECT new.\"trigger\", new.\"group\", json_each.value FROM json_each(new.additional_triggers)WHERE new.additional_triggers IS NOT NULL;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_sync",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_sync\" (\"group\" INTEGER PRIMARY KEY NOT NULL, \"last_sync\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_frequency",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_frequency\" (\"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"frequency\" INTEGER NOT NULL, \"last_used\" INTEGER NOT NULL, PRIMARY KEY(\"trigger\", \"group\"), FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_history",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"bang_history\" (\"search_query\" TEXT UNIQUE NOT NULL, \"trigger\" TEXT NOT NULL, \"group\" INTEGER NOT NULL, \"search_date\" INTEGER NOT NULL, FOREIGN KEY(\"trigger\", \"group\")REFERENCES bang(\"trigger\", \"group\")ON DELETE CASCADE);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"bang_fts\" USING fts5(trigger, website_name, content=bang, prefix='2 3');"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"bang_triggers_fts\" USING fts5(additional_trigger, content=bang_triggers, prefix='2 3');"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_data_view",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIEW bang_data_view AS SELECT b.*, bf.frequency, bf.last_used FROM bang AS b LEFT JOIN bang_frequency AS bf ON b.\"trigger\" = bf.\"trigger\" AND b.\"group\" = bf.\"group\""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_after_insert AFTER INSERT ON bang BEGIN INSERT INTO bang_fts (\"rowid\", \"trigger\", website_name) VALUES (new.\"rowid\", new.\"trigger\", new.website_name);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_after_delete AFTER DELETE ON bang BEGIN INSERT INTO bang_fts (bang_fts, \"rowid\", \"trigger\", website_name) VALUES ('delete', old.\"rowid\", old.\"trigger\", old.website_name);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_after_update AFTER UPDATE ON bang BEGIN INSERT INTO bang_fts (bang_fts, \"rowid\", \"trigger\", website_name) VALUES ('delete', old.\"rowid\", old.\"trigger\", old.website_name);INSERT INTO bang_fts (\"rowid\", \"trigger\", website_name) VALUES (new.\"rowid\", new.\"trigger\", new.website_name);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_insert_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_insert_fts AFTER INSERT ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (\"rowid\", additional_trigger) VALUES (new.\"rowid\", new.additional_trigger);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_delete_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_delete_fts AFTER DELETE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, \"rowid\", additional_trigger) VALUES ('delete', old.\"rowid\", old.additional_trigger);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bang_triggers_after_update_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER bang_triggers_after_update_fts AFTER UPDATE ON bang_triggers BEGIN INSERT INTO bang_triggers_fts (bang_triggers_fts, \"rowid\", additional_trigger) VALUES ('delete', old.\"rowid\", old.additional_trigger);INSERT INTO bang_triggers_fts (\"rowid\", additional_trigger) VALUES (new.\"rowid\", new.additional_trigger);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "quotes",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "author",
|
||||
"getter_name": "author",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "quote",
|
||||
"getter_name": "quote",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"getter_name": "source",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"getter_name": "tags",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "quotes",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"quotes\" (\"author\" TEXT NOT NULL, \"quote\" TEXT NOT NULL, \"source\" TEXT, \"tags\" TEXT);"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "container",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"getter_name": "color",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ColorConverter()",
|
||||
"dart_type_name": "Color"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"getter_name": "metadata",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ContainerMetadataConverter()",
|
||||
"dart_type_name": "ContainerMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "parent_id",
|
||||
"getter_name": "parentId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES tab(id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "container_id",
|
||||
"getter_name": "containerId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES container(id)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "container",
|
||||
"column": "id"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_probably_readerable",
|
||||
"getter_name": "isProbablyReaderable",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_markdown",
|
||||
"getter_name": "extractedContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_markdown",
|
||||
"getter_name": "fullContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "timestamp",
|
||||
"getter_name": "timestamp",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1
|
||||
],
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN\n -- Update all children of the deleted row to point to its parent\n UPDATE tab \n SET parent_id = OLD.parent_id \n WHERE parent_id = OLD.id;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_insert",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_delete",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_update",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "container",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"container\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"name\" TEXT, \"color\" INTEGER NOT NULL, \"metadata\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"tab\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"parent_id\" TEXT REFERENCES tab(id)ON DELETE SET NULL, \"container_id\" TEXT REFERENCES container(id)ON DELETE CASCADE, \"order_key\" TEXT NOT NULL, \"url\" TEXT, \"title\" TEXT, \"is_probably_readerable\" INTEGER, \"extracted_content_markdown\" TEXT, \"extracted_content_plain\" TEXT, \"full_content_markdown\" TEXT, \"full_content_plain\" TEXT, \"timestamp\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = OLD.parent_id WHERE parent_id = OLD.id;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "container",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"getter_name": "color",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ColorConverter()",
|
||||
"dart_type_name": "Color"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"getter_name": "metadata",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ContainerMetadataConverter()",
|
||||
"dart_type_name": "ContainerMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "parent_id",
|
||||
"getter_name": "parentId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES tab(id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "container_id",
|
||||
"getter_name": "containerId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES container(id)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "container",
|
||||
"column": "id"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_private",
|
||||
"getter_name": "isPrivate",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_probably_readerable",
|
||||
"getter_name": "isProbablyReaderable",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_markdown",
|
||||
"getter_name": "extractedContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_markdown",
|
||||
"getter_name": "fullContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "timestamp",
|
||||
"getter_name": "timestamp",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1
|
||||
],
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN\n -- Update all children of the deleted row to point to its parent\n UPDATE tab \n SET parent_id = OLD.parent_id \n WHERE parent_id = OLD.id;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_insert",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_delete",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_update",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "container",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"container\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"name\" TEXT, \"color\" INTEGER NOT NULL, \"metadata\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"tab\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"parent_id\" TEXT REFERENCES tab(id)ON DELETE SET NULL, \"container_id\" TEXT REFERENCES container(id)ON DELETE CASCADE, \"order_key\" TEXT NOT NULL, \"url\" TEXT, \"title\" TEXT, \"is_private\" INTEGER, \"is_probably_readerable\" INTEGER, \"extracted_content_markdown\" TEXT, \"extracted_content_plain\" TEXT, \"full_content_markdown\" TEXT, \"full_content_plain\" TEXT, \"timestamp\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = OLD.parent_id WHERE parent_id = OLD.id;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "container",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"getter_name": "color",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ColorConverter()",
|
||||
"dart_type_name": "Color"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"getter_name": "metadata",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ContainerMetadataConverter()",
|
||||
"dart_type_name": "ContainerMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"getter_name": "source",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<TabSource>(TabSource.values)",
|
||||
"dart_type_name": "TabSource"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "parent_id",
|
||||
"getter_name": "parentId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES tab(id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "container_id",
|
||||
"getter_name": "containerId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES container(id)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "container",
|
||||
"column": "id"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_private",
|
||||
"getter_name": "isPrivate",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_probably_readerable",
|
||||
"getter_name": "isProbablyReaderable",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_markdown",
|
||||
"getter_name": "extractedContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_markdown",
|
||||
"getter_name": "fullContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "timestamp",
|
||||
"getter_name": "timestamp",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1
|
||||
],
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN\n -- Update all children of the deleted row to point to its parent\n UPDATE tab \n SET parent_id = OLD.parent_id \n WHERE parent_id = OLD.id;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_insert",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_delete",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_update",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "container",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"container\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"name\" TEXT, \"color\" INTEGER NOT NULL, \"metadata\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"tab\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"source\" INTEGER NOT NULL, \"parent_id\" TEXT REFERENCES tab(id)ON DELETE SET NULL, \"container_id\" TEXT REFERENCES container(id)ON DELETE CASCADE, \"order_key\" TEXT NOT NULL, \"url\" TEXT, \"title\" TEXT, \"is_private\" INTEGER, \"is_probably_readerable\" INTEGER, \"extracted_content_markdown\" TEXT, \"extracted_content_plain\" TEXT, \"full_content_markdown\" TEXT, \"full_content_plain\" TEXT, \"timestamp\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = OLD.parent_id WHERE parent_id = OLD.id;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,441 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "container",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"getter_name": "color",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ColorConverter()",
|
||||
"dart_type_name": "Color"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"getter_name": "metadata",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ContainerMetadataConverter()",
|
||||
"dart_type_name": "ContainerMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"getter_name": "source",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<TabSource>(TabSource.values)",
|
||||
"dart_type_name": "TabSource"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "parent_id",
|
||||
"getter_name": "parentId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES tab(id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "container_id",
|
||||
"getter_name": "containerId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES container(id)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "container",
|
||||
"column": "id"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_private",
|
||||
"getter_name": "isPrivate",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_probably_readerable",
|
||||
"getter_name": "isProbablyReaderable",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_markdown",
|
||||
"getter_name": "extractedContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_markdown",
|
||||
"getter_name": "fullContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "timestamp",
|
||||
"getter_name": "timestamp",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1
|
||||
],
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN\n -- Update all children of the deleted row to point to its parent (grandparent)\n -- Only if the grandparent exists, otherwise set to NULL\n UPDATE tab\n SET parent_id = CASE\n WHEN OLD.parent_id IS NOT NULL AND\n EXISTS(SELECT 1 FROM tab WHERE id = OLD.parent_id)\n THEN OLD.parent_id\n ELSE NULL\n END\n WHERE parent_id = OLD.id;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_insert",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_delete",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_update",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "container",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"container\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"name\" TEXT, \"color\" INTEGER NOT NULL, \"metadata\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"tab\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"source\" INTEGER NOT NULL, \"parent_id\" TEXT REFERENCES tab(id)ON DELETE SET NULL, \"container_id\" TEXT REFERENCES container(id)ON DELETE CASCADE, \"order_key\" TEXT NOT NULL, \"url\" TEXT, \"title\" TEXT, \"is_private\" INTEGER, \"is_probably_readerable\" INTEGER, \"extracted_content_markdown\" TEXT, \"extracted_content_plain\" TEXT, \"full_content_markdown\" TEXT, \"full_content_plain\" TEXT, \"timestamp\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = CASE WHEN OLD.parent_id IS NOT NULL AND EXISTS (SELECT 1 FROM tab WHERE id = OLD.parent_id) THEN OLD.parent_id ELSE NULL END WHERE parent_id = OLD.id;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "container",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"getter_name": "color",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ColorConverter()",
|
||||
"dart_type_name": "Color"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"getter_name": "metadata",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ContainerMetadataConverter()",
|
||||
"dart_type_name": "ContainerMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"getter_name": "source",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<TabSource>(TabSource.values)",
|
||||
"dart_type_name": "TabSource"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "parent_id",
|
||||
"getter_name": "parentId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES tab(id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "container_id",
|
||||
"getter_name": "containerId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES container(id)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "container",
|
||||
"column": "id"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "tab_mode",
|
||||
"getter_name": "tabMode",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL DEFAULT 0",
|
||||
"default_dart": "const CustomExpression('0')",
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<TabModeDbValue>(TabModeDbValue.values)",
|
||||
"dart_type_name": "TabModeDbValue"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isolation_context_id",
|
||||
"getter_name": "isolationContextId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_probably_readerable",
|
||||
"getter_name": "isProbablyReaderable",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_markdown",
|
||||
"getter_name": "extractedContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_markdown",
|
||||
"getter_name": "fullContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "timestamp",
|
||||
"getter_name": "timestamp",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"CHECK((tab_mode = 2 AND isolation_context_id IS NOT NULL)OR(tab_mode != 2 AND isolation_context_id IS NULL))"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1
|
||||
],
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN\n -- Update all children of the deleted row to point to its parent (grandparent)\n -- Only if the grandparent exists, otherwise set to NULL\n UPDATE tab\n SET parent_id = CASE\n WHEN OLD.parent_id IS NOT NULL AND\n EXISTS(SELECT 1 FROM tab WHERE id = OLD.parent_id)\n THEN OLD.parent_id\n ELSE NULL\n END\n WHERE parent_id = OLD.id;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_insert",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_delete",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_update",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "container",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"container\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"name\" TEXT, \"color\" INTEGER NOT NULL, \"metadata\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"tab\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"source\" INTEGER NOT NULL, \"parent_id\" TEXT REFERENCES tab(id)ON DELETE SET NULL, \"container_id\" TEXT REFERENCES container(id)ON DELETE CASCADE, \"order_key\" TEXT NOT NULL, \"url\" TEXT, \"title\" TEXT, \"tab_mode\" INTEGER NOT NULL DEFAULT 0, \"isolation_context_id\" TEXT, \"is_probably_readerable\" INTEGER, \"extracted_content_markdown\" TEXT, \"extracted_content_plain\" TEXT, \"full_content_markdown\" TEXT, \"full_content_plain\" TEXT, \"timestamp\" INTEGER NOT NULL, CHECK((tab_mode = 2 AND isolation_context_id IS NOT NULL)OR(tab_mode != 2 AND isolation_context_id IS NULL)));"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = CASE WHEN OLD.parent_id IS NOT NULL AND EXISTS (SELECT 1 FROM tab WHERE id = OLD.parent_id) THEN OLD.parent_id ELSE NULL END WHERE parent_id = OLD.id;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "container",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"getter_name": "name",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"getter_name": "color",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ColorConverter()",
|
||||
"dart_type_name": "Color"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "metadata",
|
||||
"getter_name": "metadata",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const ContainerMetadataConverter()",
|
||||
"dart_type_name": "ContainerMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"getter_name": "source",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<TabSource>(TabSource.values)",
|
||||
"dart_type_name": "TabSource"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "parent_id",
|
||||
"getter_name": "parentId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES tab(id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "container_id",
|
||||
"getter_name": "containerId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES container(id)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "container",
|
||||
"column": "id"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "tab_mode",
|
||||
"getter_name": "tabMode",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL DEFAULT 0",
|
||||
"default_dart": "const CustomExpression('0')",
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const EnumIndexConverter<TabModeDbValue>(TabModeDbValue.values)",
|
||||
"dart_type_name": "TabModeDbValue"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "isolation_context_id",
|
||||
"getter_name": "isolationContextId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_pinned",
|
||||
"getter_name": "isPinned",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL DEFAULT 0",
|
||||
"default_dart": "const CustomExpression('0')",
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_probably_readerable",
|
||||
"getter_name": "isProbablyReaderable",
|
||||
"moor_type": "bool",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_markdown",
|
||||
"getter_name": "extractedContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_markdown",
|
||||
"getter_name": "fullContentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "timestamp",
|
||||
"getter_name": "timestamp",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [
|
||||
"CHECK((tab_mode = 2 AND isolation_context_id IS NOT NULL)OR(tab_mode != 2 AND isolation_context_id IS NULL))"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "tab_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "extracted_content_plain",
|
||||
"getter_name": "extractedContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "full_content_plain",
|
||||
"getter_name": "fullContentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1
|
||||
],
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN\n -- Update all children of the deleted row to point to its parent (grandparent)\n -- Only if the grandparent exists, otherwise set to NULL\n UPDATE tab\n SET parent_id = CASE\n WHEN OLD.parent_id IS NOT NULL AND\n EXISTS(SELECT 1 FROM tab WHERE id = OLD.parent_id)\n THEN OLD.parent_id\n ELSE NULL\n END\n WHERE parent_id = OLD.id;\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_insert",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_delete",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"name": "tab_after_update",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN\n INSERT INTO \n tab_fts(tab_fts, rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES('delete', old.rowid, old.title, old.url, old.extracted_content_plain, old.full_content_plain);\n INSERT INTO \n tab_fts(rowid, title, url, extracted_content_plain, full_content_plain) \n VALUES (new.rowid, new.title, new.url, new.extracted_content_plain, new.full_content_plain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "container",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"container\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"name\" TEXT, \"color\" INTEGER NOT NULL, \"metadata\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"tab\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"source\" INTEGER NOT NULL, \"parent_id\" TEXT REFERENCES tab(id)ON DELETE SET NULL, \"container_id\" TEXT REFERENCES container(id)ON DELETE CASCADE, \"order_key\" TEXT NOT NULL, \"url\" TEXT, \"title\" TEXT, \"tab_mode\" INTEGER NOT NULL DEFAULT 0, \"isolation_context_id\" TEXT, \"is_pinned\" INTEGER NOT NULL DEFAULT 0, \"is_probably_readerable\" INTEGER, \"extracted_content_markdown\" TEXT, \"extracted_content_plain\" TEXT, \"full_content_markdown\" TEXT, \"full_content_plain\" TEXT, \"timestamp\" INTEGER NOT NULL, CHECK((tab_mode = 2 AND isolation_context_id IS NOT NULL)OR(tab_mode != 2 AND isolation_context_id IS NULL)));"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"tab_fts\" USING fts5(title, url, extracted_content_plain, full_content_plain, content=tab, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_maintain_parent_chain_on_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_maintain_parent_chain_on_delete BEFORE DELETE ON tab BEGIN UPDATE tab SET parent_id = CASE WHEN OLD.parent_id IS NOT NULL AND EXISTS (SELECT 1 FROM tab WHERE id = OLD.parent_id) THEN OLD.parent_id ELSE NULL END WHERE parent_id = OLD.id;END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_insert AFTER INSERT ON tab BEGIN INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_delete AFTER DELETE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tab_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER tab_after_update AFTER UPDATE ON tab BEGIN INSERT INTO tab_fts (tab_fts, \"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES ('delete', old.\"rowid\", old.title, old.url, old.extracted_content_plain, old.full_content_plain);INSERT INTO tab_fts (\"rowid\", title, url, extracted_content_plain, full_content_plain) VALUES (new.\"rowid\", new.title, new.url, new.extracted_content_plain, new.full_content_plain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_meta":{"description":"This file contains a serialized version of schema entities for drift.","version":"1.2.0"},"options":{"store_date_time_values_as_text":false},"entities":[{"id":0,"references":[],"type":"table","data":{"name":"setting","was_declared_in_moor":true,"columns":[{"name":"key","getter_name":"key","moor_type":"string","nullable":false,"customConstraints":"PRIMARY KEY NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":["primary-key"]},{"name":"partition_key","getter_name":"partitionKey","moor_type":"string","nullable":true,"customConstraints":"","default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"value","getter_name":"value","moor_type":"any","nullable":true,"customConstraints":"","default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[],"strict":true}},{"id":1,"references":[],"type":"table","data":{"name":"icon_cache","was_declared_in_moor":true,"columns":[{"name":"origin","getter_name":"origin","moor_type":"string","nullable":false,"customConstraints":"PRIMARY KEY NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":["primary-key"]},{"name":"icon_data","getter_name":"iconData","moor_type":"blob","nullable":false,"customConstraints":"NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"fetch_date","getter_name":"fetchDate","moor_type":"dateTime","nullable":false,"customConstraints":"NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}},{"id":2,"references":[],"type":"table","data":{"name":"onboarding","was_declared_in_moor":true,"columns":[{"name":"revision","getter_name":"revision","moor_type":"int","nullable":false,"customConstraints":"NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":[]},{"name":"completion_date","getter_name":"completionDate","moor_type":"dateTime","nullable":false,"customConstraints":"NOT NULL","default_dart":null,"default_client_dart":null,"dsl_features":[]}],"is_virtual":false,"without_rowid":false,"constraints":[]}}]}
|
||||
@@ -0,0 +1,232 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "setting",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "key",
|
||||
"getter_name": "key",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "partition_key",
|
||||
"getter_name": "partitionKey",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"getter_name": "value",
|
||||
"moor_type": "any",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [],
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "icon_cache",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "origin",
|
||||
"getter_name": "origin",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "icon_data",
|
||||
"getter_name": "iconData",
|
||||
"moor_type": "blob",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "fetch_date",
|
||||
"getter_name": "fetchDate",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "onboarding",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "revision",
|
||||
"getter_name": "revision",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "completion_date",
|
||||
"getter_name": "completionDate",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "riverpod",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "key",
|
||||
"getter_name": "key",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "json",
|
||||
"getter_name": "json",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "expireAt",
|
||||
"getter_name": "expireAt",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "destroyKey",
|
||||
"getter_name": "destroyKey",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": true,
|
||||
"constraints": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "setting",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"setting\" (\"key\" TEXT PRIMARY KEY NOT NULL, \"partition_key\" TEXT, \"value\" ANY) STRICT;"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "icon_cache",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"icon_cache\" (\"origin\" TEXT PRIMARY KEY NOT NULL, \"icon_data\" BLOB NOT NULL, \"fetch_date\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onboarding",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"onboarding\" (\"revision\" INTEGER NOT NULL, \"completion_date\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "riverpod",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"riverpod\" (\"key\" TEXT PRIMARY KEY NOT NULL, \"json\" TEXT NOT NULL, \"expireAt\" INTEGER, \"destroyKey\" TEXT) WITHOUT ROWID;"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "setting",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "key",
|
||||
"getter_name": "key",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "partition_key",
|
||||
"getter_name": "partitionKey",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"getter_name": "value",
|
||||
"moor_type": "any",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": [],
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "icon_cache",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "origin",
|
||||
"getter_name": "origin",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "icon_data",
|
||||
"getter_name": "iconData",
|
||||
"moor_type": "blob",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "fetch_date",
|
||||
"getter_name": "fetchDate",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "onboarding",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "revision",
|
||||
"getter_name": "revision",
|
||||
"moor_type": "int",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "completion_date",
|
||||
"getter_name": "completionDate",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "riverpod",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "key",
|
||||
"getter_name": "key",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "json",
|
||||
"getter_name": "json",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "expireAt",
|
||||
"getter_name": "expireAt",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "destroyKey",
|
||||
"getter_name": "destroyKey",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": true,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "toolbar_button_configs",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "button_id",
|
||||
"getter_name": "buttonId",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL PRIMARY KEY",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "order_key",
|
||||
"getter_name": "orderKey",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "is_visible",
|
||||
"getter_name": "isVisible",
|
||||
"moor_type": "bool",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL DEFAULT TRUE",
|
||||
"default_dart": "const CustomExpression('TRUE')",
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "fallback_id",
|
||||
"getter_name": "fallbackId",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "REFERENCES toolbar_button_configs(button_id)ON DELETE SET NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
4
|
||||
],
|
||||
"type": "index",
|
||||
"data": {
|
||||
"on": 4,
|
||||
"name": "idx_toolbar_order_key",
|
||||
"sql": "CREATE INDEX idx_toolbar_order_key ON toolbar_button_configs(order_key);",
|
||||
"unique": false,
|
||||
"columns": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "setting",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"setting\" (\"key\" TEXT PRIMARY KEY NOT NULL, \"partition_key\" TEXT, \"value\" ANY) STRICT;"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "icon_cache",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"icon_cache\" (\"origin\" TEXT PRIMARY KEY NOT NULL, \"icon_data\" BLOB NOT NULL, \"fetch_date\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onboarding",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"onboarding\" (\"revision\" INTEGER NOT NULL, \"completion_date\" INTEGER NOT NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "riverpod",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"riverpod\" (\"key\" TEXT PRIMARY KEY NOT NULL, \"json\" TEXT NOT NULL, \"expireAt\" INTEGER, \"destroyKey\" TEXT) WITHOUT ROWID;"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "toolbar_button_configs",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"toolbar_button_configs\" (\"button_id\" TEXT NOT NULL PRIMARY KEY, \"order_key\" TEXT NOT NULL, \"is_visible\" INTEGER NOT NULL DEFAULT TRUE, \"fallback_id\" TEXT REFERENCES toolbar_button_configs(button_id)ON DELETE SET NULL);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "idx_toolbar_order_key",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE INDEX idx_toolbar_order_key ON toolbar_button_configs (order_key)"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,738 @@
|
||||
{
|
||||
"_meta": {
|
||||
"description": "This file contains a serialized version of schema entities for drift.",
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"options": {
|
||||
"store_date_time_values_as_text": false
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"id": 0,
|
||||
"references": [],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "feed",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "url",
|
||||
"getter_name": "url",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverter()",
|
||||
"dart_type_name": "Uri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"getter_name": "description",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "icon",
|
||||
"getter_name": "icon",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "site_link",
|
||||
"getter_name": "siteLink",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "authors",
|
||||
"getter_name": "authors",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedAuthorsConverter()",
|
||||
"dart_type_name": "List<FeedAuthor>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"getter_name": "tags",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedCategoriesConverter()",
|
||||
"dart_type_name": "List<FeedCategory>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "last_fetched",
|
||||
"getter_name": "lastFetched",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"references": [
|
||||
0
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "article",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "PRIMARY KEY NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
"primary-key"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "feed_id",
|
||||
"getter_name": "feedId",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL REFERENCES feed(url)ON DELETE CASCADE",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [
|
||||
{
|
||||
"foreign_key": {
|
||||
"to": {
|
||||
"table": "feed",
|
||||
"column": "url"
|
||||
},
|
||||
"initially_deferred": false,
|
||||
"on_update": null,
|
||||
"on_delete": "cascade"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverter()",
|
||||
"dart_type_name": "Uri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fetched",
|
||||
"getter_name": "fetched",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": "NOT NULL",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"getter_name": "created",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "updated",
|
||||
"getter_name": "updated",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_read",
|
||||
"getter_name": "lastRead",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "authors",
|
||||
"getter_name": "authors",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedAuthorsConverter()",
|
||||
"dart_type_name": "List<FeedAuthor>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"getter_name": "tags",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedCategoriesConverter()",
|
||||
"dart_type_name": "List<FeedCategory>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "links",
|
||||
"getter_name": "links",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedLinksConverter()",
|
||||
"dart_type_name": "List<FeedLink>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "summaryHtml",
|
||||
"getter_name": "summaryHtml",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "summaryMarkdown",
|
||||
"getter_name": "summaryMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "summaryPlain",
|
||||
"getter_name": "summaryPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "contentHtml",
|
||||
"getter_name": "contentHtml",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "contentMarkdown",
|
||||
"getter_name": "contentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "contentPlain",
|
||||
"getter_name": "contentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": false,
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"references": [
|
||||
1,
|
||||
0
|
||||
],
|
||||
"type": "view",
|
||||
"data": {
|
||||
"name": "article_view",
|
||||
"sql": "CREATE VIEW article_view AS SELECT a.*, f.icon, f.site_link FROM article AS a INNER JOIN feed AS f ON f.url = a.feed_id",
|
||||
"dart_info_name": "ArticleView",
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"getter_name": "id",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "feed_id",
|
||||
"getter_name": "feedId",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverter()",
|
||||
"dart_type_name": "Uri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "fetched",
|
||||
"getter_name": "fetched",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": false,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "created",
|
||||
"getter_name": "created",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "updated",
|
||||
"getter_name": "updated",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "last_read",
|
||||
"getter_name": "lastRead",
|
||||
"moor_type": "dateTime",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "authors",
|
||||
"getter_name": "authors",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedAuthorsConverter()",
|
||||
"dart_type_name": "List<FeedAuthor>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tags",
|
||||
"getter_name": "tags",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedCategoriesConverter()",
|
||||
"dart_type_name": "List<FeedCategory>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "links",
|
||||
"getter_name": "links",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const FeedLinksConverter()",
|
||||
"dart_type_name": "List<FeedLink>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "summaryHtml",
|
||||
"getter_name": "summaryHtml",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "summaryMarkdown",
|
||||
"getter_name": "summaryMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "summaryPlain",
|
||||
"getter_name": "summaryPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "contentHtml",
|
||||
"getter_name": "contentHtml",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "contentMarkdown",
|
||||
"getter_name": "contentMarkdown",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "contentPlain",
|
||||
"getter_name": "contentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "icon",
|
||||
"getter_name": "icon",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "site_link",
|
||||
"getter_name": "siteLink",
|
||||
"moor_type": "string",
|
||||
"nullable": true,
|
||||
"customConstraints": null,
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": [],
|
||||
"type_converter": {
|
||||
"dart_expr": "const UriConverterNullable()",
|
||||
"dart_type_name": "Uri?"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "index",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"name": "article_feed_id",
|
||||
"sql": "CREATE INDEX article_feed_id ON article (feed_id);",
|
||||
"unique": false,
|
||||
"columns": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"references": [
|
||||
1
|
||||
],
|
||||
"type": "table",
|
||||
"data": {
|
||||
"name": "article_fts",
|
||||
"was_declared_in_moor": true,
|
||||
"columns": [
|
||||
{
|
||||
"name": "title",
|
||||
"getter_name": "title",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "summaryPlain",
|
||||
"getter_name": "summaryPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
},
|
||||
{
|
||||
"name": "contentPlain",
|
||||
"getter_name": "contentPlain",
|
||||
"moor_type": "string",
|
||||
"nullable": false,
|
||||
"customConstraints": "",
|
||||
"default_dart": null,
|
||||
"default_client_dart": null,
|
||||
"dsl_features": []
|
||||
}
|
||||
],
|
||||
"is_virtual": true,
|
||||
"create_virtual_stmt": "CREATE VIRTUAL TABLE \"article_fts\" USING fts5(title, summaryPlain, contentPlain, content=article, tokenize=\"trigram\")",
|
||||
"without_rowid": false,
|
||||
"constraints": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"references": [
|
||||
1,
|
||||
4
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
4
|
||||
],
|
||||
"name": "article_after_insert",
|
||||
"sql": "CREATE TRIGGER article_after_insert AFTER INSERT ON article BEGIN\n INSERT INTO \n article_fts(rowid, title, summaryPlain, contentPlain) \n VALUES (new.rowid, new.title, new.summaryPlain, new.contentPlain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"references": [
|
||||
1,
|
||||
4
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
4
|
||||
],
|
||||
"name": "article_after_delete",
|
||||
"sql": "CREATE TRIGGER article_after_delete AFTER DELETE ON article BEGIN\n INSERT INTO \n article_fts(article_fts, rowid, title, summaryPlain, contentPlain) \n VALUES('delete', old.rowid, old.title, old.summaryPlain, old.contentPlain);\nEND;"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"references": [
|
||||
1,
|
||||
4
|
||||
],
|
||||
"type": "trigger",
|
||||
"data": {
|
||||
"on": 1,
|
||||
"references_in_body": [
|
||||
1,
|
||||
4
|
||||
],
|
||||
"name": "article_after_update",
|
||||
"sql": "CREATE TRIGGER article_after_update AFTER UPDATE ON article BEGIN\n INSERT INTO \n article_fts(article_fts, rowid, title, summaryPlain, contentPlain) \n VALUES('delete', old.rowid, old.title, old.summaryPlain, old.contentPlain);\n INSERT INTO \n article_fts(rowid, title, summaryPlain, contentPlain) \n VALUES (new.rowid, new.title, new.summaryPlain, new.contentPlain);\nEND;"
|
||||
}
|
||||
}
|
||||
],
|
||||
"fixed_sql": [
|
||||
{
|
||||
"name": "feed",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"feed\" (\"url\" TEXT PRIMARY KEY NOT NULL, \"title\" TEXT, \"description\" TEXT, \"icon\" TEXT, \"site_link\" TEXT, \"authors\" TEXT, \"tags\" TEXT, \"last_fetched\" INTEGER);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "article",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TABLE IF NOT EXISTS \"article\" (\"id\" TEXT PRIMARY KEY NOT NULL, \"feed_id\" TEXT NOT NULL REFERENCES feed(url)ON DELETE CASCADE, \"fetched\" INTEGER NOT NULL, \"created\" INTEGER, \"updated\" INTEGER, \"last_read\" INTEGER, \"title\" TEXT, \"authors\" TEXT, \"tags\" TEXT, \"links\" TEXT, \"summaryHtml\" TEXT, \"summaryMarkdown\" TEXT, \"summaryPlain\" TEXT, \"contentHtml\" TEXT, \"contentMarkdown\" TEXT, \"contentPlain\" TEXT);"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "article_view",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIEW article_view AS SELECT a.*, f.icon, f.site_link FROM article AS a INNER JOIN feed AS f ON f.url = a.feed_id"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "article_feed_id",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE INDEX article_feed_id ON article (feed_id)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "article_fts",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE VIRTUAL TABLE IF NOT EXISTS \"article_fts\" USING fts5(title, summaryPlain, contentPlain, content=article, tokenize=\"trigram\");"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "article_after_insert",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER article_after_insert AFTER INSERT ON article BEGIN INSERT INTO article_fts (\"rowid\", title, summaryPlain, contentPlain) VALUES (new.\"rowid\", new.title, new.summaryPlain, new.contentPlain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "article_after_delete",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER article_after_delete AFTER DELETE ON article BEGIN INSERT INTO article_fts (article_fts, \"rowid\", title, summaryPlain, contentPlain) VALUES ('delete', old.\"rowid\", old.title, old.summaryPlain, old.contentPlain);END"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "article_after_update",
|
||||
"sql": [
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sql": "CREATE TRIGGER article_after_update AFTER UPDATE ON article BEGIN INSERT INTO article_fts (article_fts, \"rowid\", title, summaryPlain, contentPlain) VALUES ('delete', old.\"rowid\", old.title, old.summaryPlain, old.contentPlain);INSERT INTO article_fts (\"rowid\", title, summaryPlain, contentPlain) VALUES (new.\"rowid\", new.title, new.summaryPlain, new.contentPlain);END"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:drift/drift.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
|
||||
class DatabaseRegistry {
|
||||
DatabaseRegistry._();
|
||||
static final instance = DatabaseRegistry._();
|
||||
|
||||
final _databases = <String, GeneratedDatabase>{};
|
||||
|
||||
void register(String name, GeneratedDatabase db) {
|
||||
_databases[name] = db;
|
||||
}
|
||||
|
||||
Future<void> closeAll() async {
|
||||
for (final entry in _databases.entries) {
|
||||
try {
|
||||
await entry.value.close();
|
||||
logger.i('${entry.key} database closed');
|
||||
} catch (e, st) {
|
||||
logger.e(
|
||||
'Failed to close ${entry.key} database',
|
||||
error: e,
|
||||
stackTrace: st,
|
||||
);
|
||||
}
|
||||
}
|
||||
_databases.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class AppColors extends ThemeExtension<AppColors> {
|
||||
const AppColors._({
|
||||
required this.seedColor,
|
||||
required this.privateTabPurple,
|
||||
required this.privateTabBackground,
|
||||
required this.privateTabForeground,
|
||||
required this.privateSelectionOverlay,
|
||||
required this.isolatedTabTeal,
|
||||
required this.isolatedTabBackground,
|
||||
required this.isolatedTabForeground,
|
||||
required this.isolatedSelectionOverlay,
|
||||
required this.torPurple,
|
||||
required this.torActiveGreen,
|
||||
required this.torBackgroundGrey,
|
||||
required this.warningAmber,
|
||||
required this.auraPurple,
|
||||
required this.auraGold,
|
||||
required this.auraShadow,
|
||||
required this.auraShadowHighlight,
|
||||
required this.auraTint,
|
||||
required this.brandLink,
|
||||
});
|
||||
|
||||
final Color seedColor;
|
||||
final Color privateTabPurple;
|
||||
final Color privateTabBackground;
|
||||
final Color privateTabForeground;
|
||||
final Color privateSelectionOverlay;
|
||||
final Color isolatedTabTeal;
|
||||
final Color isolatedTabBackground;
|
||||
final Color isolatedTabForeground;
|
||||
final Color isolatedSelectionOverlay;
|
||||
final Color torPurple;
|
||||
final Color torActiveGreen;
|
||||
final Color torBackgroundGrey;
|
||||
final Color warningAmber;
|
||||
final Color auraPurple;
|
||||
final Color auraGold;
|
||||
final Color auraShadow;
|
||||
final Color auraShadowHighlight;
|
||||
final Color auraTint;
|
||||
final Color brandLink;
|
||||
|
||||
/// Brand colors derived from the logo (constant across themes).
|
||||
static const brandPurple = Color(0xFF9C83F8);
|
||||
static const brandYellow = Color(0xFFFBDC6B);
|
||||
static const brandGrey = Color(0xFFA7A7A7);
|
||||
|
||||
static const light = AppColors._(
|
||||
seedColor: Color(0xFF167C80),
|
||||
privateTabPurple: Color(0xFF8000D7),
|
||||
privateTabBackground: Color(0xFFF3E5F5),
|
||||
privateTabForeground: Color(0xFF4A0072),
|
||||
privateSelectionOverlay: Color(0x648000D7),
|
||||
isolatedTabTeal: Color(0xFF00897B),
|
||||
isolatedTabBackground: Color(0xFFE0F2F1),
|
||||
isolatedTabForeground: Color(0xFF004D40),
|
||||
isolatedSelectionOverlay: Color(0x6400897B),
|
||||
torPurple: Color(0xFF7D4698),
|
||||
torActiveGreen: Color(0xFF68B030),
|
||||
torBackgroundGrey: Color(0xFFECEFF1),
|
||||
warningAmber: Color(0xFFFFA000),
|
||||
// Aura: brand colors lightened toward white
|
||||
auraPurple: Color(0xFFE0D6FC),
|
||||
auraGold: Color(0xFFFDF1D6),
|
||||
auraShadow: Color(0xFFEDEDF0),
|
||||
auraShadowHighlight: Color(0xFFE2E2E7),
|
||||
auraTint: Color(0xFFFFFFFF),
|
||||
brandLink: Color(0xFF7A5AF0),
|
||||
);
|
||||
|
||||
static const dark = AppColors._(
|
||||
seedColor: Color(0xFF167C80),
|
||||
privateTabPurple: Color(0xFF8000D7),
|
||||
privateTabBackground: Color(0xFF25003E),
|
||||
privateTabForeground: Color(0xFFFFFFFF),
|
||||
privateSelectionOverlay: Color(0x648000D7),
|
||||
isolatedTabTeal: Color(0xFF00897B),
|
||||
isolatedTabBackground: Color(0xFF003D36),
|
||||
isolatedTabForeground: Color(0xFFFFFFFF),
|
||||
isolatedSelectionOverlay: Color(0x6400897B),
|
||||
torPurple: Color(0xFF7D4698),
|
||||
torActiveGreen: Color(0xFF68B030),
|
||||
torBackgroundGrey: Color(0xFF333A41),
|
||||
warningAmber: Color(0xFFFFA000),
|
||||
// Aura: brand colors darkened toward black
|
||||
auraPurple: Color(0xFF2C2543),
|
||||
auraGold: Color(0xFF3C3827),
|
||||
auraShadow: Color(0xFF222224),
|
||||
auraShadowHighlight: Color(0xFF2D2D31),
|
||||
auraTint: Color(0xFF000000),
|
||||
brandLink: Color(0xFFFBDC6B),
|
||||
);
|
||||
|
||||
@override
|
||||
AppColors copyWith({
|
||||
Color? seedColor,
|
||||
Color? privateTabPurple,
|
||||
Color? privateTabBackground,
|
||||
Color? privateTabForeground,
|
||||
Color? privateSelectionOverlay,
|
||||
Color? isolatedTabTeal,
|
||||
Color? isolatedTabBackground,
|
||||
Color? isolatedTabForeground,
|
||||
Color? isolatedSelectionOverlay,
|
||||
Color? torPurple,
|
||||
Color? torActiveGreen,
|
||||
Color? torBackgroundGrey,
|
||||
Color? warningAmber,
|
||||
Color? auraPurple,
|
||||
Color? auraGold,
|
||||
Color? auraShadow,
|
||||
Color? auraShadowHighlight,
|
||||
Color? auraTint,
|
||||
Color? brandLink,
|
||||
}) {
|
||||
return AppColors._(
|
||||
seedColor: seedColor ?? this.seedColor,
|
||||
privateTabPurple: privateTabPurple ?? this.privateTabPurple,
|
||||
privateTabBackground: privateTabBackground ?? this.privateTabBackground,
|
||||
privateTabForeground: privateTabForeground ?? this.privateTabForeground,
|
||||
privateSelectionOverlay:
|
||||
privateSelectionOverlay ?? this.privateSelectionOverlay,
|
||||
isolatedTabTeal: isolatedTabTeal ?? this.isolatedTabTeal,
|
||||
isolatedTabBackground:
|
||||
isolatedTabBackground ?? this.isolatedTabBackground,
|
||||
isolatedTabForeground:
|
||||
isolatedTabForeground ?? this.isolatedTabForeground,
|
||||
isolatedSelectionOverlay:
|
||||
isolatedSelectionOverlay ?? this.isolatedSelectionOverlay,
|
||||
torPurple: torPurple ?? this.torPurple,
|
||||
torActiveGreen: torActiveGreen ?? this.torActiveGreen,
|
||||
torBackgroundGrey: torBackgroundGrey ?? this.torBackgroundGrey,
|
||||
warningAmber: warningAmber ?? this.warningAmber,
|
||||
auraPurple: auraPurple ?? this.auraPurple,
|
||||
auraGold: auraGold ?? this.auraGold,
|
||||
auraShadow: auraShadow ?? this.auraShadow,
|
||||
auraShadowHighlight: auraShadowHighlight ?? this.auraShadowHighlight,
|
||||
auraTint: auraTint ?? this.auraTint,
|
||||
brandLink: brandLink ?? this.brandLink,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AppColors lerp(covariant ThemeExtension<AppColors>? other, double t) {
|
||||
if (other is! AppColors) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return AppColors._(
|
||||
seedColor: Color.lerp(seedColor, other.seedColor, t)!,
|
||||
privateTabPurple: Color.lerp(
|
||||
privateTabPurple,
|
||||
other.privateTabPurple,
|
||||
t,
|
||||
)!,
|
||||
privateTabBackground: Color.lerp(
|
||||
privateTabBackground,
|
||||
other.privateTabBackground,
|
||||
t,
|
||||
)!,
|
||||
privateTabForeground: Color.lerp(
|
||||
privateTabForeground,
|
||||
other.privateTabForeground,
|
||||
t,
|
||||
)!,
|
||||
privateSelectionOverlay: Color.lerp(
|
||||
privateSelectionOverlay,
|
||||
other.privateSelectionOverlay,
|
||||
t,
|
||||
)!,
|
||||
isolatedTabTeal: Color.lerp(isolatedTabTeal, other.isolatedTabTeal, t)!,
|
||||
isolatedTabBackground: Color.lerp(
|
||||
isolatedTabBackground,
|
||||
other.isolatedTabBackground,
|
||||
t,
|
||||
)!,
|
||||
isolatedTabForeground: Color.lerp(
|
||||
isolatedTabForeground,
|
||||
other.isolatedTabForeground,
|
||||
t,
|
||||
)!,
|
||||
isolatedSelectionOverlay: Color.lerp(
|
||||
isolatedSelectionOverlay,
|
||||
other.isolatedSelectionOverlay,
|
||||
t,
|
||||
)!,
|
||||
torPurple: Color.lerp(torPurple, other.torPurple, t)!,
|
||||
torActiveGreen: Color.lerp(torActiveGreen, other.torActiveGreen, t)!,
|
||||
torBackgroundGrey: Color.lerp(
|
||||
torBackgroundGrey,
|
||||
other.torBackgroundGrey,
|
||||
t,
|
||||
)!,
|
||||
warningAmber: Color.lerp(warningAmber, other.warningAmber, t)!,
|
||||
auraPurple: Color.lerp(auraPurple, other.auraPurple, t)!,
|
||||
auraGold: Color.lerp(auraGold, other.auraGold, t)!,
|
||||
auraShadow: Color.lerp(auraShadow, other.auraShadow, t)!,
|
||||
auraShadowHighlight: Color.lerp(
|
||||
auraShadowHighlight,
|
||||
other.auraShadowHighlight,
|
||||
t,
|
||||
)!,
|
||||
auraTint: Color.lerp(auraTint, other.auraTint, t)!,
|
||||
brandLink: Color.lerp(brandLink, other.brandLink, t)!,
|
||||
);
|
||||
}
|
||||
|
||||
/// Get AppColors from the current theme
|
||||
static AppColors of(BuildContext context) {
|
||||
return Theme.of(context).extension<AppColors>() ??
|
||||
switch (Theme.of(context).brightness) {
|
||||
Brightness.dark => dark,
|
||||
Brightness.light => light,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:riverpod/riverpod.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
|
||||
final class ErrorObserver extends ProviderObserver {
|
||||
const ErrorObserver();
|
||||
|
||||
@override
|
||||
void providerDidFail(
|
||||
ProviderObserverContext context,
|
||||
Object error,
|
||||
StackTrace stackTrace,
|
||||
) {
|
||||
logger.e('Provider ${context.provider} threw $error at $stackTrace');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart' as path_provider;
|
||||
import 'package:sqlite3/sqlite3.dart';
|
||||
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:weblibre/core/logger.dart';
|
||||
import 'package:weblibre/domain/entities/profile.dart';
|
||||
import 'package:weblibre/utils/filesystem.dart' as fs;
|
||||
|
||||
final filesystem = _Filesystem();
|
||||
|
||||
class _Filesystem {
|
||||
late final Directory dataDir;
|
||||
late final Directory profilesDir;
|
||||
|
||||
late final UuidValue selectedProfile;
|
||||
late final Directory selectedProfileDir;
|
||||
late final Directory profileDatabasesDir;
|
||||
|
||||
late final String relativeProfilePath;
|
||||
|
||||
Future<List<Directory>> getAvailableProfileDirectories() {
|
||||
return profilesDir.list().transform(fs.profileTransformer).toList();
|
||||
}
|
||||
|
||||
Future<Profile?> readProfileMetadata(Directory profileDir) {
|
||||
return fs.readProfileMetadata(profileDir);
|
||||
}
|
||||
|
||||
Directory getProfileDir(UuidValue uuid) {
|
||||
return fs.getProfileDir(profilesDir, uuid);
|
||||
}
|
||||
|
||||
Future<bool> createNewProfile(Profile profile) {
|
||||
return fs.createNewProfile(profilesDir, profile);
|
||||
}
|
||||
|
||||
Future<void> updateProfileMetadata(Profile profile) {
|
||||
return fs.writeProfileMetadata(getProfileDir(profile.uuidValue), profile);
|
||||
}
|
||||
|
||||
Future<void> setStartupProfile(UuidValue profile) {
|
||||
return fs.writeStartupProfile(profilesDir, profile, flush: true);
|
||||
}
|
||||
|
||||
Future<void> clearMozillaProfileCache(String profileId) {
|
||||
return fs.clearMozillaProfileCache(selectedProfileDir, profileId);
|
||||
}
|
||||
|
||||
List<String> getMozillaProfileIds(UuidValue uuid) {
|
||||
return fs.getMozillaProfileIds(getProfileDir(uuid));
|
||||
}
|
||||
|
||||
/// If the old canonical location `{profileDir}/mozilla/` exists as a real
|
||||
/// directory and the new location `{profileDir}/files/mozilla/` does not,
|
||||
/// rename the former to the latter.
|
||||
/// Returns `true` if a migration was performed.
|
||||
static Future<bool> _migrateMozillaDirToFiles(Directory profileDir) async {
|
||||
final oldDir = Directory(p.join(profileDir.path, 'mozilla'));
|
||||
final newDir = Directory(p.join(profileDir.path, 'files', 'mozilla'));
|
||||
|
||||
final oldType = await FileSystemEntity.type(
|
||||
oldDir.path,
|
||||
followLinks: false,
|
||||
);
|
||||
|
||||
if (oldType == FileSystemEntityType.directory && !await newDir.exists()) {
|
||||
await Directory(p.join(profileDir.path, 'files')).create(recursive: true);
|
||||
await oldDir.rename(newDir.path);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> _migrateGeckoCache(Directory profileDir) async {
|
||||
final profileIds = fs.getMozillaProfileIds(profileDir);
|
||||
final globalCacheDir = Directory(p.join(dataDir.path, 'cache'));
|
||||
|
||||
for (final profileId in profileIds) {
|
||||
final oldCache = Directory(p.join(globalCacheDir.path, profileId));
|
||||
final newCache = Directory(p.join(profileDir.path, 'cache', profileId));
|
||||
|
||||
if (await oldCache.exists() && !await newCache.exists()) {
|
||||
try {
|
||||
await oldCache.rename(newCache.path);
|
||||
} catch (e, s) {
|
||||
logger.w(
|
||||
'Failed to migrate Gecko cache for $profileId',
|
||||
error: e,
|
||||
stackTrace: s,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure the top-level `{filesDir}/mozilla` symlink points to the given
|
||||
/// profile's `files/mozilla/` directory. Old versions created this symlink
|
||||
/// targeting `{profile}/mozilla` which no longer exists after the migration
|
||||
/// moved it to `{profile}/files/mozilla`. GeckoView's `extensions.json`
|
||||
/// stores absolute paths through this symlink, so it must stay valid.
|
||||
static Future<void> _linkMozillaDir(
|
||||
Directory filesDir,
|
||||
Directory profileDir,
|
||||
) async {
|
||||
final mozillaDir = Directory(p.join(profileDir.path, 'files', 'mozilla'));
|
||||
await mozillaDir.create(recursive: true);
|
||||
|
||||
final mozillaPath = p.join(filesDir.path, 'mozilla');
|
||||
|
||||
final currentType = await FileSystemEntity.type(
|
||||
mozillaPath,
|
||||
followLinks: false,
|
||||
);
|
||||
|
||||
switch (currentType) {
|
||||
case FileSystemEntityType.notFound:
|
||||
break;
|
||||
case FileSystemEntityType.link:
|
||||
final link = Link(mozillaPath);
|
||||
try {
|
||||
if (await link.target() == mozillaDir.path) {
|
||||
return;
|
||||
}
|
||||
} on FileSystemException {
|
||||
// Replace unreadable or broken links.
|
||||
}
|
||||
await link.delete();
|
||||
default:
|
||||
// Move aside any non-link entity (directory, file, etc.)
|
||||
final backupPath = p.join(
|
||||
filesDir.path,
|
||||
'mozilla.backup.${DateTime.now().millisecondsSinceEpoch}',
|
||||
);
|
||||
if (currentType == FileSystemEntityType.directory) {
|
||||
await Directory(mozillaPath).rename(backupPath);
|
||||
} else {
|
||||
await File(mozillaPath).rename(backupPath);
|
||||
}
|
||||
}
|
||||
|
||||
await Link(mozillaPath).create(mozillaDir.path);
|
||||
}
|
||||
|
||||
/// Remove path-sensitive Gecko caches that may contain stale absolute paths.
|
||||
/// Gecko regenerates these on next startup.
|
||||
static Future<void> _healGeckoStartupCaches(Directory profileDir) async {
|
||||
final profileIds = fs.getMozillaProfileIds(profileDir);
|
||||
|
||||
for (final profileId in profileIds) {
|
||||
final mozProfileDir = Directory(
|
||||
p.join(profileDir.path, 'files', 'mozilla', profileId),
|
||||
);
|
||||
if (!await mozProfileDir.exists()) continue;
|
||||
|
||||
// addonStartup.json.lz4 caches absolute addon paths
|
||||
final addonStartup = File(
|
||||
p.join(mozProfileDir.path, 'addonStartup.json.lz4'),
|
||||
);
|
||||
if (await addonStartup.exists()) {
|
||||
try {
|
||||
await addonStartup.delete();
|
||||
logger.i('Cleared addonStartup.json.lz4 for $profileId');
|
||||
} catch (e, s) {
|
||||
logger.w(
|
||||
'Failed to clear addonStartup.json.lz4 for $profileId',
|
||||
error: e,
|
||||
stackTrace: s,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// startupCache may contain stale path references
|
||||
final startupCache = Directory(
|
||||
p.join(mozProfileDir.path, 'startupCache'),
|
||||
);
|
||||
if (await startupCache.exists()) {
|
||||
try {
|
||||
await startupCache.delete(recursive: true);
|
||||
logger.i('Cleared startupCache for $profileId');
|
||||
} catch (e, s) {
|
||||
logger.w(
|
||||
'Failed to clear startupCache for $profileId',
|
||||
error: e,
|
||||
stackTrace: s,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rewrite absolute paths in `extensions.json` that still reference the
|
||||
/// old pre-migration layout (`{filesDir}/mozilla/…`). After migration the
|
||||
/// real location is `{filesDir}/weblibre_profiles/profile-…/files/mozilla/…`
|
||||
/// and the top-level symlink bridges the two, but fixing the stored paths
|
||||
/// removes the permanent dependency on the symlink.
|
||||
/// Returns `true` if any paths were rewritten.
|
||||
static Future<bool> _migrateExtensionPaths(
|
||||
Directory filesDir,
|
||||
Directory profileDir,
|
||||
) async {
|
||||
final profileIds = fs.getMozillaProfileIds(profileDir);
|
||||
// Pattern: /data/user/0/eu.weblibre.gecko/files/mozilla/
|
||||
// Should become: /data/user/0/eu.weblibre.gecko/files/weblibre_profiles/profile-…/files/mozilla/
|
||||
final oldPrefix = '${filesDir.path}/mozilla/';
|
||||
final newPrefix = '${profileDir.path}/files/mozilla/';
|
||||
|
||||
if (oldPrefix == newPrefix) return false;
|
||||
|
||||
var migrated = false;
|
||||
|
||||
for (final profileId in profileIds) {
|
||||
final extensionsFile = File(
|
||||
p.join(
|
||||
profileDir.path,
|
||||
'files',
|
||||
'mozilla',
|
||||
profileId,
|
||||
'extensions.json',
|
||||
),
|
||||
);
|
||||
|
||||
if (!await extensionsFile.exists()) continue;
|
||||
|
||||
try {
|
||||
final content = await extensionsFile.readAsString();
|
||||
if (!content.contains(oldPrefix)) continue;
|
||||
|
||||
final json = jsonDecode(content);
|
||||
var changed = false;
|
||||
|
||||
if (json is Map<String, dynamic> && json['addons'] is List) {
|
||||
for (final addon in json['addons'] as List) {
|
||||
if (addon is! Map<String, dynamic>) continue;
|
||||
|
||||
final path = addon['path'];
|
||||
if (path is String && path.startsWith(oldPrefix)) {
|
||||
addon['path'] = path.replaceFirst(oldPrefix, newPrefix);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
final rootURI = addon['rootURI'];
|
||||
if (rootURI is String && rootURI.contains(oldPrefix)) {
|
||||
addon['rootURI'] = rootURI.replaceFirst(oldPrefix, newPrefix);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
await extensionsFile.writeAsString(jsonEncode(json), flush: true);
|
||||
logger.i('Migrated extension paths in $profileId/extensions.json');
|
||||
migrated = true;
|
||||
}
|
||||
} catch (e, s) {
|
||||
logger.w(
|
||||
'Failed to migrate extension paths for $profileId',
|
||||
error: e,
|
||||
stackTrace: s,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return migrated;
|
||||
}
|
||||
|
||||
/// Run all post-migration healing for a profile directory.
|
||||
/// Called during init for the active profile and after backup restore.
|
||||
Future<void> healProfile(Directory profileDir) async {
|
||||
final filesDir = profilesDir.parent;
|
||||
final dirMigrated = await _migrateMozillaDirToFiles(profileDir);
|
||||
await _migrateGeckoCache(profileDir);
|
||||
final pathsMigrated = await _migrateExtensionPaths(filesDir, profileDir);
|
||||
|
||||
// Only update the global symlink for the currently active profile —
|
||||
// restoring a non-active profile must not repoint it.
|
||||
final isActiveProfile = profileDir.path == selectedProfileDir.path;
|
||||
if (isActiveProfile) {
|
||||
await _linkMozillaDir(filesDir, profileDir);
|
||||
}
|
||||
|
||||
// Only clear Gecko startup caches when a path migration actually happened,
|
||||
// to avoid a recurring startup performance penalty.
|
||||
if (dirMigrated || pathsMigrated) {
|
||||
await _healGeckoStartupCaches(profileDir);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _setupSqliteCache() async {
|
||||
// Make sqlite3 pick a more suitable location for temporary files - the
|
||||
// one from the system may be inaccessible due to sandboxing.
|
||||
final cachebase = (await path_provider.getTemporaryDirectory()).path;
|
||||
// We can't access /tmp on Android, which sqlite3 would try by default.
|
||||
// Explicitly tell it about the correct temporary directory.
|
||||
sqlite3.tempDirectory = cachebase;
|
||||
}
|
||||
|
||||
Future<void> _copyDirectory(
|
||||
Directory source,
|
||||
Directory destination,
|
||||
bool Function(FileSystemEntity e) filter,
|
||||
) async {
|
||||
// Create destination directory
|
||||
await destination.create(recursive: true);
|
||||
|
||||
// List all contents
|
||||
await for (final entity in source.list().where(filter)) {
|
||||
final newPath = p.join(destination.path, p.basename(entity.path));
|
||||
|
||||
if (entity is Directory) {
|
||||
// Recursively copy subdirectory
|
||||
await _copyDirectory(entity, Directory(newPath), filter);
|
||||
} else if (entity is File) {
|
||||
// Copy file
|
||||
await entity.copy(newPath);
|
||||
} else if (entity is Link) {
|
||||
// Copy link
|
||||
await Link(newPath).create(await entity.target());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
final filesDir = await path_provider.getApplicationSupportDirectory();
|
||||
|
||||
dataDir = filesDir.parent;
|
||||
|
||||
profilesDir = Directory(p.join(filesDir.path, fs.profilesDirName));
|
||||
await profilesDir.create();
|
||||
|
||||
final selectedProfile = await fs.selectStartupProfile(profilesDir);
|
||||
if (selectedProfile == null) {
|
||||
final defaultProfile = Profile.create(name: 'Default');
|
||||
if (!await fs.createNewProfile(profilesDir, defaultProfile)) {
|
||||
throw Exception('Unable to create default profile');
|
||||
}
|
||||
|
||||
this.selectedProfile = defaultProfile.uuidValue;
|
||||
await fs.writeStartupProfile(profilesDir, defaultProfile.uuidValue);
|
||||
|
||||
final mozillaDir = Directory(p.join(filesDir.path, 'mozilla'));
|
||||
if (await mozillaDir.exists()) {
|
||||
final type = await FileSystemEntity.type(mozillaDir.path);
|
||||
if (type != FileSystemEntityType.link) {
|
||||
await _migrate(defaultProfile, mozillaDir, filesDir);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.selectedProfile = selectedProfile;
|
||||
}
|
||||
|
||||
relativeProfilePath = p.join(
|
||||
fs.profilesDirName,
|
||||
'${fs.profileDirPrefix}${this.selectedProfile.uuid}',
|
||||
);
|
||||
selectedProfileDir = Directory(p.join(filesDir.path, relativeProfilePath));
|
||||
await selectedProfileDir.create();
|
||||
|
||||
profileDatabasesDir = Directory(
|
||||
p.join(selectedProfileDir.path, 'databases'),
|
||||
);
|
||||
await profileDatabasesDir.create();
|
||||
|
||||
await healProfile(selectedProfileDir);
|
||||
await _setupSqliteCache();
|
||||
}
|
||||
|
||||
Future<void> _migrate(
|
||||
Profile defaultProfile,
|
||||
Directory mozillaDir,
|
||||
Directory filesDir,
|
||||
) async {
|
||||
final profileDir = getProfileDir(defaultProfile.uuidValue);
|
||||
|
||||
final newMozillaDir = Directory(
|
||||
p.join(profileDir.path, 'files', 'mozilla'),
|
||||
);
|
||||
await newMozillaDir.create(recursive: true);
|
||||
await mozillaDir.rename(newMozillaDir.path);
|
||||
|
||||
await _copyDirectory(
|
||||
filesDir,
|
||||
Directory(p.join(profileDir.path, 'files')),
|
||||
(e) => e is! Directory || p.basename(e.path) != fs.profilesDirName,
|
||||
);
|
||||
|
||||
final profileDatabasesDir = Directory(p.join(profileDir.path, 'databases'));
|
||||
|
||||
await _copyDirectory(
|
||||
Directory(p.join(dataDir.path, 'databases')),
|
||||
profileDatabasesDir,
|
||||
(e) => true,
|
||||
);
|
||||
|
||||
final dbFolder = await path_provider.getApplicationDocumentsDirectory();
|
||||
|
||||
final bangDb = File(p.join(dbFolder.path, 'bang3.db'));
|
||||
await bangDb.copy(p.join(profileDatabasesDir.path, 'bang.db'));
|
||||
final feedDb = File(p.join(dbFolder.path, 'feed.db'));
|
||||
await feedDb.copy(p.join(profileDatabasesDir.path, 'feed.db'));
|
||||
final tabDb = File(p.join(dbFolder.path, 'tab2.db'));
|
||||
await tabDb.copy(p.join(profileDatabasesDir.path, 'tab.db'));
|
||||
final userDb = File(p.join(dbFolder.path, 'user.db'));
|
||||
await userDb.copy(p.join(profileDatabasesDir.path, 'user.db'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:io';
|
||||
|
||||
import 'package:exceptions/exceptions.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
ErrorMessage handleHttpError(Exception exception, StackTrace stackTrace) {
|
||||
return switch (exception) {
|
||||
SocketException() => const ErrorMessage(
|
||||
source: 'http',
|
||||
message: 'Could not contact remote service',
|
||||
),
|
||||
HttpException() => const ErrorMessage(
|
||||
source: 'http',
|
||||
message: 'Web request returned error',
|
||||
),
|
||||
FormatException() => const ErrorMessage(
|
||||
source: 'http',
|
||||
message: 'Bad response format',
|
||||
),
|
||||
ClientException() => const ErrorMessage(
|
||||
source: 'http',
|
||||
message: 'Could not contact remote service',
|
||||
),
|
||||
_ => ErrorMessage.fromException(exception, stackTrace),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:logger/logger.dart';
|
||||
|
||||
class _DebugLogFilter extends LogFilter {
|
||||
@override
|
||||
bool shouldLog(LogEvent event) {
|
||||
//Log all events
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
final loggerMemory = MemoryOutput(
|
||||
bufferSize: 255,
|
||||
secondOutput: ConsoleOutput(),
|
||||
);
|
||||
final logger = Logger(
|
||||
filter: _DebugLogFilter(),
|
||||
printer: PrettyPrinter(dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart),
|
||||
output: loggerMemory,
|
||||
);
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:flutter/widgets.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'app_state.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class AppStateKey extends _$AppStateKey {
|
||||
void reset() {
|
||||
state = GlobalKey(debugLabel: 'RootKey');
|
||||
}
|
||||
|
||||
@override
|
||||
GlobalKey build() {
|
||||
return GlobalKey(debugLabel: 'RootKey');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'app_state.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(AppStateKey)
|
||||
final appStateKeyProvider = AppStateKeyProvider._();
|
||||
|
||||
final class AppStateKeyProvider
|
||||
extends $NotifierProvider<AppStateKey, GlobalKey<State<StatefulWidget>>> {
|
||||
AppStateKeyProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'appStateKeyProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$appStateKeyHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
AppStateKey create() => AppStateKey();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(GlobalKey<State<StatefulWidget>> value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<GlobalKey<State<StatefulWidget>>>(
|
||||
value,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$appStateKeyHash() => r'113c208139d84cdd52f623c2c6298b430fc1a889';
|
||||
|
||||
abstract class _$AppStateKey
|
||||
extends $Notifier<GlobalKey<State<StatefulWidget>>> {
|
||||
GlobalKey<State<StatefulWidget>> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref =
|
||||
this.ref
|
||||
as $Ref<
|
||||
GlobalKey<State<StatefulWidget>>,
|
||||
GlobalKey<State<StatefulWidget>>
|
||||
>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<
|
||||
GlobalKey<State<StatefulWidget>>,
|
||||
GlobalKey<State<StatefulWidget>>
|
||||
>,
|
||||
GlobalKey<State<StatefulWidget>>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:flutter/material.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/core/design/app_colors.dart';
|
||||
|
||||
part 'defaults.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Color lightSeedColorFallback(Ref ref) => AppColors.light.seedColor;
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Color darkSeedColorFallback(Ref ref) => AppColors.dark.seedColor;
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Uri docsUri(Ref ref) => Uri.parse('https://docs.weblibre.eu/');
|
||||
@@ -0,0 +1,134 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'defaults.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(lightSeedColorFallback)
|
||||
final lightSeedColorFallbackProvider = LightSeedColorFallbackProvider._();
|
||||
|
||||
final class LightSeedColorFallbackProvider
|
||||
extends $FunctionalProvider<Color, Color, Color>
|
||||
with $Provider<Color> {
|
||||
LightSeedColorFallbackProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'lightSeedColorFallbackProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$lightSeedColorFallbackHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<Color> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Color create(Ref ref) {
|
||||
return lightSeedColorFallback(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(Color value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<Color>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$lightSeedColorFallbackHash() =>
|
||||
r'851efdcb11e4367ea2e54f1884a73fd4cd841d4a';
|
||||
|
||||
@ProviderFor(darkSeedColorFallback)
|
||||
final darkSeedColorFallbackProvider = DarkSeedColorFallbackProvider._();
|
||||
|
||||
final class DarkSeedColorFallbackProvider
|
||||
extends $FunctionalProvider<Color, Color, Color>
|
||||
with $Provider<Color> {
|
||||
DarkSeedColorFallbackProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'darkSeedColorFallbackProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$darkSeedColorFallbackHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<Color> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Color create(Ref ref) {
|
||||
return darkSeedColorFallback(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(Color value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<Color>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$darkSeedColorFallbackHash() =>
|
||||
r'161a8c4318108c31d5c300441bc3332d08c24496';
|
||||
|
||||
@ProviderFor(docsUri)
|
||||
final docsUriProvider = DocsUriProvider._();
|
||||
|
||||
final class DocsUriProvider extends $FunctionalProvider<Uri, Uri, Uri>
|
||||
with $Provider<Uri> {
|
||||
DocsUriProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'docsUriProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$docsUriHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
$ProviderElement<Uri> $createElement($ProviderPointer pointer) =>
|
||||
$ProviderElement(pointer);
|
||||
|
||||
@override
|
||||
Uri create(Ref ref) {
|
||||
return docsUri(ref);
|
||||
}
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(Uri value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<Uri>(value),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _$docsUriHash() => r'6456efcf97ddc7ee87a67e2d3380f7241e3d76ea';
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:io';
|
||||
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'device_info.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class AndroidDeviceInfo extends _$AndroidDeviceInfo {
|
||||
@override
|
||||
Future<AndroidDeviceInfoData?> build() async {
|
||||
if (!Platform.isAndroid) return null;
|
||||
|
||||
final deviceInfo = DeviceInfoPlugin();
|
||||
final androidInfo = await deviceInfo.androidInfo;
|
||||
|
||||
return AndroidDeviceInfoData(sdkInt: androidInfo.version.sdkInt);
|
||||
}
|
||||
}
|
||||
|
||||
class AndroidDeviceInfoData {
|
||||
final int sdkInt;
|
||||
|
||||
const AndroidDeviceInfoData({required this.sdkInt});
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'device_info.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(AndroidDeviceInfo)
|
||||
final androidDeviceInfoProvider = AndroidDeviceInfoProvider._();
|
||||
|
||||
final class AndroidDeviceInfoProvider
|
||||
extends $AsyncNotifierProvider<AndroidDeviceInfo, AndroidDeviceInfoData?> {
|
||||
AndroidDeviceInfoProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'androidDeviceInfoProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$androidDeviceInfoHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
AndroidDeviceInfo create() => AndroidDeviceInfo();
|
||||
}
|
||||
|
||||
String _$androidDeviceInfoHash() => r'05c6cc63a6ee34f137aef538d65e8ab94b9cca89';
|
||||
|
||||
abstract class _$AndroidDeviceInfo
|
||||
extends $AsyncNotifier<AndroidDeviceInfoData?> {
|
||||
FutureOr<AndroidDeviceInfoData?> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref =
|
||||
this.ref
|
||||
as $Ref<AsyncValue<AndroidDeviceInfoData?>, AndroidDeviceInfoData?>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<
|
||||
AsyncValue<AndroidDeviceInfoData?>,
|
||||
AndroidDeviceInfoData?
|
||||
>,
|
||||
AsyncValue<AndroidDeviceInfoData?>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:intl/date_symbol_data_local.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'format.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class Format extends _$Format {
|
||||
String fullDateTime(DateTime date) {
|
||||
final pattern = DateFormat('yMMMMd').addPattern('Hm');
|
||||
|
||||
return pattern.format(date);
|
||||
}
|
||||
|
||||
String shortDate(DateTime date) {
|
||||
return DateFormat('yyyy-MM-dd').format(date);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> build() async {
|
||||
await initializeDateFormatting();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'format.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint, type=warning
|
||||
|
||||
@ProviderFor(Format)
|
||||
final formatProvider = FormatProvider._();
|
||||
|
||||
final class FormatProvider extends $AsyncNotifierProvider<Format, void> {
|
||||
FormatProvider._()
|
||||
: super(
|
||||
from: null,
|
||||
argument: null,
|
||||
retry: null,
|
||||
name: r'formatProvider',
|
||||
isAutoDispose: false,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$formatHash();
|
||||
|
||||
@$internal
|
||||
@override
|
||||
Format create() => Format();
|
||||
}
|
||||
|
||||
String _$formatHash() => r'695b94cc4b25596e4ef953d08ba954424a5ec33e';
|
||||
|
||||
abstract class _$Format extends $AsyncNotifier<void> {
|
||||
FutureOr<void> build();
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final ref = this.ref as $Ref<AsyncValue<void>, void>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<AsyncValue<void>, void>,
|
||||
AsyncValue<void>,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleCreate(ref, build);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* 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:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:weblibre/data/models/drag_data.dart';
|
||||
|
||||
part 'global_drop.g.dart';
|
||||
|
||||
@Riverpod()
|
||||
class WillAcceptDrop extends _$WillAcceptDrop {
|
||||
// ignore: use_setters_to_change_properties api decision
|
||||
void setData(DropTargetData data) {
|
||||
state = data;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
state = null;
|
||||
}
|
||||
|
||||
@override
|
||||
DropTargetData? build() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||