Skip to content

Commit

Permalink
Merge pull request #144 from kosukesaigusa/119_create_update_job_page
Browse files Browse the repository at this point in the history
#119 仕事編集、作成ページの作成
  • Loading branch information
RikitoNoto authored Aug 15, 2023
2 parents b02c79f + 7e4eb16 commit cec36fc
Show file tree
Hide file tree
Showing 12 changed files with 972 additions and 185 deletions.
17 changes: 13 additions & 4 deletions packages/dart_flutter_common/lib/src/widgets/section.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Section extends StatelessWidget {
/// [Section] を作成する。
const Section({
required this.title,
this.titleBadge,
this.titleStyle,
this.titleMaxLines = 1,
this.titleBottomMargin = 16,
Expand All @@ -24,6 +25,9 @@ class Section extends StatelessWidget {
/// セクションのタイトル。
final String title;

/// タイトルの横に配置されるバッジ
final Widget? titleBadge;

/// [title] のスタイル。
final TextStyle? titleStyle;

Expand Down Expand Up @@ -58,10 +62,15 @@ class Section extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: titleStyle,
maxLines: titleMaxLines,
Row(
children: [
Text(
title,
style: titleStyle,
maxLines: titleMaxLines,
),
if (titleBadge != null) titleBadge!,
],
),
SizedBox(height: titleBottomMargin),
if (description != null) ...[
Expand Down
32 changes: 21 additions & 11 deletions packages/dart_flutter_common/lib/src/widgets/selectable_chips.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SelectableChips<T> extends StatelessWidget {
this.spacing = 8,
this.runSpacing = 0,
this.crossAxisAlignment = WrapCrossAlignment.start,
this.onTap,
this.chipPadding = const EdgeInsets.all(8),
super.key,
});
Expand Down Expand Up @@ -48,6 +49,9 @@ class SelectableChips<T> extends StatelessWidget {
/// [direction] と垂直方向の並び方。
final WrapCrossAlignment crossAxisAlignment;

/// チップが押されたときのコールバック
final void Function(T item)? onTap;

/// チップの余白。
final EdgeInsetsGeometry chipPadding;

Expand All @@ -68,6 +72,7 @@ class SelectableChips<T> extends StatelessWidget {
label: labels[item] ?? '',
isEnabled: enabledItems.contains(item),
padding: chipPadding,
onTap: () => onTap?.call(item),
),
)
.toList(),
Expand All @@ -80,27 +85,32 @@ class _SwitchingChip extends StatelessWidget {
required this.label,
required this.padding,
this.isEnabled = false,
this.onTap,
});

final String label;
final EdgeInsetsGeometry padding;
final bool isEnabled;
final void Function()? onTap;

@override
Widget build(BuildContext context) {
return InputChip(
onPressed: () => {},
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
isEnabled: isEnabled,
side: isEnabled ? BorderSide.none : null,
label: Text(
label,
style: TextStyle(
color: Theme.of(context).colorScheme.onSecondaryContainer,
return GestureDetector(
onTap: onTap,
child: InputChip(
onPressed: onTap, // コールバックを設定しないとデザインが変わるので、設定している
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
isEnabled: isEnabled,
side: isEnabled ? BorderSide.none : null,
label: Text(
label,
style: TextStyle(
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
padding: padding,
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
),
padding: padding,
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
);
}
}
56 changes: 56 additions & 0 deletions packages/firebase_common/lib/src/firestore_repositories/job.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,60 @@ class JobRepository {
_query.subscribeDocuments(
queryBuilder: (query) => query.where('hostId', isEqualTo: hostId),
);

/// [Job] の情報を作成する。
Future<void> create({
required String hostId,
required String title,
required String content,
required String place,
required Set<AccessType> accessTypes,
required String accessDescription,
required String belongings,
required String reward,
required String comment,
required String imageUrl,
}) =>
_query.add(
createJob: CreateJob(
hostId: hostId,
title: title,
content: content,
place: place,
accessTypes: accessTypes,
accessDescription: accessDescription,
belongings: belongings,
reward: reward,
comment: comment,
imageUrl: imageUrl,
),
);

/// [Job] の情報を更新する。
Future<void> update({
required String jobId,
String? title,
String? content,
String? place,
Set<AccessType>? accessTypes,
String? accessDescription,
String? belongings,
String? reward,
String? comment,
String? imageUrl,
}) =>
_query.update(
jobId: jobId,
updateJob: UpdateJob(
title: title,
content: content,
place: place,
accessTypes: accessTypes,
accessDescription: accessDescription,
belongings: belongings,
reward: reward,
comment: comment,
imageUrl: imageUrl,
),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import '../../../chat/ui/chat_room.dart';
import '../../../chat/ui/chat_rooms.dart';
import '../../../host/ui/create_or_update_host.dart';
import '../../../host/ui/host.dart';
import '../../../job/ui/job_create.dart';
import '../../../job/ui/job_detail.dart';
import '../../../job/ui/job_update.dart';
import '../../../map/ui/map.dart';
import '../../../worker/ui/worker.dart';
import '../../color/ui/color.dart';
Expand Down Expand Up @@ -60,6 +62,16 @@ class DevelopmentItemsPage extends ConsumerWidget {
JobDetailPage.location(jobId: 'PYRsrMSOApEgZ6lzMuUK'),
),
),
ListTile(
title: const Text('仕事情報作成ページ'),
onTap: () => context.router.pushNamed(JobCreatePage.location),
),
ListTile(
title: const Text('仕事情報編集ページ'),
onTap: () => context.router.pushNamed(
JobUpdatePage.location(jobId: 'PYRsrMSOApEgZ6lzMuUK'),
),
),
ListTile(
title: const Text('チャットルーム一覧ページ(StreamProvider、未既読管理)'),
onTap: () => context.router.pushNamed(ChatRoomsPage.location),
Expand Down
52 changes: 52 additions & 0 deletions packages/mottai_flutter_app/lib/job/job.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,58 @@ class JobService {
Stream<ReadJob?> subscribeJob({required String jobId}) =>
_jobRepository.subscribeJob(jobId: jobId);

/// [Job] の情報を作成する。
Future<void> create({
required String hostId,
required String title,
required String content,
required String place,
required Set<AccessType> accessTypes,
required String accessDescription,
required String belongings,
required String reward,
required String comment,
required String imageUrl,
}) =>
_jobRepository.create(
hostId: hostId,
title: title,
content: content,
place: place,
accessTypes: accessTypes,
accessDescription: accessDescription,
belongings: belongings,
reward: reward,
comment: comment,
imageUrl: imageUrl,
);

/// [Job] の情報を更新する。
Future<void> update({
required String jobId,
String? title,
String? content,
String? place,
Set<AccessType>? accessTypes,
String? accessDescription,
String? belongings,
String? reward,
String? comment,
String? imageUrl,
}) =>
_jobRepository.update(
jobId: jobId,
title: title,
content: content,
place: place,
accessTypes: accessTypes,
accessDescription: accessDescription,
belongings: belongings,
reward: reward,
comment: comment,
imageUrl: imageUrl,
);

/// 指定したユーザーの [Job] を全件取得する。
Future<List<ReadJob>> fetchUserJobs({required String hostId}) =>
_jobRepository.fetchUserJobs(hostId: hostId);
Expand Down
103 changes: 103 additions & 0 deletions packages/mottai_flutter_app/lib/job/ui/job_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'dart:io';

import 'package:firebase_common/firebase_common.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import '../../image/firebase_storage.dart';
import '../job.dart';

final jobControllerProvider =
Provider.family.autoDispose<JobController, String>(
(ref, userId) => JobController(
jobService: ref.watch(jobServiceProvider),
firebaseStorageService: ref.watch(firebaseStorageServiceProvider),
userId: userId,
),
);

class JobController {
const JobController({
required JobService jobService,
required FirebaseStorageService firebaseStorageService,
required String userId,
}) : _jobService = jobService,
_firebaseStorageService = firebaseStorageService,
_userId = userId;

/// storageの画像を保存するフォルダのパス
static const _storagePath = 'jobs';

final JobService _jobService;
final FirebaseStorageService _firebaseStorageService;
final String _userId;

/// [Job] の情報を作成する。
Future<void> create({
required String hostId,
required String title,
required String content,
required String place,
required Set<AccessType> accessTypes,
required String accessDescription,
required String belongings,
required String reward,
required String comment,
File? imageFile,
}) async {
var imageUrl = '';
if (imageFile != null) {
imageUrl = await _uploadImage(imageFile);
}
await _jobService.create(
hostId: hostId,
title: title,
content: content,
place: place,
accessTypes: accessTypes,
accessDescription: accessDescription,
belongings: belongings,
reward: reward,
comment: comment,
imageUrl: imageUrl,
);
}

/// [Job] を更新する。
Future<void> updateJob({
required String jobId,
String? title,
String? content,
String? place,
Set<AccessType>? accessTypes,
String? accessDescription,
String? belongings,
String? reward,
String? comment,
File? imageFile,
}) async {
String? imageUrl;
if (imageFile != null) {
imageUrl = await _uploadImage(imageFile);
}
await _jobService.update(
jobId: jobId,
title: title,
content: content,
place: place,
accessTypes: accessTypes,
accessDescription: accessDescription,
belongings: belongings,
reward: reward,
comment: comment,
imageUrl: imageUrl,
);
}

Future<String> _uploadImage(File imageFile) {
final imagePath = '$_storagePath/$_userId-${DateTime.now()}.jpg';
return _firebaseStorageService.upload(
path: imagePath,
resource: FirebaseStorageFile(imageFile),
);
}
}
32 changes: 32 additions & 0 deletions packages/mottai_flutter_app/lib/job/ui/job_create.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import '../../auth/ui/auth_dependent_builder.dart';
import 'job_form.dart';

/// 仕事情報更新ページ。
@RoutePage()
class JobCreatePage extends ConsumerWidget {
const JobCreatePage({
super.key,
});

static const path = '/jobs/create';

/// [JobCreatePage] に遷移する際に `context.router.pushNamed` で指定する文字列。
static const location = path;

@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('お手伝い募集内容を入力')),
// TODO:なるんさんが実装中の「本人かどうかビルダー」に後で書き換える。
body: AuthDependentBuilder(
onAuthenticated: (hostId) {
return JobForm.create(hostId: hostId);
},
),
);
}
}
Loading

0 comments on commit cec36fc

Please sign in to comment.