Skip to content

Commit

Permalink
feat: add web support
Browse files Browse the repository at this point in the history
  • Loading branch information
denysvitali committed Apr 6, 2024
1 parent 1d538d6 commit 0e29b66
Show file tree
Hide file tree
Showing 16 changed files with 194 additions and 55 deletions.
26 changes: 24 additions & 2 deletions .metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,27 @@
# This file should be version controlled and should not be manually edited.

version:
revision: 3b309bda072a6b326e8aa4591a5836af600923ce
channel: beta
revision: "300451adae589accbece3490f4396f10bdf15e6e"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 300451adae589accbece3490f4396f10bdf15e6e
base_revision: 300451adae589accbece3490f4396f10bdf15e6e
- platform: web
create_revision: 300451adae589accbece3490f4396f10bdf15e6e
base_revision: 300451adae589accbece3490f4396f10bdf15e6e

# User provided section

# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
29 changes: 17 additions & 12 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
{
"configurations": [
{
"name": "Flutter",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"args": [
"--flavor",
"main"
],
}
]
"configurations": [
{
"name": "Flutter (Chromium)",
"type": "dart",
"request": "launch",
"program": "lib/main.dart",
"args": ["-d", "chrome", "--flavor", "main"],
"deviceId": "chrome",
},
{
"name": "Flutter",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"args": ["--flavor", "main"]
}
]
}
1 change: 0 additions & 1 deletion lib/components/KanbanWidget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import 'package:provider/provider.dart';

