Compare commits
38 Commits
v1.0.27+27
...
v1.3.0+56
Author | SHA1 | Date | |
---|---|---|---|
14081489cc | |||
2a500144f5 | |||
9655f15927 | |||
dff9cda829 | |||
16ebc51e77 | |||
7981436374 | |||
e38d2086b3 | |||
a4d50289c2 | |||
82fb23dbfd | |||
13f957f016 | |||
707f14c5fb | |||
493422e724 | |||
3b95d128e1 | |||
57636c5de6 | |||
f75299f0d4 | |||
03c6431eca | |||
5876c809a5 | |||
c35308fbc1 | |||
e945844151 | |||
74eb6e3d26 | |||
9755066d1e | |||
671b3cfbe0 | |||
93fb3536ee | |||
346e447d5e | |||
f7777821fd | |||
ffbde73300 | |||
836a0886e2 | |||
1cd10b3809 | |||
43c42ac0d5 | |||
bf4e0fa493 | |||
27476fbc1d | |||
8e9f0eb1b8 | |||
f083fc8e8f | |||
9a716018fc | |||
f1eb52518b | |||
c7d996a402 | |||
ee93ef576b | |||
78ff1953ad |
@ -3,7 +3,7 @@ name: Flutter Schmutter
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -51,3 +51,11 @@ jobs:
|
|||||||
files: |-
|
files: |-
|
||||||
build/app/outputs/flutter-apk/app-release.apk
|
build/app/outputs/flutter-apk/app-release.apk
|
||||||
token: '${{secrets.RELEASE_TOKEN}}'
|
token: '${{secrets.RELEASE_TOKEN}}'
|
||||||
|
|
||||||
|
- name: upload apk to f-droid server
|
||||||
|
run: |
|
||||||
|
BUILD_NUMBER=$(grep '^version:' pubspec.yaml | sed 's/.*+//')
|
||||||
|
curl -X POST "https://flumm.io/pullfdroid.php" \
|
||||||
|
-F "token=${{ secrets.PULLER_TOKEN }}" \
|
||||||
|
-F "apk=@build/app/outputs/flutter-apk/app-release.apk" \
|
||||||
|
-F "build=$BUILD_NUMBER"
|
||||||
|
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 f0ck
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
52
README.md
@ -1,16 +1,48 @@
|
|||||||
# f0ckapp
|
# fApp
|
||||||
|

