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

Feature/post content #27

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
61 changes: 47 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,28 @@ The application requires the Firebase to run. The debug build connects to the lo
firebase emulators:start
```

**To disable the emulator in the dev environment, disable the line of codes in `lib/main.dart` or build in production mode**
### Connect to a Android Device

1. Make sure firebase emulators is running.

2. Connect your device via USB and enable developer mode.

3. Head over to your Android SDK platform-tools folder `android_sdk/platform-tools/`

4. Connect your Android device with Firebase Emulator Servers:

Firestore
```bash
adb reverse tcp:8080 tcp:8080
```
Storage
```bash
adb reverse tcp:9199 tcp:9199
Comment on lines +26 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally it should be required since it is supposed to be automatic.

```
5. Run the app from your IDE.

```dart
if (kDebugMode) {
try {
FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
} catch (e) {
// ignore: avoid_print
print(e);
}
}
```

### How to initialize Firebase
### How to initialize Firebase Production
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's instruction for production. It's just a general instruction.


Reference: <https://firebase.google.com/docs/flutter/setup?platform=web>

Expand All @@ -43,14 +51,39 @@ Reference: <https://firebase.google.com/docs/flutter/setup?platform=web>
firebase login
```

4. Install FlutterFire
4. Configure with your firebase project
```bash
firebase init
```

5. Install FlutterFire

```bash
dart pub global activate flutterfire_cli
```

5. Configure it using this command in the Project
6. Configure it using this command in the Project

```bash
flutterfire configure
```

7. Disable the firebase emulator dev environment

**disable the following lines of codes in `lib/main.dart` or build in production mode**
```dart
if (kDebugMode) {
try {
const host = '0.0.0.0';
FirebaseStorage.instance.useStorageEmulator(host, 9199);
FirebaseFirestore.instance.useFirestoreEmulator(host, 8080);
} catch (e) {
// ignore: avoid_print
print(e);
}
}
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't required since there's the kDebugMode, it's not mandatory change. It's only required when the dev wants to test their debug build against to firebase server rather than the emulator.


## Support

If you have trouble running this project. Go to our [discord group](https://discord.gg/NMc62wG7eU) and ask your questions under #stackoverflow channel. We'll be happy to lend you a hand.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we'd better encourage users to create a ticket in GitHub rather than Discord. (That doesn't mean they can't tho..)

12 changes: 10 additions & 2 deletions firebase.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
{
"emulators": {
"firestore": {
"port": 8080
"port": 8080,
"host": "0.0.0.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to do this.

},
"ui": {
"enabled": true
},
"singleProjectMode": true
"singleProjectMode": true,
"storage": {
"port": 9199,
"host": "0.0.0.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to do this.

}
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"storage": {
"rules": "storage.rules"
}
}
2 changes: 1 addition & 1 deletion firestore.rules
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ service cloud.firestore {
// all client requests to your Firestore database will be denied until you Update
// your rules
match /{document=**} {
allow read, write: if request.time < timestamp.date(2023, 7, 26);
allow read, write: if request.time < timestamp.date(2024, 7, 26);
}
}
}
8 changes: 7 additions & 1 deletion lib/l10n/intl_en.arb
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{
"@@locale": "en",
"whatsUpIn": "What's up in",
"montreal": "Montréal"
"montreal": "Montreal",
"add":"Add",
"title":"title",
"upload":"Upload",
"selectPicture":"Select Picture",
"or":"OR",
"takePicture": "Take Picture"
}
8 changes: 7 additions & 1 deletion lib/l10n/intl_fr.arb
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
{
"@@locale": "fr",
"whatsUpIn": "Quoi de neuf à",
"montreal": "Montréal"
"montreal": "Montréal",
"add": "Ajouter",
"title":"titre",
"upload":"Téléverser",
"selectPicture":"Choisir une Image",
"or":"OU",
"takePicture":"Prendre une Photo"
}
10 changes: 4 additions & 6 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
Expand All @@ -16,12 +17,9 @@ void main() async {

if (kDebugMode) {
try {
FirebaseFirestore.instance.useFirestoreEmulator(
defaultTargetPlatform == TargetPlatform.android
? "10.0.2.2"
: "localhost",
8080,
);
const host = '0.0.0.0';
FirebaseStorage.instance.useStorageEmulator(host, 9199);
FirebaseFirestore.instance.useFirestoreEmulator(host, 8080);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need this change.

} catch (e) {
// ignore: avoid_print
print(e);
Expand Down
201 changes: 201 additions & 0 deletions lib/presentation/components/src/add_post_modal_sheet.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import 'dart:io';

import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:instreal/features/posts/post_entity.dart';

import '../../../l10n/index.dart';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mind using absolute path?


class AddPostModalPage extends StatefulWidget {
final Reference storageRef;
const AddPostModalPage({required this.storageRef, super.key});

@override
State<AddPostModalPage> createState() => _AddPostModalPageState();
}

class _AddPostModalPageState extends State<AddPostModalPage> {
XFile? image;
final ImagePicker picker = ImagePicker();
final TextEditingController textEditName = TextEditingController();

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Row(
children: [
Text(InstrealLocalizations.of(context)!.add),
const Spacer(),
const Align(
alignment: Alignment.centerRight, child: CloseButton()),
],
),
const SizedBox(height: 8),
if (image != null)
ImageView(
imagePath: image!.path,
onPressedRemoveImage: () {
image = null;
setState(() {/*Remove Image*/});
},
)
else
ImagePickerView(
onPressedCamera: () async {
image = await picker.pickImage(source: ImageSource.camera);
setState(() {/*Update Image*/});
},
onPressedGallery: () async {
image = await picker.pickImage(source: ImageSource.gallery);
setState(() {/*Update Image*/});
},
),
TextField(
decoration: InputDecoration(
label: Text(InstrealLocalizations.of(context)!.title),
),
controller: textEditName,
),
const SizedBox(height: 16),
Row(
children: [
OutlinedButton(
onPressed: () async {
final storage =
widget.storageRef.child('images/instreal.jpg');

late String fileUrl;
//TODO may need a proper implementation

try {
await storage.putFile(File(image!.path));
fileUrl = await storage.getDownloadURL();
} on FirebaseException catch (e) {
debugPrint(e.message);
}

if (context.mounted) {
Navigator.pop(
context,
Post(
id: '',
title: textEditName.text,
author: 'author', // get name from firebase auth V3
imageUrl: fileUrl,
),
);
}
},
child: Text(InstrealLocalizations.of(context)!.upload)),
//add loading indicator when uploading
],
)
],
),
);
}
}

class ImagePickerView extends StatelessWidget {
final double? iconSize = 96;
final VoidCallback onPressedCamera;
final VoidCallback onPressedGallery;

const ImagePickerView({
required this.onPressedCamera,
required this.onPressedGallery,
super.key,
});

@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 4 / 3,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Colors.grey,
style: BorderStyle.solid,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton.outlined(
padding: const EdgeInsets.all(16),
onPressed: onPressedGallery,
icon: Icon(
Icons.photo,
size: iconSize,
),
),
Text(InstrealLocalizations.of(context)!.selectPicture)
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text(InstrealLocalizations.of(context)!.or)),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton.outlined(
padding: const EdgeInsets.all(16),
onPressed: onPressedCamera,
icon: Icon(
Icons.photo_camera,
size: iconSize,
),
),
Text(InstrealLocalizations.of(context)!.takePicture)
],
),
],
),
),
);
}
}

class ImageView extends StatelessWidget {
final VoidCallback onPressedRemoveImage;
final String imagePath;
const ImageView({
required this.imagePath,
required this.onPressedRemoveImage,
super.key,
});

@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 4 / 3,
child: ClipRRect(
borderRadius: BorderRadius.circular(16.0),
child: Stack(
fit: StackFit.expand,
children: [
Image.file(
File(imagePath),
fit: BoxFit.cover,
),
Align(
alignment: Alignment.bottomRight,
child: IconButton(
onPressed: onPressedRemoveImage,
icon: const Icon(Icons.delete),
),
)
],
),
),
);
}
}
Loading