remove builds from db if pkg is deleted

fix apibuilder interval refreshes refreshing widgets not visible
This commit is contained in:
lukas-heiligenbrunner 2024-01-01 17:11:05 +01:00
parent fca5df4c70
commit 02fda58db4
15 changed files with 110 additions and 68 deletions

View File

@ -1,9 +1,6 @@
use crate::repo::repo::{remove_pkg, remove_version}; use crate::repo::repo::{remove_pkg, remove_version};
use rocket::serde::json::Json;
use rocket::serde::Deserialize;
use rocket::{post, State}; use rocket::{post, State};
use rocket_okapi::okapi::schemars; use rocket_okapi::openapi;
use rocket_okapi::{openapi, JsonSchema};
use sea_orm::DatabaseConnection; use sea_orm::DatabaseConnection;
#[openapi(tag = "test")] #[openapi(tag = "test")]

View File

@ -1,7 +1,7 @@
use crate::aur::aur::download_pkgbuild; use crate::aur::aur::download_pkgbuild;
use crate::db::prelude::Packages;
use crate::db::prelude::Versions; use crate::db::prelude::Versions;
use crate::db::versions; use crate::db::prelude::{Builds, Packages};
use crate::db::{builds, versions};
use crate::pkgbuild::build::build_pkgbuild; use crate::pkgbuild::build::build_pkgbuild;
use anyhow::anyhow; use anyhow::anyhow;
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, QueryFilter}; use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, QueryFilter};
@ -39,6 +39,49 @@ pub async fn add_pkg(
Ok(pkg_file_name) Ok(pkg_file_name)
} }
pub async fn remove_pkg(db: &DatabaseConnection, pkg_id: i32) -> anyhow::Result<()> {
let pkg = Packages::find_by_id(pkg_id)
.one(db)
.await?
.ok_or(anyhow!("id not found"))?;
fs::remove_dir_all(format!("./builds/{}", pkg.name))?;
let versions = Versions::find()
.filter(versions::Column::PackageId.eq(pkg.id))
.all(db)
.await?;
for v in versions {
rem_ver(db, v).await?;
}
// remove corresponding builds
let builds = Builds::find()
.filter(builds::Column::PkgId.eq(pkg.id))
.all(db)
.await?;
for b in builds {
b.delete(db).await?;
}
// remove package db entry
pkg.delete(db).await?;
Ok(())
}
pub async fn remove_version(db: &DatabaseConnection, version_id: i32) -> anyhow::Result<()> {
let version = Versions::find()
.filter(versions::Column::PackageId.eq(version_id))
.one(db)
.await?;
if let Some(version) = version {
rem_ver(db, version).await?;
}
Ok(())
}
fn repo_add(pkg_file_name: String) -> anyhow::Result<()> { fn repo_add(pkg_file_name: String) -> anyhow::Result<()> {
let db_file = format!("{REPO_NAME}.db.tar.gz"); let db_file = format!("{REPO_NAME}.db.tar.gz");
@ -79,40 +122,6 @@ fn repo_remove(pkg_file_name: String) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
pub async fn remove_pkg(db: &DatabaseConnection, pkg_id: i32) -> anyhow::Result<()> {
let pkg = Packages::find_by_id(pkg_id)
.one(db)
.await?
.ok_or(anyhow!("id not found"))?;
fs::remove_dir_all(format!("./builds/{}", pkg.name))?;
let versions = Versions::find()
.filter(versions::Column::PackageId.eq(pkg.id))
.all(db)
.await?;
for v in versions {
rem_ver(db, v).await?;
}
// remove package db entry
pkg.delete(db).await?;
Ok(())
}
pub async fn remove_version(db: &DatabaseConnection, version_id: i32) -> anyhow::Result<()> {
let version = Versions::find()
.filter(versions::Column::PackageId.eq(version_id))
.one(db)
.await?;
if let Some(version) = version {
rem_ver(db, version).await?;
}
Ok(())
}
async fn rem_ver(db: &DatabaseConnection, version: versions::Model) -> anyhow::Result<()> { async fn rem_ver(db: &DatabaseConnection, version: versions::Model) -> anyhow::Result<()> {
if let Some(filename) = version.file_name.clone() { if let Some(filename) = version.file_name.clone() {
// so repo-remove only supports passing a package name and removing the whole package // so repo-remove only supports passing a package name and removing the whole package

View File

@ -2,8 +2,9 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:visibility_detector/visibility_detector.dart';
import 'BaseProvider.dart'; import '../../providers/BaseProvider.dart';
class APIBuilder<T extends BaseProvider, K, DTO> extends StatefulWidget { class APIBuilder<T extends BaseProvider, K, DTO> extends StatefulWidget {
const APIBuilder( const APIBuilder(
@ -25,6 +26,7 @@ class APIBuilder<T extends BaseProvider, K, DTO> extends StatefulWidget {
class _APIBuilderState<T extends BaseProvider, K, DTO> class _APIBuilderState<T extends BaseProvider, K, DTO>
extends State<APIBuilder<T, K, DTO>> { extends State<APIBuilder<T, K, DTO>> {
Timer? timer; Timer? timer;
bool visible = true;
@override @override
void initState() { void initState() {
@ -33,12 +35,10 @@ class _APIBuilderState<T extends BaseProvider, K, DTO>
if (widget.interval != null) { if (widget.interval != null) {
timer = Timer.periodic(widget.interval!, (Timer t) { timer = Timer.periodic(widget.interval!, (Timer t) {
final RenderObject? box = context.findRenderObject(); if (visible) {
print(box); Provider.of<T>(context, listen: false)
print(context.mounted); .refresh(context, dto: widget.dto);
}
Provider.of<T>(context, listen: false)
.refresh(context, dto: widget.dto);
}); });
} }
} }
@ -53,15 +53,30 @@ class _APIBuilderState<T extends BaseProvider, K, DTO>
Widget build(BuildContext context) { Widget build(BuildContext context) {
final Future<K> fut = Provider.of<T>(context).data as Future<K>; final Future<K> fut = Provider.of<T>(context).data as Future<K>;
return FutureBuilder<K>( return VisibilityDetector(
future: fut, key: widget.key ?? const Key("APIBuilder"),
builder: (context, snapshot) { onVisibilityChanged: (visibilityInfo) {
if (snapshot.hasData) { var visiblePercentage = visibilityInfo.visibleFraction * 100;
return widget.onData(snapshot.data!);
} else { if (mounted) {
return widget.onLoad(); setState(() {
visible = visiblePercentage != 0;
});
} }
debugPrint(
'Widget ${visibilityInfo.key} is ${visiblePercentage}% visible');
}, },
child: FutureBuilder<K>(
future: fut,
builder: (context, snapshot) {
if (snapshot.hasData) {
return widget.onData(snapshot.data!);
} else {
return widget.onLoad();
}
},
),
); );
} }
} }

View File

@ -4,7 +4,7 @@ import 'package:aurcache/api/builds.dart';
import 'package:aurcache/components/builds_table.dart'; import 'package:aurcache/components/builds_table.dart';
import 'package:aurcache/models/build.dart'; import 'package:aurcache/models/build.dart';
import 'package:aurcache/components/dashboard/your_packages.dart'; import 'package:aurcache/components/dashboard/your_packages.dart';
import 'package:aurcache/providers/APIBuilder.dart'; import 'package:aurcache/components/api/APIBuilder.dart';
import 'package:aurcache/providers/builds_provider.dart'; import 'package:aurcache/providers/builds_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -41,6 +41,7 @@ class _RecentBuildsState extends State<RecentBuilds> {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: APIBuilder<BuildsProvider, List<Build>, BuildsDTO>( child: APIBuilder<BuildsProvider, List<Build>, BuildsDTO>(
key: const Key("Builds on dashboard"),
dto: BuildsDTO(limit: 10), dto: BuildsDTO(limit: 10),
interval: const Duration(seconds: 10), interval: const Duration(seconds: 10),
onLoad: () => const Text("no data"), onLoad: () => const Text("no data"),

View File

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:aurcache/api/packages.dart'; import 'package:aurcache/api/packages.dart';
import 'package:aurcache/providers/APIBuilder.dart'; import 'package:aurcache/components/api/APIBuilder.dart';
import 'package:aurcache/providers/packages_provider.dart'; import 'package:aurcache/providers/packages_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -44,7 +44,7 @@ class _YourPackagesState extends State<YourPackages> {
child: SizedBox( child: SizedBox(
width: double.infinity, width: double.infinity,
child: APIBuilder<PackagesProvider, List<Package>, Object>( child: APIBuilder<PackagesProvider, List<Package>, Object>(
key: GlobalKey(), key: Key("Packages on dashboard"),
interval: const Duration(seconds: 10), interval: const Duration(seconds: 10),
onData: (data) { onData: (data) {
return DataTable( return DataTable(

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils/responsive.dart'; import '../../utils/responsive.dart';
import '../screens/dashboard_screen.dart'; import '../../screens/dashboard_screen.dart';
import 'side_menu.dart'; import 'side_menu.dart';
class MenuShell extends StatelessWidget { class MenuShell extends StatelessWidget {

View File

@ -1,7 +1,7 @@
import 'package:aurcache/screens/build_screen.dart'; import 'package:aurcache/screens/build_screen.dart';
import 'package:aurcache/screens/builds_screen.dart'; import 'package:aurcache/screens/builds_screen.dart';
import 'package:aurcache/screens/dashboard_screen.dart'; import 'package:aurcache/screens/dashboard_screen.dart';
import 'package:aurcache/components/menu_shell.dart'; import 'package:aurcache/components/routing/menu_shell.dart';
import 'package:aurcache/screens/package_screen.dart'; import 'package:aurcache/screens/package_screen.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../constants/color_constants.dart'; import '../../constants/color_constants.dart';
class SideMenu extends StatelessWidget { class SideMenu extends StatelessWidget {
const SideMenu({ const SideMenu({
@ -86,7 +86,7 @@ class DrawerListTile extends StatelessWidget {
), ),
title: Text( title: Text(
title, title,
style: TextStyle(color: Colors.white54), style: const TextStyle(color: Colors.white54),
), ),
); );
} }

View File

@ -1,4 +1,4 @@
import 'package:aurcache/components/router.dart'; import 'package:aurcache/components/routing/router.dart';
import 'package:aurcache/providers/build_provider.dart'; import 'package:aurcache/providers/build_provider.dart';
import 'package:aurcache/providers/builds_provider.dart'; import 'package:aurcache/providers/builds_provider.dart';
import 'package:aurcache/providers/package_provider.dart'; import 'package:aurcache/providers/package_provider.dart';

View File

@ -3,7 +3,7 @@ import 'dart:async';
import 'package:aurcache/api/builds.dart'; import 'package:aurcache/api/builds.dart';
import 'package:aurcache/components/build_output.dart'; import 'package:aurcache/components/build_output.dart';
import 'package:aurcache/models/build.dart'; import 'package:aurcache/models/build.dart';
import 'package:aurcache/providers/APIBuilder.dart'; import 'package:aurcache/components/api/APIBuilder.dart';
import 'package:aurcache/providers/build_provider.dart'; import 'package:aurcache/providers/build_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';

View File

@ -1,5 +1,5 @@
import 'package:aurcache/components/builds_table.dart'; import 'package:aurcache/components/builds_table.dart';
import 'package:aurcache/providers/APIBuilder.dart'; import 'package:aurcache/components/api/APIBuilder.dart';
import 'package:aurcache/providers/builds_provider.dart'; import 'package:aurcache/providers/builds_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../constants/color_constants.dart'; import '../constants/color_constants.dart';

View File

@ -1,5 +1,5 @@
import 'package:aurcache/api/statistics.dart'; import 'package:aurcache/api/statistics.dart';
import 'package:aurcache/providers/APIBuilder.dart'; import 'package:aurcache/components/api/APIBuilder.dart';
import 'package:aurcache/providers/stats_provider.dart'; import 'package:aurcache/providers/stats_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View File

@ -2,10 +2,11 @@ import 'dart:async';
import 'package:aurcache/api/builds.dart'; import 'package:aurcache/api/builds.dart';
import 'package:aurcache/api/packages.dart'; import 'package:aurcache/api/packages.dart';
import 'package:aurcache/providers/APIBuilder.dart'; import 'package:aurcache/components/api/APIBuilder.dart';
import 'package:aurcache/providers/builds_provider.dart'; import 'package:aurcache/providers/builds_provider.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../api/API.dart'; import '../api/API.dart';
import '../components/builds_table.dart'; import '../components/builds_table.dart';
@ -15,6 +16,7 @@ import '../models/build.dart';
import '../models/package.dart'; import '../models/package.dart';
import '../providers/package_provider.dart'; import '../providers/package_provider.dart';
import '../providers/packages_provider.dart'; import '../providers/packages_provider.dart';
import '../providers/stats_provider.dart';
class PackageScreen extends StatefulWidget { class PackageScreen extends StatefulWidget {
const PackageScreen({super.key, required this.pkgID}); const PackageScreen({super.key, required this.pkgID});
@ -61,6 +63,15 @@ class _PackageScreenState extends State<PackageScreen> {
final succ = await API.deletePackage(pkg.id); final succ = await API.deletePackage(pkg.id);
if (succ) { if (succ) {
context.pop(); context.pop();
Provider.of<PackagesProvider>(context,
listen: false)
.refresh(context);
Provider.of<BuildsProvider>(context,
listen: false)
.refresh(context);
Provider.of<StatsProvider>(context, listen: false)
.refresh(context);
} }
}, },
child: const Text( child: const Text(
@ -92,7 +103,7 @@ class _PackageScreenState extends State<PackageScreen> {
width: double.infinity, width: double.infinity,
child: APIBuilder<BuildsProvider, List<Build>, child: APIBuilder<BuildsProvider, List<Build>,
BuildsDTO>( BuildsDTO>(
key: GlobalKey(), key: const Key("Builds on Package info"),
dto: BuildsDTO(pkgID: pkg.id), dto: BuildsDTO(pkgID: pkg.id),
interval: const Duration(seconds: 5), interval: const Duration(seconds: 5),
onData: (data) { onData: (data) {

View File

@ -405,6 +405,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
visibility_detector:
dependency: "direct main"
description:
name: visibility_detector
sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420
url: "https://pub.dev"
source: hosted
version: "0.4.0+2"
web: web:
dependency: transitive dependency: transitive
description: description:

View File

@ -41,6 +41,7 @@ dependencies:
dio: ^5.3.3 dio: ^5.3.3
go_router: ^13.0.0 go_router: ^13.0.0
provider: ^6.1.1 provider: ^6.1.1
visibility_detector: ^0.4.0+2
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: