Skip to content

Commit

Permalink
onboarding picture
Browse files Browse the repository at this point in the history
  • Loading branch information
leo-lox committed Oct 25, 2024
1 parent ee8e662 commit ab01a3e
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 38 deletions.
18 changes: 9 additions & 9 deletions lib/data_layer/data_sources/nostr_build_file_upload.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart';

import '../../domain_layer/entities/mem_file.dart';

class NostrBuildFileUpload {
Future<String> uploadImage(File file) async {
Future<String> uploadImage(MemFile file) async {
final uri = Uri.parse('https://nostr.build/upload.php');
var request = http.MultipartRequest('POST', uri);

var bytes = await file.readAsBytes();
var mimeType = lookupMimeType(file.path);
var filename = file.path.split('/').last;

final httpImage = http.MultipartFile.fromBytes("fileToUpload", bytes,
contentType: MediaType.parse(mimeType!), filename: filename);
final httpImage = http.MultipartFile.fromBytes(
"fileToUpload",
file.bytes,
contentType: MediaType.parse(file.mimeType),
filename: file.name,
);
request.files.add(httpImage);

final response = await request.send();
Expand Down
21 changes: 19 additions & 2 deletions lib/data_layer/repositories/file_upload_repository_impl.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import 'dart:io';

import 'package:camelus/data_layer/data_sources/nostr_build_file_upload.dart';
import 'package:camelus/domain_layer/entities/mem_file.dart';
import 'package:camelus/domain_layer/repositories/upload_file_repository.dart';
import 'package:mime/mime.dart';

class FileUploadRepositoryImpl implements FileUploadRepository {
final NostrBuildFileUpload nostrBuildFileUpload;
Expand All @@ -11,7 +13,22 @@ class FileUploadRepositoryImpl implements FileUploadRepository {
});

@override
Future<String> uploadImage(File file) {
return nostrBuildFileUpload.uploadImage(file);
Future<String> uploadImage(MemFile memFile) {
return nostrBuildFileUpload.uploadImage(memFile);
}

@override
Future<String> uploadImageFile(File file) async {
var bytes = await file.readAsBytes();
var mimeType = lookupMimeType(file.path);
var filename = file.path.split('/').last;
if (mimeType == null || mimeType.isEmpty) {
throw Exception("No mime type found");
}
return uploadImage(MemFile(
bytes: bytes,
mimeType: mimeType,
name: filename,
));
}
}
5 changes: 2 additions & 3 deletions lib/data_layer/repositories/follow_repository_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,8 @@ class FollowRepositoryImpl implements FollowRepository {
Future<void> setFollowing(ContactList contactList) {
final ndkContactList = (contactList as ContactListModel).toNdk();

throw UnimplementedError();
// return dartNdkSource.dartNdk.follows
// .broadcastSetContacts(ndkContactList, [], eventSigner);
return dartNdkSource.dartNdk.follows
.broadcastSetContactList(ndkContactList);
}

@override
Expand Down
13 changes: 13 additions & 0 deletions lib/domain_layer/entities/mem_file.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import 'dart:typed_data';

class MemFile {
Uint8List bytes;
final String mimeType;
final String name;

MemFile({
required this.bytes,
required this.mimeType,
required this.name,
});
}
14 changes: 9 additions & 5 deletions lib/domain_layer/entities/onboarding_user_info.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import 'dart:typed_data';

import 'package:camelus/helpers/bip340.dart';
import '../../helpers/bip340.dart';
import 'mem_file.dart';

class OnboardingUserInfo {
String? name = '';
Uint8List? picture;
Uint8List? banner;
MemFile? picture;
MemFile? banner;
String? about = '';
String? nip05;
String? website = '';
bool nip46 = false;
KeyPair keyPair;
List<String> followPubkeys = [];
String lud06 = '';
String lud16 = '';

OnboardingUserInfo({
this.name,
Expand All @@ -20,6 +21,9 @@ class OnboardingUserInfo {
this.about,
this.nip05,
this.website,
this.nip46 = false,
this.lud06 = '',
this.lud16 = '',
required this.keyPair,
});
}
5 changes: 4 additions & 1 deletion lib/domain_layer/repositories/upload_file_repository.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'dart:io';

import '../entities/mem_file.dart';

abstract class FileUploadRepository {
Future<String> uploadImage(File file);
Future<String> uploadImageFile(File file);
Future<String> uploadImage(MemFile file);
}
10 changes: 8 additions & 2 deletions lib/domain_layer/usecases/file_upload.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import 'dart:io';
import 'package:camelus/domain_layer/repositories/upload_file_repository.dart';

import '../entities/mem_file.dart';
import '../repositories/upload_file_repository.dart';

class FileUpload {
final FileUploadRepository fileUploadRepository;

FileUpload(this.fileUploadRepository);

Future<String> uploadImage(File file) async {
Future<String> uploadImageFile(File file) async {
return await fileUploadRepository.uploadImageFile(file);
}

Future<String> uploadImage(MemFile file) async {
return await fileUploadRepository.uploadImage(file);
}
}
19 changes: 17 additions & 2 deletions lib/domain_layer/usecases/follow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,23 @@ class Follow {
return followRepository.unfollowUser(npub);
}

Future<void> setContacts(List<String> contacts) async {
throw UnimplementedError();
/// overrides the previous contact list and sets a new one with the given pubkeys
Future<void> setContacts(List<String> pubkeys) async {
_checkSelfPubkey();
return followRepository.setFollowing(
ContactList(
pubKey: selfPubkey!,
contacts: pubkeys,
contactRelays: [],
createdAt: 0,
followedCommunities: [],
followedEvents: [],
followedTags: [],
petnames: [],
sources: [],
loadedTimestamp: null,
),
);
}

Future<bool> isFollowing(String npub) async {
Expand Down
13 changes: 9 additions & 4 deletions lib/presentation_layer/atoms/crop_avatar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import 'package:crop_your_image/crop_your_image.dart';
class CropAvatar extends StatelessWidget {
final Uint8List _imageData;

Uint8List _croppedImageData = Uint8List(0);
Uint8List? _croppedImageData;

double aspectRatio;

CropAvatar({
super.key,
this.aspectRatio = 1,
required Uint8List imageData,
}) : _imageData = imageData;
}) : _imageData = imageData,
_croppedImageData = imageData; // init data if cropping does not work

final _controller = CropController();

Expand All @@ -23,7 +27,7 @@ class CropAvatar extends StatelessWidget {
children: [
Crop(
baseColor: Palette.background,
aspectRatio: 1,
aspectRatio: aspectRatio,
//radius: 150,
interactive: false,
withCircleUi: true,
Expand All @@ -32,6 +36,7 @@ class CropAvatar extends StatelessWidget {

onCropped: (image) {
_croppedImageData = image;
Navigator.pop<Uint8List>(context, _croppedImageData);
},
),
Column(
Expand All @@ -44,7 +49,7 @@ class CropAvatar extends StatelessWidget {
child: longButton(
name: "apply",
onPressed: () {
Navigator.pop(context, _croppedImageData);
_controller.crop();
},
),
),
Expand Down
2 changes: 1 addition & 1 deletion lib/presentation_layer/components/write_post.dart
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ class _WritePostState extends ConsumerState<WritePost> {
List<String> imageUrls = [];
for (var image in _images) {
try {
var url = await ref.watch(fileUploadProvider).uploadImage(image);
var url = await ref.watch(fileUploadProvider).uploadImageFile(image);
imageUrls.add(url);
} catch (e) {
log("errUploadImage: ${e.toString()}");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import 'dart:convert';

import 'package:camelus/presentation_layer/atoms/mnemonic_grid.dart';
import 'package:camelus/presentation_layer/providers/key_pair_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
Expand All @@ -11,9 +9,15 @@ import 'package:url_launcher/url_launcher.dart';
import '../../../../config/palette.dart';
import '../../../../domain_layer/entities/generated_private_key.dart';
import '../../../../domain_layer/entities/onboarding_user_info.dart';
import '../../../../domain_layer/entities/user_metadata.dart';
import '../../../../domain_layer/usecases/generate_private_key.dart';
import '../../../../helpers/bip340.dart';
import '../../../atoms/long_button.dart';
import '../../../atoms/mnemonic_grid.dart';
import '../../../providers/file_upload_provider.dart';
import '../../../providers/following_provider.dart';
import '../../../providers/key_pair_provider.dart';
import '../../../providers/metadata_provider.dart';
import '../../home_page.dart';

class OnboardingDone extends ConsumerStatefulWidget {
Expand Down Expand Up @@ -65,6 +69,40 @@ ${_privateKey.mnemonicSentence}
Clipboard.setData(ClipboardData(text: clipData));
}

Future<void> _broadcastAcc() async {
String? uploadedPicture;
String? uploadedBanner;
final fileUploadP = ref.watch(fileUploadProvider);

if (widget.userInfo.picture != null) {
uploadedPicture = await fileUploadP.uploadImage(widget.userInfo.picture!);
}
if (widget.userInfo.banner != null) {
uploadedBanner = await fileUploadP.uploadImage(widget.userInfo.banner!);
}

//! todo: broadcast data to nostr network
final metadataP = ref.watch(metadataProvider);
final followP = ref.watch(followingProvider);

final UserMetadata userMetadata = UserMetadata(
eventId: '',
lastFetch: 0,
pubkey: _privateKey.publicKey,
name: widget.userInfo.name,
picture: uploadedPicture,
banner: uploadedBanner,
about: widget.userInfo.about,
website: widget.userInfo.website,
nip05: widget.userInfo.nip05,
lud06: widget.userInfo.lud06,
lud16: widget.userInfo.lud16,
);

metadataP.broadcastMetadata(userMetadata);
followP.setContacts(widget.userInfo.followPubkeys);
}

@override
void initState() {
super.initState();
Expand Down Expand Up @@ -101,7 +139,9 @@ ${_privateKey.mnemonicSentence}
const storage = FlutterSecureStorage();
storage.write(key: "nostrKeys", value: json.encode(myKeyPair.toJson()));

//! todo: broadcast data to nostr network
await _broadcastAcc();

if (!mounted) return;

// naviage to /
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import 'dart:io';
import 'dart:typed_data';

import 'package:camelus/domain_layer/entities/mem_file.dart';
import 'package:camelus/presentation_layer/atoms/crop_avatar.dart';
import 'package:camelus/presentation_layer/atoms/my_profile_picture.dart';
import 'package:camelus/config/palette.dart';
import 'package:camelus/domain_layer/entities/onboarding_user_info.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mime/mime.dart';

class OnboardingPicture extends ConsumerStatefulWidget {
final Function pictureCallback;
Expand All @@ -31,26 +33,37 @@ class _OnboardingPictureState extends ConsumerState<OnboardingPicture> {
File file = File(result.files.single.path!);

Uint8List imageData = file.readAsBytesSync();
_openCropImagePopup(imageData);
String imageMimeType = lookupMimeType(file.path) ?? '';
String imageName = file.path.split('/').last;

MemFile memFile = MemFile(
bytes: imageData,
mimeType: imageMimeType,
name: imageName,
);
widget.signUpInfo.picture = memFile;
_openCropImagePopup(memFile.bytes);
} else {
// User canceled the picker
return;
}
}

Uint8List? _displayImage;

_openCropImagePopup(Uint8List imageData) {
// push fullscreen widget
Navigator.push(
context,
MaterialPageRoute(
MaterialPageRoute<Uint8List>(
builder: (context) => CropAvatar(
imageData: imageData,
),
),
).then((value) {
if (value != null) {
setState(() {
widget.signUpInfo.picture = value;
widget.signUpInfo.picture!.bytes = value;
});
}
});
Expand All @@ -74,11 +87,11 @@ class _OnboardingPictureState extends ConsumerState<OnboardingPicture> {
imageUrl: null,
pubkey: widget.signUpInfo.keyPair.publicKey,
),
if (widget.signUpInfo.picture != null)
if (widget.signUpInfo.picture?.bytes != null)
SizedBox.fromSize(
size: const Size.fromRadius(30), // Image radius
child: Image(image: MemoryImage(widget.signUpInfo.picture!)),
)
child: Image.memory(widget.signUpInfo.picture!.bytes),
),
],
),
);
Expand Down

0 comments on commit ab01a3e

Please sign in to comment.