Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Re-enable Frontend #21

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions database/database.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import 'package:yaroorm/yaroorm.dart';
class CreateArticlesTable extends Migration {
@override
void up(List<Schema> schemas) {
schemas.add(ArticleSchema);
schemas.addAll([ArticleSchema, ArticleCommentSchema]);
}

@override
void down(List<Schema> schemas) {
schemas.add(Schema.dropIfExists(ArticleSchema));
schemas.addAll([
Schema.dropIfExists(ArticleCommentSchema),
Schema.dropIfExists(ArticleSchema),
]);
}
}
44 changes: 44 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/

# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
**.g.dart

# Symbolication related
app.*.symbols

# Obfuscation related
app.*.map.json

# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
45 changes: 45 additions & 0 deletions frontend/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.

version:
revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
- platform: android
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
- platform: ios
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
- platform: linux
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
- platform: macos
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
- platform: web
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
- platform: windows
create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9
base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9

# 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'
16 changes: 16 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# web

A new Flutter project.

## Getting Started

This project is a starting point for a Flutter application.

A few resources to get you started if this is your first Flutter project:

- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)

For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
20 changes: 20 additions & 0 deletions frontend/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.

analyzer:
exclude:
- "**.g.dart"
3 changes: 3 additions & 0 deletions frontend/lib/auth/auth.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export 'pages/login_page.dart';
export 'pages/register_page.dart';
export 'auth_header.dart';
47 changes: 47 additions & 0 deletions frontend/lib/auth/auth_header.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:frontend/data/models/user.dart';
import 'package:frontend/data/providers/auth_provider.dart';
import 'package:frontend/main.dart';
import 'package:frontend/utils/provider.dart';
import 'package:provider/provider.dart';

class AuthHeaderOptions extends StatelessWidget {
const AuthHeaderOptions({super.key});

@override
Widget build(BuildContext context) {
final auth = context.read<AuthProvider>();
const spacing = SizedBox(width: 24);

return StreamBuilder<ProviderEvent<User>>(
stream: auth.stream,
initialData: auth.lastEvent,
builder: (context, snapshot) {
final user = auth.lastEvent?.data;
final isLoading = snapshot.data?.state == ProviderState.loading;

return Row(
mainAxisSize: MainAxisSize.min,
children: [
if (!isLoading && user == null) ...[
Button(child: const Text('Login '), onPressed: () => router.push('/login')),
spacing,
Button(child: const Text('Register'), onPressed: () => router.push('/register')),
],
if (user != null) ...[
Text('Welcome, ${user.name.split(' ').first}'),
spacing,
Button(
child: const Text('Logout'),
onPressed: () {
auth.logout();
router.pushReplacement('/');
},
),
],
],
);
},
);
}
}
57 changes: 57 additions & 0 deletions frontend/lib/auth/pages/auth_layout.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:frontend/data/models/user.dart';
import 'package:frontend/data/providers/auth_provider.dart';
import 'package:frontend/utils/misc.dart';
import 'package:frontend/utils/provider.dart';
import 'package:provider/provider.dart';

class BaseAuthLayout extends StatefulWidget {
final Widget Function(AuthProvider auth, BaseAuthLayoutState layout) child;

const BaseAuthLayout({super.key, required this.child});

@override
State<BaseAuthLayout> createState() => BaseAuthLayoutState();
}

class BaseAuthLayoutState extends State<BaseAuthLayout> {
bool _showingLoading = false;

@override
Widget build(BuildContext context) {
final authProv = context.read<AuthProvider>();
return ScaffoldPage(
padding: EdgeInsets.zero,
content: Stack(
children: [
Container(color: Colors.grey),
Center(
child: SizedBox(
width: 320,
child: Card(
padding: EdgeInsets.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (_showingLoading) const SizedBox(width: double.maxFinite, child: ProgressBar()),
Container(
padding: const EdgeInsets.symmetric(vertical: 32, horizontal: 16),
child: widget.child(authProv, this),
),
],
),
),
),
),
],
),
);
}

void setLoading(bool show) => setState(() => _showingLoading = show);

void handleErrors(ProviderEvent<User> event) {
if (event.state != ProviderState.error) return;
showError(context, event.errorMessage!);
}
}
88 changes: 88 additions & 0 deletions frontend/lib/auth/pages/login_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:frontend/main.dart';

import 'auth_layout.dart';

const _spacing = SizedBox(height: 18);

class LoginPage extends StatefulWidget {
final String? returnUrl;

const LoginPage({super.key, this.returnUrl});

@override
State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
String? email;
String? password;

@override
Widget build(BuildContext context) {
final themeData = FluentTheme.of(context);

return BaseAuthLayout(
child: (auth, layout) {
loginAction(String email, String password) async {
layout.setLoading(true);
await auth.login(email, password);

final lastEvent = auth.lastEvent!;
if (lastEvent.data != null) {
return router.pushReplacement(widget.returnUrl ?? '/');
} else {
router.pushReplacement('/login');
}

layout
..setLoading(false)
..handleErrors(lastEvent);
}

return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoLabel(
label: 'Email',
child: TextBox(onChanged: (value) => setState(() => email = value)),
),
_spacing,
InfoLabel(
label: 'Password',
child: PasswordBox(onChanged: (value) => setState(() => password = value)),
),
_spacing,
GestureDetector(
onTap: () => router.push('/register'),
child: Text.rich(
TextSpan(
text: 'No account? ',
children: <InlineSpan>[
TextSpan(
text: 'Create one!',
style: themeData.typography.body?.apply(color: themeData.accentColor.dark)),
],
),
),
),
const SizedBox(height: 32),
Row(
children: [
const Expanded(child: SizedBox.shrink()),
FilledButton(
style: ButtonStyle(
shape: ButtonState.all(const RoundedRectangleBorder(borderRadius: BorderRadius.zero)),
),
onPressed: [email, password].contains(null) ? null : () => loginAction(email!, password!),
child: const Text('Sign in'),
)
],
)
],
);
},
);
}
}
Loading
Loading