Skip to content
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
28 changes: 2 additions & 26 deletions homete/Model/Dependencies/HouseworkClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,10 @@ extension HouseworkClient: DependencyClient {
}
} snapshotListener: { id, cohabitantId, anchorDate, offset in

let targetDateList = calcTargetPeriod(
let targetDateList = HouseworkIndexedDate.calcTargetPeriod(
anchorDate: anchorDate,
offsetDays: offset,
calendar: Calendar.autoupdatingCurrent,
locale: .current
calendar: .autoupdatingCurrent
)

return await FirestoreService.shared.addSnapshotListener(id: id) {
Expand All @@ -83,26 +82,3 @@ extension HouseworkClient: DependencyClient {

static let previewValue = HouseworkClient()
}

private extension HouseworkClient {

static func calcTargetPeriod(
anchorDate: Date,
offsetDays: Int,
calendar: Calendar,
locale: Locale
) -> [[String: String]] {

let base = calendar.startOfDay(for: anchorDate)
guard offsetDays >= 0 else {

return [["value": HouseworkIndexedDate(base, locale: locale).value]]
}
// -offset ... +offset の範囲を列挙
return (-offsetDays...offsetDays).compactMap { delta in

guard let date = calendar.date(byAdding: .day, value: delta, to: base) else { return nil }
return ["value": HouseworkIndexedDate(base, locale: locale).value]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ struct DailyHouseworkList: Equatable, Sendable {
static func makeInitialValue(
selectedDate: Date,
items: [HouseworkItem],
calendar: Calendar,
locale: Locale
calendar: Calendar
) -> Self {

return .init(
items: items,
metaData: .init(selectedDate: selectedDate, calendar: calendar, locale: locale)
metaData: .init(selectedDate: selectedDate, calendar: calendar)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ struct DailyHouseworkMetaData: Equatable {

extension DailyHouseworkMetaData {

init(selectedDate: Date, calendar: Calendar, locale: Locale) {
init(selectedDate: Date, calendar: Calendar) {

let indexedDate = HouseworkIndexedDate(selectedDate, locale: locale)
let indexedDate = HouseworkIndexedDate(selectedDate, calendar: calendar)
let expiredAt = calendar.date(byAdding: .month, value: 1, to: selectedDate) ?? selectedDate
self.init(indexedDate: indexedDate, expiredAt: expiredAt)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ extension HouseworkBoardList {
init(
dailyList: [DailyHouseworkList],
selectedDate: Date,
locale: Locale
calendar: Calendar
) {

items = dailyList
.first {
$0.metaData.indexedDate == .init(selectedDate, locale: locale)
$0.metaData.indexedDate == .init(selectedDate, calendar: calendar)
}?.items ?? []
}
}
36 changes: 26 additions & 10 deletions homete/Model/Domain/Cohabitant/Housework/HouseworkIndexedDate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,36 @@
import Foundation

struct HouseworkIndexedDate: Equatable, Codable, Hashable {

let value: String

static func calcTargetPeriod(
anchorDate: Date,
offsetDays: Int,
calendar: Calendar
) -> [[String: String]] {

let base = calendar.startOfDay(for: anchorDate)
guard offsetDays >= 0 else {

return [["value": HouseworkIndexedDate(base, calendar: calendar).value]]
}
// -offset ... +offset の範囲を列挙
return (-offsetDays...offsetDays).compactMap { delta in

guard let date = calendar.date(byAdding: .day, value: delta, to: base) else { return nil }
return ["value": HouseworkIndexedDate(date, calendar: calendar).value]
}
}
}

extension HouseworkIndexedDate {

private static let formatStyle = Date.FormatStyle(date: .numeric, time: .omitted)
.year(.extended(minimumLength: 4))
.month(.twoDigits)
.day(.twoDigits)

init(_ date: Date, locale: Locale = .init(identifier: "ja_JP")) {
value = date.formatted(
Self.formatStyle
.locale(locale)
)
init(_ date: Date, calendar: Calendar) {
let formatStyle = Date.FormatStyle(date: .numeric, time: .omitted, calendar: calendar)
.year(.extended(minimumLength: 4))
.month(.twoDigits)
.day(.twoDigits)
value = date.formatted(formatStyle)
}
}
56 changes: 49 additions & 7 deletions homete/Model/Domain/Cohabitant/Housework/HouseworkItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,38 @@ import Foundation
struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable {

let id: String
/// 家事の日付情報
let indexedDate: HouseworkIndexedDate
/// 家事のタイトル
let title: String
/// 家事ポイント
let point: Int
/// 家事ステータス
let state: HouseworkState
/// 実行者のユーザID
let executorId: String?
/// 実行日時
let executedAt: Date?
/// 確認者のユーザID
let reviewerId: String?
/// 承認日時
let approvedAt: Date?
/// 確認コメント
let reviewerComment: String?
/// 有効期限
let expiredAt: Date

var formattedIndexedDate: String {

return indexedDate.value
}

/// レビュー可能かどうか
func canReview(ownUserId: String) -> Bool {

return executorId != ownUserId && state != .completed
}

func updatePendingApproval(at now: Date, changer: String) -> Self {

return .init(
Expand All @@ -33,6 +52,26 @@ struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable {
state: .pendingApproval,
executorId: changer,
executedAt: now,
reviewerId: reviewerId,
approvedAt: approvedAt,
reviewerComment: reviewerComment,
expiredAt: expiredAt
)
}

func updateApproved(at now: Date, reviewer: String, comment: String) -> Self {

return .init(
id: id,
indexedDate: indexedDate,
title: title,
point: point,
state: .completed,
executorId: executorId,
executedAt: executedAt,
reviewerId: reviewer,
approvedAt: now,
reviewerComment: comment,
expiredAt: expiredAt
)
}
Expand All @@ -47,15 +86,12 @@ struct HouseworkItem: Identifiable, Equatable, Sendable, Hashable, Codable {
state: .incomplete,
executorId: nil,
executedAt: nil,
reviewerId: nil,
approvedAt: nil,
reviewerComment: nil,
expiredAt: expiredAt
)
}

func isApprovable(_ userId: String) -> Bool {

guard let executorId else { return false }
return executorId != userId
}
}

extension HouseworkItem {
Expand All @@ -67,7 +103,10 @@ extension HouseworkItem {
metaData: DailyHouseworkMetaData,
state: HouseworkState = .incomplete,
executorId: String? = nil,
executedAt: Date? = nil
executedAt: Date? = nil,
reviewerId: String? = nil,
approvedAt: Date? = nil,
reviewerComment: String? = nil
) {

self.init(
Expand All @@ -78,6 +117,9 @@ extension HouseworkItem {
state: state,
executorId: executorId,
executedAt: executedAt,
reviewerId: reviewerId,
approvedAt: approvedAt,
reviewerComment: reviewerComment,
expiredAt: metaData.expiredAt
)
}
Expand Down
25 changes: 25 additions & 0 deletions homete/Model/Domain/Cohabitant/Housework/HouseworkListStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ final class HouseworkListStore {

Task.detached {

// TODO: PushNotificationContentにファクトリーメソッドを定義する
let notificationContent = PushNotificationContent(
title: "新しい家事が登録されました",
message: newItem.title
Expand Down Expand Up @@ -100,6 +101,30 @@ final class HouseworkListStore {
}
}

func approved(target: HouseworkItem, now: Date, reviwer: Account, comment: String) async throws {

let targetIndexedDate = target.indexedDate
let targetId = target.id

guard let targetItem = items.item(targetId, targetIndexedDate) else {

preconditionFailure("Not found target item(id: \(targetId), indexedDate: \(targetIndexedDate))")
}

let updatedItem = targetItem.updateApproved(at: now, reviewer: reviwer.id, comment: comment)
try await houseworkClient.insertOrUpdateItem(updatedItem, cohabitantId)

Task.detached {

let notificationContent = PushNotificationContent.approvedMessage(
reviwerName: reviwer.userName,
houseworkTitle: target.title,
comment: comment
)
try await self.cohabitantPushNotificationClient.send(self.cohabitantId, notificationContent)
}
}

func returnToIncomplete(target: HouseworkItem, now: Date) async throws {

let targetIndexedDate = target.indexedDate
Expand Down
10 changes: 10 additions & 0 deletions homete/Model/Domain/PushNotificationContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ struct PushNotificationContent: Equatable {
let title: String
let message: String
}

extension PushNotificationContent {

static func approvedMessage(reviwerName: String, houseworkTitle: String, comment: String) -> Self {
return .init(
title: "\(reviwerName)が「\(houseworkTitle)」を承認しました!",
message: comment
)
}
}
39 changes: 39 additions & 0 deletions homete/Views/Components/PreviewUtil/HouseworkUtil.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// HouseworkUtil.swift
// homete
//
// Created by Taichi Sato on 2026/01/12.
//

import Foundation

extension HouseworkItem {

static func makeForPreview(
id: String = UUID().uuidString,
title: String = "",
point: Int = 10,
indexedDate: HouseworkIndexedDate = .init(value: "2026/01/01"),
expiredAt: Date = .distantFuture,
state: HouseworkState = .incomplete,
executorId: String? = nil,
executedAt: Date? = nil,
reviewerId: String? = nil,
approvedAt: Date? = nil,
reviewerComment: String? = nil
) -> Self {

return .init(
id: id,
title: title,
point: point,
metaData: .init(indexedDate: indexedDate, expiredAt: expiredAt),
state: state,
executorId: executorId,
executedAt: executedAt,
reviewerId: reviewerId,
approvedAt: approvedAt,
reviewerComment: reviewerComment
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private extension HouseworkItemPropertyListContent {
title: "洗濯",
point: 10,
metaData: .init(
indexedDate: .init(.init(timeIntervalSince1970: 0)),
indexedDate: .init(value: "2026/1/1"),
expiredAt: .init(timeIntervalSince1970: 0)
),
executedAt: .distantFuture
Expand Down
Loading
Loading