prepare for multiple apps
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
extension DateTimeFormat on DateTime {
|
||||
String _twoDigits(int n) => n.toString().padLeft(2, '0');
|
||||
|
||||
String formatWithMinutePrecision() {
|
||||
final year = this.year.toString();
|
||||
final month = _twoDigits(this.month);
|
||||
final day = _twoDigits(this.day);
|
||||
final hour = _twoDigits(this.hour);
|
||||
final minute = _twoDigits(this.minute);
|
||||
|
||||
return '$year-$month-$day $hour:$minute';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
extension DurationX on Duration {
|
||||
String formatTimeZoneOffset() {
|
||||
final hours = inHours.abs().toString().padLeft(2, '0');
|
||||
final minutes = (inMinutes.abs() % 60).toString().padLeft(2, '0');
|
||||
final sign = isNegative ? '-' : '+';
|
||||
|
||||
return '$sign$hours$minutes';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
|
||||
/// This is modified from default latin1 fallback to use utf8
|
||||
Encoding _encodingForHeaders(Map<String, String> headers) =>
|
||||
_encodingForContentTypeHeader(_contentTypeForHeaders(headers), utf8);
|
||||
|
||||
/// Returns the [MediaType] object for the given headers' content-type.
|
||||
///
|
||||
/// Defaults to `application/octet-stream`.
|
||||
MediaType _contentTypeForHeaders(Map<String, String> headers) {
|
||||
final contentType = headers['content-type'];
|
||||
if (contentType != null) return MediaType.parse(contentType);
|
||||
return MediaType('application', 'octet-stream');
|
||||
}
|
||||
|
||||
Encoding _encodingForContentTypeHeader(
|
||||
MediaType contentTypeHeader, [
|
||||
Encoding fallback = latin1,
|
||||
]) {
|
||||
final charset = contentTypeHeader.parameters['charset'];
|
||||
|
||||
// Default to utf8 for application/json when charset is unspecified.
|
||||
if (contentTypeHeader.type == 'application' &&
|
||||
contentTypeHeader.subtype == 'json' &&
|
||||
charset == null) {
|
||||
return utf8;
|
||||
}
|
||||
|
||||
// Attempt to find the encoding or fall back to the default.
|
||||
return charset != null ? Encoding.getByName(charset) ?? fallback : fallback;
|
||||
}
|
||||
|
||||
extension ResponesEncoding on Response {
|
||||
String get bodyUnicodeFallback =>
|
||||
_encodingForHeaders(headers).decode(bodyBytes);
|
||||
}
|
||||
@@ -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 'dart:ui';
|
||||
|
||||
import 'package:fast_equatable/hash.dart';
|
||||
|
||||
extension ImageHash on Image {
|
||||
Future<int?> calculateHash() async {
|
||||
final byteData = await toByteData();
|
||||
if (byteData != null) {
|
||||
final digest = secureHash(byteData);
|
||||
return digest;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Future<EquatableImage> toEquatable() {
|
||||
// return EquatableImage.calculate(this);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
extension UniqueItems<T> on Iterable<T> {
|
||||
Iterable<T> findDuplicates() sync* {
|
||||
final seen = <T>{};
|
||||
|
||||
for (final item in this) {
|
||||
if (seen.contains(item)) {
|
||||
yield item;
|
||||
} else {
|
||||
seen.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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:ui' as ui;
|
||||
|
||||
import 'package:intl/locale.dart' as intl;
|
||||
|
||||
extension LocaleFormat on intl.Locale {
|
||||
String rawToString(String separator) {
|
||||
final StringBuffer out = StringBuffer(languageCode);
|
||||
if (scriptCode != null && scriptCode!.isNotEmpty) {
|
||||
out.write('$separator$scriptCode');
|
||||
}
|
||||
final String? countryCode = this.countryCode;
|
||||
if (countryCode != null && countryCode.isNotEmpty) {
|
||||
out.write('$separator${this.countryCode}');
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
|
||||
extension LocaleConverter on ui.Locale {
|
||||
intl.Locale toIntlLocale() {
|
||||
return intl.Locale.fromSubtags(
|
||||
languageCode: languageCode,
|
||||
countryCode: countryCode,
|
||||
scriptCode: scriptCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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';
|
||||
|
||||
extension RelativeSafeArea on MediaQueryData {
|
||||
double relativeSafeArea() {
|
||||
return 1.0 - (padding.top / size.height);
|
||||
}
|
||||
}
|
||||
@@ -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 'dart:async';
|
||||
|
||||
import 'package:hooks_riverpod/misc.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
|
||||
extension CacheForExtension on Ref {
|
||||
/// Keeps the provider alive for [duration] after the last listener is removed.
|
||||
KeepAliveLink cacheFor(Duration duration) {
|
||||
Timer? timer;
|
||||
final link = keepAlive();
|
||||
|
||||
onCancel(() {
|
||||
// All listeners are gone — start the dispose timer.
|
||||
timer = Timer(duration, link.close);
|
||||
});
|
||||
|
||||
onResume(() {
|
||||
// A new listener was added — cancel the timer.
|
||||
timer?.cancel();
|
||||
});
|
||||
|
||||
onDispose(() {
|
||||
// Provider is being fully disposed — clean up.
|
||||
timer?.cancel();
|
||||
});
|
||||
|
||||
return link;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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/>.
|
||||
*/
|
||||
extension StringExtension on String {
|
||||
String toCapitalized() =>
|
||||
isEmpty ? this : '${this[0].toUpperCase()}${substring(1)}';
|
||||
|
||||
bool startsWithIgnoreCase(String prefix) =>
|
||||
toLowerCase().startsWith(prefix.toLowerCase());
|
||||
}
|
||||
@@ -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 'package:weblibre/utils/uri_policy.dart';
|
||||
|
||||
extension UriX on Uri {
|
||||
Uri get base => Uri.parse('$scheme://$authority');
|
||||
|
||||
bool get hasSupportedScheme =>
|
||||
allSupportedSchemes.any((s) => s.name == scheme);
|
||||
|
||||
bool get isHttp => isScheme('http');
|
||||
bool get isHttps => isScheme('https');
|
||||
bool get isHttpOrHttps => isHttp || isHttps;
|
||||
|
||||
bool get isLocalhost => host == 'localhost' || host == '127.0.0.1';
|
||||
|
||||
/// Removes a bare root path (`/`) when there is no query or fragment, so
|
||||
/// that `https://example.com/` and `https://example.com` are treated as
|
||||
/// equivalent.
|
||||
Uri get normalized {
|
||||
if (path == '/' && !hasQuery && !hasFragment) {
|
||||
return replace(path: '');
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user