import '../global.dart';
import '../models/bucket.dart';
import '../models/list.dart';
import '../models/project.dart';
import '../pages/list/list.dart';
import '../stores/project_store.dart';
Expand Down
4 changes: 4 additions & 0 deletions lib/global.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:developer' as dev;

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:vikunja_app/api/bucket_implementation.dart';
Expand Down Expand Up @@ -89,6 +90,9 @@ class VikunjaGlobalState extends State<VikunjaGlobal> {
late String currentTimeZone;

void updateWorkmanagerDuration() {
if (kIsWeb) {
return;
}
Workmanager().cancelAll().then((value) {
settingsManager.getWorkmanagerDuration().then((duration) {
if (duration.inMinutes > 0) {
Expand Down
82 changes: 51 additions & 31 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:io';

import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:permission_handler/permission_handler.dart';
Expand Down Expand Up @@ -34,6 +35,9 @@ class IgnoreCertHttpOverrides extends HttpOverrides {

@pragma('vm:entry-point')
void callbackDispatcher() {
if (kIsWeb) {
return;
}
Workmanager().executeTask((task, inputData) async {
print(
"Native called background task: $task"); //simpleTask will be emitted here.
Expand Down Expand Up @@ -95,18 +99,31 @@ void main() async {
Permission.notification.request();
}
});
await FlutterDownloader.initialize();
Workmanager().initialize(callbackDispatcher, isInDebugMode: false);
try {
if (!kIsWeb) {
await FlutterDownloader.initialize();
}
} catch (e) {
print("Failed to initialize downloader: $e");
}
try {
if (!kIsWeb) {
Workmanager().initialize(callbackDispatcher, isInDebugMode: false);
}
} catch (e) {
print("Failed to initialize workmanager: $e");
}
runApp(VikunjaGlobal(
child: new VikunjaApp(
home: HomePage(),
key: UniqueKey(),
navkey: globalNavigatorKey,
),
login: new VikunjaApp(
home: LoginPage(),
key: UniqueKey(),
)));
child: new VikunjaApp(
home: HomePage(),
key: UniqueKey(),
navkey: globalNavigatorKey,
),
login: new VikunjaApp(
home: LoginPage(),
key: UniqueKey(),
),
));
}

final ValueNotifier<bool> updateTheme = ValueNotifier(false);
Expand All @@ -118,38 +135,41 @@ class VikunjaApp extends StatelessWidget {
const VikunjaApp({Key? key, required this.home, this.navkey})
: super(key: key);

Future<ThemeData> getThemedata() async {
FlutterThemeMode themeMode = FlutterThemeMode.light;
try {
SettingsManager manager = SettingsManager(new FlutterSecureStorage());
themeMode = await manager.getThemeMode();
} catch (e) {
print("Failed to get theme mode: $e");
}
switch (themeMode) {
case FlutterThemeMode.dark:
return buildVikunjaDarkTheme();
case FlutterThemeMode.materialYouLight:
return buildVikunjaMaterialLightTheme();
case FlutterThemeMode.materialYouDark:
return buildVikunjaMaterialDarkTheme();
default:
return buildVikunjaTheme();
}
}

@override
Widget build(BuildContext context) {
SettingsManager manager = SettingsManager(new FlutterSecureStorage());

return new ValueListenableBuilder(
valueListenable: updateTheme,
builder: (_, mode, __) {
updateTheme.value = false;
FlutterThemeMode themeMode = FlutterThemeMode.system;
Future<ThemeData> theme = manager.getThemeMode().then((value) {
themeMode = value;
switch (value) {
case FlutterThemeMode.dark:
return buildVikunjaDarkTheme();
case FlutterThemeMode.materialYouLight:
return buildVikunjaMaterialLightTheme();
case FlutterThemeMode.materialYouDark:
return buildVikunjaMaterialDarkTheme();
default:
return buildVikunjaTheme();
}
});
return FutureBuilder<ThemeData>(
future: theme,
future: getThemedata(),
builder: (BuildContext context, AsyncSnapshot<ThemeData> data) {
if (data.hasData) {
return new DynamicColorBuilder(
builder: (lightTheme, darkTheme) {
ThemeData? themeData = data.data;
if (themeMode == FlutterThemeMode.materialYouLight)
if (data.data == FlutterThemeMode.materialYouLight)
themeData = themeData?.copyWith(colorScheme: lightTheme);
else if (themeMode == FlutterThemeMode.materialYouDark)
else if (data.data == FlutterThemeMode.materialYouDark)
themeData = themeData?.copyWith(colorScheme: darkTheme);
return MaterialApp(
title: 'Vikunja',
Expand Down
2 changes: 0 additions & 2 deletions lib/pages/list/list_edit.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import 'dart:ffi';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:vikunja_app/global.dart';
import 'package:vikunja_app/models/list.dart';
import 'package:vikunja_app/theme/button.dart';
Expand Down
1 change: 0 additions & 1 deletion lib/pages/project/project_edit.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'dart:ffi';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:vikunja_app/global.dart';
Expand Down
2 changes: 0 additions & 2 deletions lib/pages/project/project_task_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ import 'package:vikunja_app/components/AddDialog.dart';
import 'package:vikunja_app/components/KanbanWidget.dart';
import 'package:vikunja_app/components/TaskTile.dart';
import 'package:vikunja_app/global.dart';
import 'package:vikunja_app/models/list.dart';
import 'package:vikunja_app/models/task.dart';
import 'package:vikunja_app/models/bucket.dart';
import 'package:vikunja_app/pages/list/list_edit.dart';
import 'package:vikunja_app/pages/list/task_edit.dart';
import 'package:vikunja_app/pages/project/project_edit.dart';

Expand Down
8 changes: 4 additions & 4 deletions lib/service/services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,10 @@ class SettingsManager {
key: "workmanager-duration", value: duration.inMinutes.toString());
}

Future<List<String>?> getPastServers() {
return _storage
.read(key: "recent-servers")
.then((value) => (jsonDecode(value!) as List<dynamic>).cast<String>());
Future<List<String>?> getPastServers() async {
String jsonString = await _storage.read(key: "recent-servers") ?? "[]";
List<dynamic> server = jsonDecode(jsonString);
return server.map((e) => e as String).toList();
}

Future<void> setPastServers(List<String>? server) {
Expand Down
Binary file added web/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/icons/Icon-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/icons/Icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/icons/Icon-maskable-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added web/icons/Icon-maskable-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">

<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="vikunja_app">
<link rel="apple-touch-icon" href="icons/Icon-192.png">

<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>

<title>vikunja_app</title>
<link rel="manifest" href="manifest.json">

<script>
// The value below is injected by flutter build, do not touch.
const serviceWorkerVersion = null;
</script>
<!-- This script adds the flutter initialization JS code -->
<script src="flutter.js" defer></script>
</head>
<body>
<script>
window.addEventListener('load', function(ev) {
// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
}
});
});
</script>
</body>
</html>
35 changes: 35 additions & 0 deletions web/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "vikunja_app",
"short_name": "vikunja_app",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}

0 comments on commit 0e29b66

Please sign in to comment.