- worst update eu west
This commit is contained in:
parent
f7777821fd
commit
346e447d5e
@ -1,5 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="f0ckapp"
|
android:label="f0ckapp"
|
||||||
@ -28,12 +29,26 @@
|
|||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<meta-data android:name="flutter_deeplinking_enabled" android:value="false"/>
|
||||||
|
<intent-filter android:autoVerify="true">
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
<data android:scheme="https" android:host="f0ck.me"/>
|
||||||
|
</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:scheme="f0ck" android:host="com.f0ck.f0ckapp"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Don't delete the meta-data below.
|
<!-- Don't delete the meta-data below.
|
||||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
<!-- Required to query activities that can process text, see:
|
<!-- Required to query activities that can process text, see:
|
||||||
https://developer.android.com/training/package-visibility and
|
https://developer.android.com/training/package-visibility and
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
@ -11,12 +12,14 @@ void main() async {
|
|||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
await AppVersion.init();
|
await AppVersion.init();
|
||||||
|
final Uri? initialUri = await AppLinks().getInitialLink();
|
||||||
|
|
||||||
runApp(ProviderScope(child: const F0ckApp()));
|
runApp(ProviderScope(child: F0ckApp(initialUri: initialUri)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class F0ckApp extends ConsumerWidget {
|
class F0ckApp extends ConsumerWidget {
|
||||||
const F0ckApp({super.key});
|
final Uri? initialUri;
|
||||||
|
const F0ckApp({super.key, this.initialUri});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@ -25,7 +28,7 @@ class F0ckApp extends ConsumerWidget {
|
|||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ref.watch(themeNotifierProvider),
|
theme: ref.watch(themeNotifierProvider),
|
||||||
home: const MediaGrid(),
|
home: MediaGrid(initialUri: initialUri),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -106,14 +106,16 @@ class MediaNotifier extends StateNotifier<MediaState> {
|
|||||||
state = state.replace(mediaItems: updated);
|
state = state.replace(mediaItems: updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> loadMedia() async {
|
Future<void> loadMedia({int? id}) async {
|
||||||
if (state.isLoading) return;
|
//if (state.isLoading) return;
|
||||||
|
if (id != null) {
|
||||||
|
print('requested id: ${id.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
state = state.replace(isLoading: true);
|
state = state.replace(isLoading: true);
|
||||||
try {
|
try {
|
||||||
final older = state.mediaItems.isNotEmpty
|
final older =
|
||||||
? state.mediaItems.last.id
|
id ?? (state.mediaItems.isNotEmpty ? state.mediaItems.last.id : null);
|
||||||
: null;
|
|
||||||
final newMedia = await fetchMedia(
|
final newMedia = await fetchMedia(
|
||||||
older: older,
|
older: older,
|
||||||
type: mediaTypes[state.typeIndex],
|
type: mediaTypes[state.typeIndex],
|
||||||
|
@ -102,6 +102,12 @@ class _DetailViewState extends ConsumerState<DetailView> {
|
|||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: Text('f0ck #${mediaState.mediaItems[_currentIndex].id}'),
|
title: Text('f0ck #${mediaState.mediaItems[_currentIndex].id}'),
|
||||||
actions: [
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.fullscreen),
|
||||||
|
onPressed: () {
|
||||||
|
// wip
|
||||||
|
},
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.download),
|
icon: Icon(Icons.download),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:f0ckapp/screens/DetailView.dart';
|
import 'package:f0ckapp/screens/DetailView.dart';
|
||||||
import 'package:f0ckapp/providers/MediaProvider.dart';
|
import 'package:f0ckapp/providers/MediaProvider.dart';
|
||||||
import 'package:f0ckapp/utils/AppVersion.dart';
|
import 'package:f0ckapp/utils/AppVersion.dart';
|
||||||
|
import 'package:f0ckapp/utils/ParseDeepLink.dart';
|
||||||
import 'package:f0ckapp/providers/ThemeProvider.dart';
|
import 'package:f0ckapp/providers/ThemeProvider.dart';
|
||||||
|
|
||||||
const List<String> mediaTypes = ["alles", "image", "video", "audio"];
|
const List<String> mediaTypes = ["alles", "image", "video", "audio"];
|
||||||
const List<String> mediaModes = ["sfw", "nsfw", "untagged", "all"];
|
const List<String> mediaModes = ["sfw", "nsfw", "untagged", "all"];
|
||||||
|
|
||||||
class MediaGrid extends ConsumerStatefulWidget {
|
class MediaGrid extends ConsumerStatefulWidget {
|
||||||
const MediaGrid({super.key});
|
final Uri? initialUri = null;
|
||||||
|
const MediaGrid({super.key, required initialUri});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<MediaGrid> createState() => _MediaGridState();
|
ConsumerState<MediaGrid> createState() => _MediaGridState();
|
||||||
@ -25,6 +28,8 @@ class _MediaGridState extends ConsumerState<MediaGrid> {
|
|||||||
final TextEditingController _usernameController = TextEditingController();
|
final TextEditingController _usernameController = TextEditingController();
|
||||||
final TextEditingController _passwordController = TextEditingController();
|
final TextEditingController _passwordController = TextEditingController();
|
||||||
|
|
||||||
|
final appLinks = AppLinks();
|
||||||
|
|
||||||
int _calculateCrossAxisCount(BuildContext context, int defaultCount) {
|
int _calculateCrossAxisCount(BuildContext context, int defaultCount) {
|
||||||
return defaultCount == 0
|
return defaultCount == 0
|
||||||
? (MediaQuery.of(context).size.width / 110).clamp(3, 5).toInt()
|
? (MediaQuery.of(context).size.width / 110).clamp(3, 5).toInt()
|
||||||
@ -43,6 +48,23 @@ class _MediaGridState extends ConsumerState<MediaGrid> {
|
|||||||
ref.read(mediaProvider.notifier).loadMedia();
|
ref.read(mediaProvider.notifier).loadMedia();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
appLinks.uriLinkStream.listen((Uri uri) async {
|
||||||
|
final parsedResult = parseDeepLink(uri);
|
||||||
|
if (parsedResult == null) return;
|
||||||
|
if (parsedResult['route'] != 'complex') return;
|
||||||
|
final params = parsedResult['params'] as Map<String, String>;
|
||||||
|
await handleComplexDeepLink(params, context, ref, _scrollController);
|
||||||
|
});
|
||||||
|
|
||||||
|
//print('initial: ${parseDeepLink(widget.initialUri)}');
|
||||||
|
Future.microtask(() async {
|
||||||
|
final initparsedResult = parseDeepLink(widget.initialUri);
|
||||||
|
if (initparsedResult == null) return;
|
||||||
|
if (initparsedResult['route'] != 'complex') return;
|
||||||
|
final initparams = initparsedResult['params'] as Map<String, String>;
|
||||||
|
await handleComplexDeepLink(initparams, context, ref, _scrollController);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
83
lib/utils/ParseDeepLink.dart
Normal file
83
lib/utils/ParseDeepLink.dart
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import 'package:f0ckapp/providers/MediaProvider.dart';
|
||||||
|
import 'package:f0ckapp/screens/DetailView.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
Map<String, RegExp> routes = {
|
||||||
|
//'login': RegExp(r'^/login/?$'),
|
||||||
|
//'user': RegExp(r'^/user/(?<user>.*)$'),
|
||||||
|
'complex': RegExp(
|
||||||
|
r'^/?'
|
||||||
|
r'(?:tag/(?<tag>.+?))?'
|
||||||
|
r'(?:/user/(?<username>.+?)/(?<set>f0cks|favs))?'
|
||||||
|
r'(?:/(?<media>image|audio|video))?'
|
||||||
|
r'(?:/(?<id>\d+))?'
|
||||||
|
r'$',
|
||||||
|
),
|
||||||
|
//'random': RegExp(r'^/random$'),
|
||||||
|
//'search': RegExp(r'^/search/?$'),
|
||||||
|
};
|
||||||
|
|
||||||
|
Map<String, dynamic> parseDeepLink(Uri? uri) {
|
||||||
|
if (uri == null) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
String url = uri.toString().replaceAll("https://f0ck.me", "");
|
||||||
|
|
||||||
|
for (final MapEntry<String, RegExp> entry in routes.entries) {
|
||||||
|
final String routeName = entry.key;
|
||||||
|
final RegExp pattern = entry.value;
|
||||||
|
final RegExpMatch? match = pattern.firstMatch(url.toString());
|
||||||
|
if (match != null) {
|
||||||
|
Map<String, String> params = <String, String>{};
|
||||||
|
for (String name in match.groupNames) {
|
||||||
|
params[name] = match.namedGroup(name) ?? '';
|
||||||
|
}
|
||||||
|
return {'route': routeName, 'params': params};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> handleComplexDeepLink(
|
||||||
|
Map<String, String> params,
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
ScrollController scrollController,
|
||||||
|
) async {
|
||||||
|
final media = params['media'];
|
||||||
|
const validMediaTypes = {'audio', 'video', 'image'};
|
||||||
|
if (media != null && validMediaTypes.contains(media)) {
|
||||||
|
ref.read(mediaProvider.notifier).setType(media);
|
||||||
|
}
|
||||||
|
|
||||||
|
final idParam = params['id'];
|
||||||
|
if (idParam == null || idParam.isEmpty) return;
|
||||||
|
|
||||||
|
final int? id = int.tryParse(idParam);
|
||||||
|
if (id == null) return;
|
||||||
|
|
||||||
|
final mediaState = ref.read(mediaProvider);
|
||||||
|
final index = mediaState.mediaItems.indexWhere((item) => item.id == id);
|
||||||
|
if (index == -1) {
|
||||||
|
await ref.read(mediaProvider.notifier).loadMedia(id: id + 50);
|
||||||
|
|
||||||
|
final updatedState = ref.read(mediaProvider);
|
||||||
|
final updatedIndex = updatedState.mediaItems.indexWhere(
|
||||||
|
(item) => item.id == id,
|
||||||
|
);
|
||||||
|
print(updatedIndex.toString());
|
||||||
|
if (updatedIndex == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool? navigationResult = await Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(builder: (_) => DetailView(initialItemId: id)),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (navigationResult == true) {
|
||||||
|
scrollController.jumpTo(0);
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,7 @@ class VideoControlsOverlay extends ConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Positioned(
|
Positioned(
|
||||||
right: 12,
|
right: 12,
|
||||||
top: 12,
|
bottom: 12,
|
||||||
child: _ControlButton(
|
child: _ControlButton(
|
||||||
mediaState.muted ? Icons.volume_off : Icons.volume_up,
|
mediaState.muted ? Icons.volume_off : Icons.volume_up,
|
||||||
() {
|
() {
|
||||||
@ -35,13 +35,6 @@ class VideoControlsOverlay extends ConsumerWidget {
|
|||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
|
||||||
right: 12,
|
|
||||||
bottom: 12,
|
|
||||||
child: _ControlButton(Icons.fullscreen, () {
|
|
||||||
button();
|
|
||||||
}, size: 16),
|
|
||||||
),
|
|
||||||
Center(
|
Center(
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
40
pubspec.lock
40
pubspec.lock
@ -1,6 +1,38 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
app_links:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: app_links
|
||||||
|
sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.4.0"
|
||||||
|
app_links_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_linux
|
||||||
|
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
app_links_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_platform_interface
|
||||||
|
sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.2"
|
||||||
|
app_links_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: app_links_web
|
||||||
|
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.4"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -240,6 +272,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
gtk:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: gtk
|
||||||
|
sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
html:
|
html:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 1.1.6+36
|
version: 1.1.7+37
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.9.0-100.2.beta
|
sdk: ^3.9.0-100.2.beta
|
||||||
@ -41,6 +41,7 @@ dependencies:
|
|||||||
share_plus: ^11.0.0
|
share_plus: ^11.0.0
|
||||||
flutter_secure_storage: ^9.2.4
|
flutter_secure_storage: ^9.2.4
|
||||||
flutter_riverpod: ^2.6.1
|
flutter_riverpod: ^2.6.1
|
||||||
|
app_links: ^6.4.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user