scaling the timeline
This commit is contained in:
parent
c63f062640
commit
ee081de0ab
14 changed files with 202 additions and 63 deletions
|
@ -1,4 +1,7 @@
|
||||||
|
import 'package:flutter/painting.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import '../global.dart' as global;
|
||||||
|
|
||||||
enum Settings {
|
enum Settings {
|
||||||
instanceUrl,
|
instanceUrl,
|
||||||
|
@ -38,3 +41,20 @@ Future<String> loadAuthCode() async {
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> saveLocale(String locale) async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
return await prefs.setString("active-locale", locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Locale> loadLocale() async {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
String? locale = prefs.getString("active-locale");
|
||||||
|
if (locale == null) {
|
||||||
|
if (global.availableLocales.contains(Locale(Intl.systemLocale))) {
|
||||||
|
return Locale(Intl.systemLocale);
|
||||||
|
}
|
||||||
|
return const Locale("en");
|
||||||
|
}
|
||||||
|
return Locale(locale);
|
||||||
|
}
|
||||||
|
|
1
lib/business_logic/timeline/timeline.dart
Normal file
1
lib/business_logic/timeline/timeline.dart
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:flutter/painting.dart';
|
||||||
|
|
||||||
const String name = "slothmu";
|
const String name = "slothmu";
|
||||||
const String version = "v0.1 'not even alpha'";
|
const String version = "v0.1 'not even alpha'";
|
||||||
const String useragent = "$name/$version";
|
const String useragent = "$name/$version";
|
||||||
|
@ -8,3 +10,4 @@ const Map<String, String> defaultHeaders = {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
};
|
};
|
||||||
const List<String> bad = ["gab.com", "spinster.xyz", "truthsocial.com"];
|
const List<String> bad = ["gab.com", "spinster.xyz", "truthsocial.com"];
|
||||||
|
const List<Locale> availableLocales = [Locale("en"), Locale("de")];
|
||||||
|
|
14
lib/i18n/de.json
Normal file
14
lib/i18n/de.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"greeting": "hallo!",
|
||||||
|
"instance-url": "anbieter url",
|
||||||
|
"instance-url-example": "beispiel.de",
|
||||||
|
"authorize-in-browser": "im browser autorisieren",
|
||||||
|
"login-failed-snackbar-text": "login fehlgeschlagen!",
|
||||||
|
"back-button": "zurück",
|
||||||
|
"confirm-button": "bestätigen",
|
||||||
|
"timeline" : "timeline",
|
||||||
|
"chat": "chat",
|
||||||
|
"notifications": "benachrichtigungen",
|
||||||
|
"settings": "einstellungen"
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"greeting": "hallo!",
|
|
||||||
"user-id-not-valid": "hmm... die id sieht nicht ganz richtig aus...",
|
|
||||||
"user-id": "nutzer id",
|
|
||||||
"user-id-example": "nutzer@beispiel.de",
|
|
||||||
"authorize-in-browser": "im browser autorisieren",
|
|
||||||
"login-failed-snackbar-text": "login fehlgeschlagen!",
|
|
||||||
"back-button": "zurück",
|
|
||||||
"confirm-button": "bestätigen",
|
|
||||||
"copy-code-from-browser": "bitte den code aus dem browser hierhin kopieren.",
|
|
||||||
"code-hint": "code"
|
|
||||||
}
|
|
||||||
|
|
14
lib/i18n/en.json
Normal file
14
lib/i18n/en.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"greeting": "hello!",
|
||||||
|
"instance-url": "instance url",
|
||||||
|
"instance-url-example": "example.com",
|
||||||
|
"authorize-in-browser": "authorize in browser",
|
||||||
|
"login-failed-snackbar-text": "login failed!",
|
||||||
|
"back-button": "back",
|
||||||
|
"confirm-button": "confirm",
|
||||||
|
"timeline" : "timeline",
|
||||||
|
"chat": "chat",
|
||||||
|
"notifications": "notifications",
|
||||||
|
"settings": "settings"
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"greeting": "hello!",
|
|
||||||
"user-id-not-valid": "sorry, this user id doesn't look quite right... ",
|
|
||||||
"user-id": "user id",
|
|
||||||
"user-id-example": "user@example.com",
|
|
||||||
"authorize-in-browser": "authorize in browser",
|
|
||||||
"login-failed-snackbar-text": "login failed!",
|
|
||||||
"back-button": "back",
|
|
||||||
"confirm-button": "confirm",
|
|
||||||
"copy-code-from-browser": "please copy the code from your browser here!",
|
|
||||||
"code-hint": "code"
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,16 +6,20 @@ import 'pages/login.dart';
|
||||||
import 'business_logic/settings.dart' as settings;
|
import 'business_logic/settings.dart' as settings;
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'themes/themes.dart' as themes;
|
import 'themes/themes.dart' as themes;
|
||||||
|
import 'global.dart' as global;
|
||||||
|
|
||||||
String _initRoute = "/";
|
String _initRoute = "/";
|
||||||
ThemeData theme = themes.getTheme(themes.available[0]);
|
ThemeData theme = themes.getTheme(themes.available[0]);
|
||||||
|
Locale activeLocale = const Locale("en");
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
Intl.defaultLocale = 'en_US';
|
Intl.defaultLocale = "en";
|
||||||
|
await settings.saveLocale("en");
|
||||||
|
activeLocale = await settings.loadLocale();
|
||||||
|
|
||||||
// check if all information is available
|
// check if all information is available
|
||||||
if (await settings.loadAuthCode() == "") {
|
if (await settings.loadAuthCode() == "") {
|
||||||
_initRoute = "/login";
|
_initRoute = "/";
|
||||||
}
|
}
|
||||||
runApp(const Slothmu());
|
runApp(const Slothmu());
|
||||||
}
|
}
|
||||||
|
@ -28,16 +32,13 @@ class Slothmu extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SlothmuState extends State<Slothmu> {
|
class _SlothmuState extends State<Slothmu> {
|
||||||
List<Locale> supported = const [
|
|
||||||
Locale("en", "US"),
|
|
||||||
Locale("de", "DE"),
|
|
||||||
];
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
LocalJsonLocalization.delegate.directories = ['lib/i18n'];
|
LocalJsonLocalization.delegate.directories = ['lib/i18n'];
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
theme: theme,
|
theme: theme,
|
||||||
supportedLocales: supported,
|
locale: activeLocale,
|
||||||
|
supportedLocales: global.availableLocales,
|
||||||
localizationsDelegates: [
|
localizationsDelegates: [
|
||||||
GlobalCupertinoLocalizations.delegate,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:slothmu/business_logic/user.dart';
|
|
||||||
import 'package:localization/localization.dart';
|
import 'package:localization/localization.dart';
|
||||||
import '../business_logic/auth/oauth.dart' as oauth;
|
import '../business_logic/auth/oauth.dart' as oauth;
|
||||||
import '../business_logic/settings.dart' as settings;
|
import '../business_logic/settings.dart' as settings;
|
||||||
import '../business_logic/user.dart' as user;
|
|
||||||
|
|
||||||
class Login extends StatefulWidget {
|
class Login extends StatefulWidget {
|
||||||
const Login({Key? key}) : super(key: key);
|
const Login({Key? key}) : super(key: key);
|
||||||
|
@ -47,23 +45,15 @@ class _LoginFormState extends State<LoginForm> {
|
||||||
Text("greeting".i18n(), style: Theme.of(context).textTheme.headline1),
|
Text("greeting".i18n(), style: Theme.of(context).textTheme.headline1),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
onSaved: (value) async {
|
onSaved: (value) async {
|
||||||
await settings
|
await settings.saveInstanceUrl(value!);
|
||||||
.saveInstanceUrl(user.urlFromUsername(name: value!));
|
|
||||||
await settings.saveUsername(user.userFromUsername(name: value));
|
|
||||||
},
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "user-id".i18n(),
|
labelText: "instance-url".i18n(),
|
||||||
hintText: "user-id-example".i18n(),
|
hintText: "instance-url-example".i18n(),
|
||||||
icon: const Icon(Icons.person),
|
icon: const Icon(Icons.home),
|
||||||
prefixText: "@",
|
prefixText: "https://",
|
||||||
),
|
),
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
validator: (value) {
|
|
||||||
if (value!.isEmpty || !isValidUsername(name: value)) {
|
|
||||||
return "user-id-not-valid".i18n();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
TextButton.icon(
|
TextButton.icon(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -76,8 +66,7 @@ class _LoginFormState extends State<LoginForm> {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) => const AuthPage(),
|
||||||
const AuthPage(baseurl: "kittycat.homes"),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -93,7 +82,7 @@ class _LoginFormState extends State<LoginForm> {
|
||||||
page that handles authenticating user
|
page that handles authenticating user
|
||||||
*/
|
*/
|
||||||
class AuthPage extends StatefulWidget {
|
class AuthPage extends StatefulWidget {
|
||||||
const AuthPage({Key? key, required String baseurl}) : super(key: key);
|
const AuthPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AuthPage> createState() => _AuthPageState();
|
State<AuthPage> createState() => _AuthPageState();
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:slothmu/partials/post.dart';
|
||||||
|
import 'package:slothmu/partials/thread.dart';
|
||||||
|
|
||||||
Widget timeline(context) {
|
Widget timeline(context) {
|
||||||
return Padding(
|
return Container(
|
||||||
padding: const EdgeInsets.all(24),
|
child: ListView(
|
||||||
child: Column(
|
padding: const EdgeInsets.fromLTRB(24, 0, 24, 64),
|
||||||
children: [],
|
addAutomaticKeepAlives: false,
|
||||||
));
|
children: [Thread(), Thread(), Thread()],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:localization/localization.dart';
|
||||||
import 'package:slothmu/pages/chat/chat.dart';
|
import 'package:slothmu/pages/chat/chat.dart';
|
||||||
import 'package:slothmu/pages/notifications/notifications.dart';
|
import 'package:slothmu/pages/notifications/notifications.dart';
|
||||||
import 'package:slothmu/pages/timeline/timeline.dart';
|
import 'package:slothmu/pages/timeline/timeline.dart';
|
||||||
|
@ -47,13 +48,16 @@ class _MainScaffoldState extends State<MainScaffold> {
|
||||||
onDestinationSelected: (index) =>
|
onDestinationSelected: (index) =>
|
||||||
setState(() => this.index = index),
|
setState(() => this.index = index),
|
||||||
selectedIndex: index,
|
selectedIndex: index,
|
||||||
destinations: const [
|
destinations: [
|
||||||
NavigationDestination(icon: Icon(Icons.forum), label: "Timeline"),
|
|
||||||
NavigationDestination(icon: Icon(Icons.chat), label: "Chat"),
|
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.notifications), label: "Notifications"),
|
icon: const Icon(Icons.forum), label: "timeline".i18n()),
|
||||||
NavigationDestination(
|
NavigationDestination(
|
||||||
icon: Icon(Icons.settings), label: "Settings"),
|
icon: const Icon(Icons.chat), label: "chat".i18n()),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: const Icon(Icons.notifications),
|
||||||
|
label: "notifications".i18n()),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: const Icon(Icons.settings), label: "settings".i18n()),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
82
lib/partials/post.dart
Normal file
82
lib/partials/post.dart
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Post extends StatefulWidget {
|
||||||
|
const Post({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Post> createState() => _PostState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PostState extends State<Post> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.face,
|
||||||
|
size: 64,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"first name display last name name",
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"@alice_exampleuser@example.com",
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
postBody(context),
|
||||||
|
postActionBar(context),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget postBody(context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 6, 6, 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
left: BorderSide(color: Theme.of(context).colorScheme.onSurface),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: RichText(
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
text: const TextSpan(
|
||||||
|
text:
|
||||||
|
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Risus commodo viverra maecenas accumsan lacus vel facilisis volutpat est. Diam in arcu cursus euismod quis viverra. Elementum sagittis vitae et leo duis."),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget postActionBar(context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(Icons.reply),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(Icons.repeat),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(Icons.favorite_outline),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(Icons.more_horiz),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
35
lib/partials/thread.dart
Normal file
35
lib/partials/thread.dart
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:slothmu/partials/post.dart';
|
||||||
|
|
||||||
|
class Thread extends StatefulWidget {
|
||||||
|
const Thread({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<Thread> createState() => _ThreadState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ThreadState extends State<Thread> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
width: MediaQuery.of(context).size.width / 1.2,
|
||||||
|
constraints: const BoxConstraints(maxWidth: 1000, minWidth: 375),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
border: Border.symmetric(
|
||||||
|
horizontal: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
width: 12)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [Post(), Post(), Post()],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,8 +68,6 @@ flutter:
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
assets:
|
assets:
|
||||||
- lib/i18n/
|
- lib/i18n/
|
||||||
# - images/a_dot_burr.jpeg
|
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
|
Loading…
Reference in a new issue