|
||||||
|
## Overview
|
||||||
|
|
||||||
A new Flutter project.
|
fApp is the mobile application for the website [f0ck.me](https://f0ck.me). This app provides a user-friendly interface to access the content of the website and utilize its features conveniently from your mobile device.
|
||||||
|
|
||||||
## Getting Started
|
## Installation
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
fApp is available in its own F-Droid repository.
|
||||||
|
|
||||||
A few resources to get you started if this is your first Flutter project:
|
### Installation Steps
|
||||||
|
|
||||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
1. Add the F-Droid repository to your F-Droid app:
|
||||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
- Go to the settings in the F-Droid app.
|
||||||
|
- Select "Repositories" and add the URL `https://fdroid.flumm.io/fdroid/repo/`.
|
||||||
|
|
||||||
For help getting started with Flutter development, view the
|
2. Search for "f0ckapp" in the F-Droid app and install the app.
|
||||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
|
||||||
samples, guidance on mobile development, and a full API reference.
|
## Development
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Flutter SDK
|
||||||
|
- Dart SDK
|
||||||
|
- Android Studio or Visual Studio Code
|
||||||
|
|
||||||
|
### Setting Up the Project
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone https://git.lat/f0ck/fApp.git
|
||||||
|
cd fApp
|
||||||
|
2. Install dependencies:
|
||||||
|
```flutter pub get```
|
||||||
|
3. Run the app:
|
||||||
|
```flutter run```
|
||||||
|
|
||||||
|
### Contributing
|
||||||
|
|
||||||
|
don't.
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License. See the LICENSE file for more information.
|
||||||
|
|
||||||
|
### Contact
|
||||||
|
|
||||||
|
For questions or feedback, you can reach us at [contact@f0ck.me](mailto:contact@f0ck.me).
|
||||||
|
@ -11,12 +11,12 @@ android {
|
|||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
jvmTarget = JavaVersion.VERSION_17.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<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.WAKE_LOCK"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
<application
|
<application
|
||||||
android:label="f0ckapp"
|
android:label="f0ck"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:enableOnBackInvokedCallback="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@ -13,8 +15,7 @@
|
|||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize">
|
||||||
android:requestLegacyExternalStorage="true">
|
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
@ -27,12 +28,19 @@
|
|||||||
<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>
|
||||||
|
<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>
|
||||||
</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,5 +1,69 @@
|
|||||||
package com.f0ck.f0ckapp
|
package com.f0ck.f0ckapp
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import androidx.annotation.NonNull
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
|
||||||
class MainActivity : FlutterActivity()
|
class MainActivity : FlutterActivity() {
|
||||||
|
private val CHANNEL = "MediaShit"
|
||||||
|
|
||||||
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine): Unit {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
|
||||||
|
call,
|
||||||
|
result ->
|
||||||
|
if (call.method == "saveFile") {
|
||||||
|
val filePath = call.argument<String>("filePath")
|
||||||
|
val fileName = call.argument<String>("fileName")
|
||||||
|
val subDir = call.argument<String?>("subDir")
|
||||||
|
|
||||||
|
if (filePath == null || fileName == null)
|
||||||
|
result.error("SAVE_FAILED", "file not found", null)
|
||||||
|
|
||||||
|
if (!saveFileUsingMediaStore(applicationContext, filePath!!, fileName!!, subDir))
|
||||||
|
result.error("COPY_FAILED", "Datei konnte nicht gespeichert werden", null)
|
||||||
|
result.success(true)
|
||||||
|
} else result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun saveFileUsingMediaStore(
|
||||||
|
context: Context,
|
||||||
|
filePath: String,
|
||||||
|
fileName: String,
|
||||||
|
subDir: String?
|
||||||
|
|
||||||
|
): Boolean {
|
||||||
|
val srcFile = File(filePath)
|
||||||
|
if (!srcFile.exists()) return false
|
||||||
|
|
||||||
|
val values =
|
||||||
|
ContentValues().apply {
|
||||||
|
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
|
||||||
|
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + (subDir ?: "f0ck"))
|
||||||
|
put(MediaStore.MediaColumns.IS_PENDING, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val resolver = context.contentResolver
|
||||||
|
val collection = MediaStore.Downloads.EXTERNAL_CONTENT_URI
|
||||||
|
val uri = resolver.insert(collection, values) ?: return false
|
||||||
|
|
||||||
|
resolver.openOutputStream(uri).use { out ->
|
||||||
|
FileInputStream(srcFile).use { input -> input.copyTo(out!!, 4096) }
|
||||||
|
}
|
||||||
|
|
||||||
|
values.clear()
|
||||||
|
values.put(MediaStore.MediaColumns.IS_PENDING, 0)
|
||||||
|
resolver.update(uri, values, null, null)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||||
|
</adaptive-icon>
|
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 3.5 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png
Normal file
After Width: | Height: | Size: 839 B |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 1.9 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png
Normal file
After Width: | Height: | Size: 446 B |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.2 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 75 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 133 KiB |
After Width: | Height: | Size: 133 KiB |
BIN
android/app/src/main/res/play_store_512.png
Normal file
After Width: | Height: | Size: 345 KiB |
BIN
assets/images/f0ck_small.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 447 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@2x.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.7 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-20@3x.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@2x.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 7.0 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-29@3x.png
Normal file
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 2.2 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@2x.png
Normal file
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon-40@3x.png
Normal file
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 43 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@2x.png
Normal file
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 36 KiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon@3x.png
Normal file
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 1.1 MiB |
BIN
ios/Runner/Assets.xcassets/AppIcon.appiconset/AppIcon~ipad.png
Normal file
After Width: | Height: | Size: 11 KiB |
110
lib/controller/media_controller.dart
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import 'package:encrypt_shared_preferences/provider.dart';
|
||||||
|
import 'package:f0ckapp/utils/animatedtransition.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'package:f0ckapp/service/media_service.dart';
|
||||||
|
import 'package:f0ckapp/models/media_item.dart';
|
||||||
|
|
||||||
|
class MediaController extends GetxController {
|
||||||
|
final EncryptedSharedPreferencesAsync storage =
|
||||||
|
EncryptedSharedPreferencesAsync.getInstance();
|
||||||
|
|
||||||
|
final RxList<MediaItem> mediaItems = <MediaItem>[].obs;
|
||||||
|
final RxBool isLoading = false.obs;
|
||||||
|
final RxString errorMessage = ''.obs;
|
||||||
|
final MediaService _mediaService = MediaService();
|
||||||
|
|
||||||
|
RxnString tag = RxnString();
|
||||||
|
RxInt type = 0.obs;
|
||||||
|
RxInt mode = 0.obs;
|
||||||
|
RxBool random = false.obs;
|
||||||
|
late RxBool muted = false.obs;
|
||||||
|
late RxInt crossAxisCount = 0.obs;
|
||||||
|
late RxBool drawerSwipeEnabled = true.obs;
|
||||||
|
final Rx<PageTransition> transitionType = PageTransition.opacity.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() async {
|
||||||
|
super.onInit();
|
||||||
|
await loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadSettings() async {
|
||||||
|
muted.value = await storage.getBoolean('muted') ?? false;
|
||||||
|
crossAxisCount.value = await storage.getInt('crossAxisCount') ?? 0;
|
||||||
|
drawerSwipeEnabled.value =
|
||||||
|
await storage.getBoolean('drawerSwipeEnabled') ?? true;
|
||||||
|
transitionType.value =
|
||||||
|
PageTransition.values[await storage.getInt('transitionType') ?? 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveSettings() async {
|
||||||
|
await storage.setBoolean('muted', muted.value);
|
||||||
|
await storage.setInt('crossAxisCount', crossAxisCount.value);
|
||||||
|
await storage.setBoolean('drawerSwipeEnabled', drawerSwipeEnabled.value);
|
||||||
|
await storage.setInt('transitionType', transitionType.value.index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setTag(String? newTag) async {
|
||||||
|
tag.value = newTag;
|
||||||
|
await loadMediaItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setType(int newType) async {
|
||||||
|
type.value = newType;
|
||||||
|
await loadMediaItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setMode(int newMode) async {
|
||||||
|
mode.value = newMode;
|
||||||
|
await loadMediaItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> toggleRandom() async {
|
||||||
|
random.value = !random.value;
|
||||||
|
await loadMediaItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> toggleMuted() async {
|
||||||
|
muted.value = !muted.value;
|
||||||
|
await saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setCrossAxisCount(int newCrossAxisCount) async {
|
||||||
|
crossAxisCount.value = newCrossAxisCount;
|
||||||
|
await saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setDrawerSwipeEnabled(bool newValue) async {
|
||||||
|
drawerSwipeEnabled.value = newValue;
|
||||||
|
await saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setTransitionType(PageTransition newType) async {
|
||||||
|
transitionType.value = newType;
|
||||||
|
await saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadMediaItems({int? older, bool append = false}) async {
|
||||||
|
if (isLoading.value) return;
|
||||||
|
try {
|
||||||
|
isLoading.value = true;
|
||||||
|
final List<MediaItem> items = await _mediaService.fetchMediaItems(
|
||||||
|
type: type.value,
|
||||||
|
mode: mode.value,
|
||||||
|
random: random.value ? 1 : 0,
|
||||||
|
tag: tag.value,
|
||||||
|
older: older,
|
||||||
|
);
|
||||||
|
|
||||||
|
append ? mediaItems.addAll(items) : mediaItems.assignAll(items);
|
||||||
|
|
||||||
|
errorMessage.value = '';
|
||||||
|
} catch (e) {
|
||||||
|
errorMessage.value = 'Fehler beim Laden der Daten: ${e.toString()}';
|
||||||
|
Get.snackbar('Error', e.toString());
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
289
lib/controller/theme_controller.dart
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:encrypt_shared_preferences/provider.dart';
|
||||||
|
|
||||||
|
final ThemeData f0ckTheme = ThemeData(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
primaryColor: const Color(0xFF9FFF00),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFF000000),
|
||||||
|
colorScheme: const ColorScheme.dark(
|
||||||
|
primary: Color(0xFF9FFF00),
|
||||||
|
secondary: Color(0xFF262626),
|
||||||
|
surface: Color(0xFF232323),
|
||||||
|
onPrimary: Color(0xFF000000),
|
||||||
|
onSecondary: Colors.white,
|
||||||
|
onSurface: Colors.white,
|
||||||
|
),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Color(0xFF2B2B2B),
|
||||||
|
foregroundColor: Color(0xFF9FFF00),
|
||||||
|
elevation: 2,
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Colors.white),
|
||||||
|
bodyMedium: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: const Color(0xFF000000),
|
||||||
|
backgroundColor: const Color(0xFF9FFF00),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
thumbColor: WidgetStateProperty.all<Color>(const Color(0xFF2B2B2B)),
|
||||||
|
trackColor: WidgetStateProperty.all<Color>(const Color(0xFF424242)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData p1nkTheme = ThemeData(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
primaryColor: const Color(0xFF171717),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFF171717),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
color: Color(0xFF2B2B2B),
|
||||||
|
foregroundColor: Color(0xFFFF00D0),
|
||||||
|
elevation: 2,
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Color(0xFFFFFFFF)),
|
||||||
|
bodyMedium: TextStyle(color: Color(0xFFFFFFFF)),
|
||||||
|
titleLarge: TextStyle(color: Color(0xFFFFFFFF)),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: const Color(0xFF000000),
|
||||||
|
backgroundColor: const Color(0xFFFF00D0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
||||||
|
color: Color(0xFFFF00D0),
|
||||||
|
),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
thumbColor: WidgetStateProperty.all(const Color(0xFF2B2B2B)),
|
||||||
|
trackColor: WidgetStateProperty.all(const Color(0xFF424242)),
|
||||||
|
),
|
||||||
|
colorScheme: const ColorScheme.dark(
|
||||||
|
primary: Color(0xFF171717),
|
||||||
|
secondary: Color(0xFFFF00D0),
|
||||||
|
surface: Color(0xFF171717),
|
||||||
|
onPrimary: Color(0xFFFFFFFF),
|
||||||
|
onSecondary: Color(0xFF000000),
|
||||||
|
onSurface: Color(0xFFFFFFFF),
|
||||||
|
error: Color(0xFFA72828),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData paperTheme = ThemeData(
|
||||||
|
brightness: Brightness.light,
|
||||||
|
primaryColor: const Color(0xFF000000),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFFFFFFFF),
|
||||||
|
colorScheme: const ColorScheme.light(
|
||||||
|
primary: Color(0xFF000000),
|
||||||
|
secondary: Color(0xFF262626),
|
||||||
|
surface: Color(0xFFFFFFFF),
|
||||||
|
onPrimary: Colors.white,
|
||||||
|
onSecondary: Colors.white,
|
||||||
|
onSurface: Color(0xFF000000),
|
||||||
|
),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Color(0xFFFFFFFF),
|
||||||
|
foregroundColor: Color(0xFF000000),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Color(0xFF000000)),
|
||||||
|
bodyMedium: TextStyle(color: Color(0xFF000000)),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: const Color(0xFFFFFFFF),
|
||||||
|
backgroundColor: const Color(0xFF000000),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData orangeTheme = ThemeData(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
primaryColor: const Color(0xFFFF6F00),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFF171717),
|
||||||
|
colorScheme: const ColorScheme.dark(
|
||||||
|
primary: Color(0xFFFF6F00),
|
||||||
|
secondary: Color(0xFF262626),
|
||||||
|
surface: Color(0xFF232323),
|
||||||
|
onPrimary: Colors.white,
|
||||||
|
onSecondary: Colors.white,
|
||||||
|
onSurface: Colors.white,
|
||||||
|
),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Color(0xFF2B2B2B),
|
||||||
|
foregroundColor: Color(0xFFFF6F00),
|
||||||
|
elevation: 2,
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Colors.white),
|
||||||
|
bodyMedium: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
backgroundColor: const Color(0xFFFF6F00),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
thumbColor: WidgetStateProperty.all<Color>(const Color(0xFF2B2B2B)),
|
||||||
|
trackColor: WidgetStateProperty.all<Color>(const Color(0xFF424242)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData amoledTheme = ThemeData(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
primaryColor: const Color(0xFFFFFFFF),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFF000000),
|
||||||
|
canvasColor: const Color(0xFF000000),
|
||||||
|
cardColor: const Color(0xFF000000),
|
||||||
|
colorScheme: const ColorScheme.dark(
|
||||||
|
primary: Color(0xFFFFFFFF),
|
||||||
|
secondary: Color(0xFF1F1F1F),
|
||||||
|
surface: Color(0xFF000000),
|
||||||
|
onPrimary: Color(0xFF000000),
|
||||||
|
onSecondary: Colors.white,
|
||||||
|
onSurface: Colors.white,
|
||||||
|
),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Color(0xFF000000),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 2,
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Colors.white),
|
||||||
|
bodyMedium: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
backgroundColor: const Color(0xFFFFFFFF),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
thumbColor: WidgetStateProperty.all<Color>(const Color(0xFF1D1C1C)),
|
||||||
|
trackColor: WidgetStateProperty.all<Color>(const Color(0xFF000000)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData f0ck95Theme = ThemeData(
|
||||||
|
brightness: Brightness.light,
|
||||||
|
primaryColor: const Color(0xFFC0C0C0),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFF008080),
|
||||||
|
colorScheme: const ColorScheme.light(
|
||||||
|
primary: Color(0xFFC0C0C0),
|
||||||
|
secondary: Color(0xFF808080),
|
||||||
|
surface: Color(0xFFC0C0C0),
|
||||||
|
onPrimary: Colors.black,
|
||||||
|
onSecondary: Colors.white,
|
||||||
|
),
|
||||||
|
appBarTheme: AppBarTheme(
|
||||||
|
backgroundColor: const Color(0xFFE0E0E0),
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
elevation: 4,
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Colors.black),
|
||||||
|
bodyMedium: TextStyle(color: Colors.black),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
thumbColor: WidgetStateProperty.all<Color>(const Color(0xFF2B2B2B)),
|
||||||
|
trackColor: WidgetStateProperty.all<Color>(const Color(0xFF424242)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData f0ck95dTheme = ThemeData(
|
||||||
|
brightness: Brightness.dark,
|
||||||
|
primaryColor: const Color(0xFFFFFFFF),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFF0E0F0F),
|
||||||
|
colorScheme: const ColorScheme.dark(
|
||||||
|
primary: Color(0xFFFFFFFF),
|
||||||
|
secondary: Color(0xFFC0C0C0),
|
||||||
|
surface: Color(0xFF333131),
|
||||||
|
onPrimary: Colors.black,
|
||||||
|
onSecondary: Colors.white,
|
||||||
|
),
|
||||||
|
appBarTheme: const AppBarTheme(
|
||||||
|
backgroundColor: Color(0xFF0B0A0A),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 2,
|
||||||
|
),
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
bodyLarge: TextStyle(color: Colors.white),
|
||||||
|
bodyMedium: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
foregroundColor: Colors.black,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
scrollbarTheme: ScrollbarThemeData(
|
||||||
|
thumbColor: WidgetStateProperty.all<Color>(const Color(0xFF2B2B2B)),
|
||||||
|
trackColor: WidgetStateProperty.all<Color>(const Color(0xFF424242)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
class ThemeController extends GetxController {
|
||||||
|
final EncryptedSharedPreferencesAsync storage =
|
||||||
|
EncryptedSharedPreferencesAsync.getInstance();
|
||||||
|
|
||||||
|
final Rx<ThemeData> currentTheme = f0ckTheme.obs;
|
||||||
|
|
||||||
|
final Map<String, ThemeData> themeMap = {
|
||||||
|
'f0ck': f0ckTheme,
|
||||||
|
'P1nk': p1nkTheme,
|
||||||
|
'Orange': orangeTheme,
|
||||||
|
'Amoled': amoledTheme,
|
||||||
|
'Paper': paperTheme,
|
||||||
|
'f0ck95': f0ck95Theme,
|
||||||
|
'f0ck95d': f0ck95dTheme,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
super.onInit();
|
||||||
|
_loadTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadTheme() async {
|
||||||
|
try {
|
||||||
|
final String? savedThemeName = await storage.getString('theme');
|
||||||
|
if (savedThemeName != null && themeMap.containsKey(savedThemeName)) {
|
||||||
|
currentTheme.value = themeMap[savedThemeName]!;
|
||||||
|
Get.changeTheme(currentTheme.value);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Get.snackbar('', 'Fehler beim Laden des Themes: $error');
|
||||||
|
currentTheme.value = f0ckTheme;
|
||||||
|
Get.changeTheme(f0ckTheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateTheme(String themeName) async {
|
||||||
|
try {
|
||||||
|
await storage.setString('theme', themeName);
|
||||||
|
if (themeMap.containsKey(themeName)) {
|
||||||
|
currentTheme.value = themeMap[themeName]!;
|
||||||
|
Get.changeTheme(currentTheme.value);
|
||||||
|
} else {
|
||||||
|
currentTheme.value = f0ckTheme;
|
||||||
|
Get.changeTheme(f0ckTheme);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Get.snackbar('', 'Fehler beim Aktualisieren des Themes: $error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +1,45 @@
|
|||||||
|
import 'package:f0ckapp/screens/settings_screen.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:f0ckapp/providers/MediaProvider.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:f0ckapp/providers/ThemeProvider.dart';
|
import 'package:encrypt_shared_preferences/provider.dart';
|
||||||
import 'package:f0ckapp/screens/MediaGrid.dart';
|
|
||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:f0ckapp/utils/appversion.dart';
|
||||||
|
import 'package:f0ckapp/controller/theme_controller.dart';
|
||||||
|
import 'package:f0ckapp/controller/media_controller.dart';
|
||||||
|
import 'package:f0ckapp/screens/detail_view.dart';
|
||||||
|
import 'package:f0ckapp/screens/media_grid.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
|
await EncryptedSharedPreferencesAsync.initialize('VokTnbAbemBUa2j9');
|
||||||
|
await AppVersion.init();
|
||||||
|
Get.put(MediaController());
|
||||||
|
final ThemeController themeController = Get.put(ThemeController());
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
Obx(
|
||||||
providers: [
|
() => MaterialApp(
|
||||||
ChangeNotifierProvider(create: (context) => ThemeProvider()),
|
navigatorKey: Get.key,
|
||||||
ChangeNotifierProvider(create: (context) => MediaProvider())
|
theme: themeController.currentTheme.value,
|
||||||
],
|
debugShowCheckedModeBanner: false,
|
||||||
child: F0ckApp()
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
)
|
final uri = Uri.parse(settings.name ?? '/');
|
||||||
);
|
|
||||||
|
if (uri.path == '/' || uri.pathSegments.isEmpty) {
|
||||||
|
return MaterialPageRoute(builder: (_) => MediaGrid());
|
||||||
}
|
}
|
||||||
|
|
||||||
class F0ckApp extends StatelessWidget {
|
if (uri.pathSegments.length == 1) {
|
||||||
const F0ckApp({super.key});
|
final int id = int.parse(uri.pathSegments.first);
|
||||||
|
return MaterialPageRoute(builder: (_) => DetailView(initialId: id));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
return MaterialPageRoute(builder: (_) => MediaGrid());
|
||||||
Widget build(BuildContext context) {
|
},
|
||||||
final themeProvider = Provider.of<ThemeProvider>(context);
|
),
|
||||||
|
|
||||||
return MaterialApp(
|
|
||||||
debugShowCheckedModeBanner: false,
|
|
||||||
theme: themeProvider.themeData,
|
|
||||||
home: Scaffold(
|
|
||||||
body: MediaGrid(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -18,6 +18,15 @@ class MediaItem {
|
|||||||
});
|
});
|
||||||
|
|
||||||
factory MediaItem.fromJson(Map<String, dynamic> json) {
|
factory MediaItem.fromJson(Map<String, dynamic> json) {
|
||||||
|
List<Tag> parsedTags = [];
|
||||||
|
if (json['tags'] is List) {
|
||||||
|
parsedTags = (json['tags'] as List<dynamic>)
|
||||||
|
.map((tagJson) => Tag.fromJson(tagJson as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
parsedTags = [];
|
||||||
|
}
|
||||||
|
|
||||||
return MediaItem(
|
return MediaItem(
|
||||||
id: json['id'],
|
id: json['id'],
|
||||||
mime: json['mime'],
|
mime: json['mime'],
|
||||||
@ -25,15 +34,14 @@ class MediaItem {
|
|||||||
stamp: json['stamp'],
|
stamp: json['stamp'],
|
||||||
dest: json['dest'],
|
dest: json['dest'],
|
||||||
mode: json['mode'],
|
mode: json['mode'],
|
||||||
tags: (json['tags'] as List<dynamic>)
|
tags: parsedTags,
|
||||||
.map((tagJson) => Tag.fromJson(tagJson))
|
|
||||||
.toList(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String get thumbnailUrl => 'https://f0ck.me/t/$id.webp';
|
String get thumbnailUrl => 'https://f0ck.me/t/$id.webp';
|
||||||
String get mediaUrl => 'https://f0ck.me/b/$dest';
|
String get mediaUrl => 'https://f0ck.me/b/$dest';
|
||||||
String get coverUrl => 'https://f0ck.me/ca/$id.webp';
|
String get coverUrl => 'https://f0ck.me/ca/$id.webp';
|
||||||
|
String get postUrl => 'https://f0ck.me/$id';
|
||||||
}
|
}
|
||||||
|
|
||||||
class Tag {
|
class Tag {
|
19
lib/models/suggestion_model.dart
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
class Suggestion {
|
||||||
|
final String tag;
|
||||||
|
final int tagged;
|
||||||
|
final double score;
|
||||||
|
|
||||||
|
Suggestion({
|
||||||
|
required this.tag,
|
||||||
|
required this.tagged,
|
||||||
|
required this.score,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Suggestion.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Suggestion(
|
||||||
|
tag: json['tag'].toString(),
|
||||||
|
tagged: json['tagged'],
|
||||||
|
score: (json['score'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,87 +0,0 @@
|
|||||||
import 'package:f0ckapp/services/Api.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:f0ckapp/models/MediaItem.dart';
|
|
||||||
|
|
||||||
class MediaProvider extends ChangeNotifier {
|
|
||||||
int _typeid = 0;
|
|
||||||
int _mode = 0;
|
|
||||||
bool _random = false;
|
|
||||||
String? _tag;
|
|
||||||
int _crossAxisCount = 0;
|
|
||||||
List<MediaItem> _mediaItems = [];
|
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
List<String> types = ["alles", "image", "video", "audio"];
|
|
||||||
List<String> modes = ["sfw", "nsfw", "untagged", "all"];
|
|
||||||
|
|
||||||
String get type => types[_typeid];
|
|
||||||
int get typeid => _typeid;
|
|
||||||
int get mode => _mode;
|
|
||||||
bool get random => _random;
|
|
||||||
String? get tag => _tag;
|
|
||||||
int get crossAxisCount => _crossAxisCount;
|
|
||||||
List<MediaItem> get mediaItems => _mediaItems;
|
|
||||||
bool get isLoading => _isLoading;
|
|
||||||
|
|
||||||
void setType(String type) {
|
|
||||||
_typeid = types.indexOf(type);
|
|
||||||
loadMedia(reload: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMode(int mode) {
|
|
||||||
_mode = mode;
|
|
||||||
loadMedia(reload: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void toggleRandom() {
|
|
||||||
_random = !_random;
|
|
||||||
loadMedia(reload: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTag(String? tag) {
|
|
||||||
_tag = tag;
|
|
||||||
loadMedia(reload: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCrossAxisCount(int crossAxisCount) {
|
|
||||||
_crossAxisCount = crossAxisCount;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMediaItems(List<MediaItem> mediaItems) {
|
|
||||||
_mediaItems = mediaItems;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMediaItems(List<MediaItem> newItems) {
|
|
||||||
_mediaItems.addAll(newItems);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> loadMedia({bool reload = false}) async {
|
|
||||||
if (_isLoading) return;
|
|
||||||
_isLoading = true;
|
|
||||||
notifyListeners();
|
|
||||||
|
|
||||||
try {
|
|
||||||
final newMedia = await fetchMedia(
|
|
||||||
older: reload
|
|
||||||
? null
|
|
||||||
: _mediaItems.isNotEmpty
|
|
||||||
? _mediaItems.last.id
|
|
||||||
: null,
|
|
||||||
type: type,
|
|
||||||
mode: mode,
|
|
||||||
random: random,
|
|
||||||
tag: tag,
|
|
||||||
);
|
|
||||||
|
|
||||||
reload ? setMediaItems(newMedia) : addMediaItems(newMedia);
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('Fehler beim Laden der Medien: $e');
|
|
||||||
} finally {
|
|
||||||
_isLoading = false;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
final ThemeData f0ckTheme = ThemeData(
|
|
||||||
brightness: Brightness.dark,
|
|
||||||
primaryColor: Color(0xFF9FFF00),
|
|
||||||
scaffoldBackgroundColor: Color(0xFF000000),
|
|
||||||
colorScheme: ColorScheme.dark(
|
|
||||||
primary: Color(0xFF9FFF00),
|
|
||||||
secondary: Color(0xFF262626),
|
|
||||||
surface: Color(0xFF232323),
|
|
||||||
onPrimary: Color(0xFF000000),
|
|
||||||
onSecondary: Color(0xFFFFFFFF),
|
|
||||||
onSurface: Color(0xFFFFFFFF),
|
|
||||||
),
|
|
||||||
appBarTheme: AppBarTheme(
|
|
||||||
backgroundColor: Color(0xFF2B2B2B),
|
|
||||||
foregroundColor: Color(0xFF9FFF00),
|
|
||||||
elevation: 2,
|
|
||||||
),
|
|
||||||
textTheme: TextTheme(
|
|
||||||
bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFFFFFFFF)),
|
|
||||||
bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFFFFFFFF)),
|
|
||||||
),
|
|
||||||
buttonTheme: ButtonThemeData(
|
|
||||||
buttonColor: Color(0xFF9FFF00),
|
|
||||||
textTheme: ButtonTextTheme.primary,
|
|
||||||
),
|
|
||||||
scrollbarTheme: ScrollbarThemeData(
|
|
||||||
thumbColor: WidgetStateProperty.all(Color(0xFF2B2B2B)),
|
|
||||||
trackColor: WidgetStateProperty.all(Color(0xFF424242)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final ThemeData paperTheme = ThemeData(
|
|
||||||
brightness: Brightness.light,
|
|
||||||
primaryColor: Color(0xFF000000),
|
|
||||||
scaffoldBackgroundColor: Color(0xFFFFFFFF),
|
|
||||||
colorScheme: ColorScheme.light(
|
|
||||||
primary: Color(0xFF000000),
|
|
||||||
secondary: Color(0xFF262626),
|
|
||||||
surface: Color(0xFFFFFFFF),
|
|
||||||
onPrimary: Color(0xFFFFFFFF),
|
|
||||||
onSecondary: Color(0xFFFFFFFF),
|
|
||||||
onSurface: Color(0xFF000000),
|
|
||||||
),
|
|
||||||
appBarTheme: AppBarTheme(
|
|
||||||
backgroundColor: Color(0xFFFFFFFF),
|
|
||||||
foregroundColor: Color(0xFF000000),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
textTheme: TextTheme(
|
|
||||||
bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFF000000)),
|
|
||||||
bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFF000000)),
|
|
||||||
),
|
|
||||||
buttonTheme: ButtonThemeData(
|
|
||||||
buttonColor: Color(0xFF000000),
|
|
||||||
textTheme: ButtonTextTheme.primary,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final ThemeData f0ck95Theme = ThemeData(
|
|
||||||
brightness: Brightness.light,
|
|
||||||
primaryColor: Color(0xFFC0C0C0),
|
|
||||||
scaffoldBackgroundColor: Color(0xFF008080),
|
|
||||||
colorScheme: ColorScheme.light(
|
|
||||||
primary: Color(0xFFC0C0C0),
|
|
||||||
secondary: Color(0xFF808080),
|
|
||||||
surface: Color(0xFFC0C0C0),
|
|
||||||
onPrimary: Color(0xFF000000),
|
|
||||||
onSecondary: Color(0xFFFFFFFF),
|
|
||||||
),
|
|
||||||
appBarTheme: AppBarTheme(
|
|
||||||
backgroundColor: Color(0xFFC0C0C0),
|
|
||||||
foregroundColor: Color(0xFF000000),
|
|
||||||
elevation: 2,
|
|
||||||
),
|
|
||||||
textTheme: TextTheme(
|
|
||||||
bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFF000000)),
|
|
||||||
bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFF000000)),
|
|
||||||
),
|
|
||||||
buttonTheme: ButtonThemeData(
|
|
||||||
buttonColor: Color(0xFF000000),
|
|
||||||
textTheme: ButtonTextTheme.primary,
|
|
||||||
),
|
|
||||||
scrollbarTheme: ScrollbarThemeData(
|
|
||||||
thumbColor: WidgetStateProperty.all(Color(0xFF2B2B2B)),
|
|
||||||
trackColor: WidgetStateProperty.all(Color(0xFF424242)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final ThemeData f0ck95dTheme = ThemeData(
|
|
||||||
brightness: Brightness.dark,
|
|
||||||
primaryColor: Color(0xFFFFFFFF),
|
|
||||||
scaffoldBackgroundColor: Color(0xFF0E0F0F),
|
|
||||||
colorScheme: ColorScheme.dark(
|
|
||||||
primary: Color(0xFFFFFFFF),
|
|
||||||
secondary: Color(0xFFC0C0C0),
|
|
||||||
surface: Color(0xFF333131),
|
|
||||||
onPrimary: Color(0xFF000000),
|
|
||||||
onSecondary: Color(0xFFFFFFFF),
|
|
||||||
),
|
|
||||||
appBarTheme: AppBarTheme(
|
|
||||||
backgroundColor: Color(0xFF0B0A0A),
|
|
||||||
foregroundColor: Color(0xFFFFFFFF),
|
|
||||||
elevation: 2,
|
|
||||||
),
|
|
||||||
textTheme: TextTheme(
|
|
||||||
bodyLarge: TextStyle(fontFamily: 'VCR', color: Color(0xFFFFFFFF)),
|
|
||||||
bodyMedium: TextStyle(fontFamily: 'monospace', color: Color(0xFFFFFFFF)),
|
|
||||||
),
|
|
||||||
buttonTheme: ButtonThemeData(
|
|
||||||
buttonColor: Color(0xFFFFFFFF),
|
|
||||||
textTheme: ButtonTextTheme.primary,
|
|
||||||
),
|
|
||||||
scrollbarTheme: ScrollbarThemeData(
|
|
||||||
thumbColor: WidgetStateProperty.all(Color(0xFF2B2B2B)),
|
|
||||||
trackColor: WidgetStateProperty.all(Color(0xFF424242)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ThemeProvider extends ChangeNotifier {
|
|
||||||
ThemeData _themeData = f0ck95dTheme;
|
|
||||||
|
|
||||||
ThemeData get themeData => _themeData;
|
|
||||||
|
|
||||||
/*void toggleTheme() {
|
|
||||||
_themeData = _themeData == lightTheme ? darkTheme : lightTheme;
|
|
||||||
notifyListeners();
|
|
||||||
}*/
|
|
||||||
}
|
|