Skip to content

Commit

Permalink
feat: ✨ 所有者を匿名化できるように修正
Browse files Browse the repository at this point in the history
  • Loading branch information
dino3616 committed Feb 25, 2024
1 parent e5a7ba8 commit d7d8e59
Show file tree
Hide file tree
Showing 33 changed files with 499 additions and 160 deletions.
14 changes: 14 additions & 0 deletions apps/api/src/module/user/controller/user-mutation.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ export class UserMutation {
return createdUser;
}

@Mutation(() => UserObject)
async updateUserDisclosure(
@Args('where', { type: () => UserWhereAuthIdInput }, ValidationPipe)
where: UserWhereAuthIdInput,
@Args('isDiscloseAsOwner', { type: () => Boolean }, ValidationPipe)
isDiscloseAsOwner: boolean,
): Promise<User> {
this.logger.log(`${this.updateUserDisclosure.name} called`);

const updatedUser = await this.userUseCase.updateUserDisclosure(where.authId, isDiscloseAsOwner);

return updatedUser;
}

@Mutation(() => UserObject)
async relateFingerprintWithUser(
@Args('where', { type: () => UserWhereAuthIdInput }, ValidationPipe)
Expand Down
9 changes: 9 additions & 0 deletions apps/api/src/module/user/use-case/impl/user.use-case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ export class UserUseCase implements UserUseCaseInterface {
return createdUser;
}

async updateUserDisclosure(
authId: Parameters<UserUseCaseInterface['updateUserDisclosure']>[0],
isDiscloseAsOwner: Parameters<UserUseCaseInterface['updateUserDisclosure']>[1],
): Promise<User> {
const updatedUser = await this.userRepository.updateByAuthId(authId, { isDiscloseAsOwner });

return updatedUser;
}

async relateFingerprintWithUser(
authId: Parameters<UserUseCaseInterface['relateFingerprintWithUser']>[0],
hashedFingerprintId: Parameters<UserUseCaseInterface['relateFingerprintWithUser']>[1],
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/module/user/use-case/user.use-case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export interface UserUseCaseInterface {
findUser(authId: User['authId']): Promise<User | null>;
findUserByHashedFingerprintId(hashedFingerprintId: NonNullable<User['hashedFingerprintId']>): Promise<User | null>;
createUser(user: Omit<User, 'id' | 'hashedFingerprintId' | 'lostAndFoundState' | 'isDiscloseAsOwner' | 'createdAt' | 'isOnTheWay'>): Promise<User>;
updateUserDisclosure(authId: User['authId'], isDiscloseAsOwner: User['isDiscloseAsOwner']): Promise<User>;
relateFingerprintWithUser(authId: User['authId'], hashedFingerprintId: NonNullable<User['hashedFingerprintId']>): Promise<User>;
}
49 changes: 49 additions & 0 deletions apps/locker-dashboard/graphql.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,55 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateUserDisclosure",
"description": null,
"args": [
{
"name": "isDiscloseAsOwner",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "where",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UserWhereAuthIdInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "User",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
Expand Down
49 changes: 49 additions & 0 deletions apps/website/graphql.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,55 @@
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updateUserDisclosure",
"description": null,
"args": [
{
"name": "isDiscloseAsOwner",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "where",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "UserWhereAuthIdInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "User",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Image } from '@lockerai/core/component/image';
import { DotIcon } from '@lockerai/core/icon/dot-icon';
import { UserAvatarPlaceholderIcon } from '@lockerai/core/icon/user-avatar-placeholder-icon';
import { formatDate } from '@lockerai/core/util/format-date';
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
import type { LostItem } from '#website/common/model/lost-item';
Expand All @@ -12,40 +13,14 @@ type UserActionStatusListProps = Omit<ComponentPropsWithoutRef<'div'>, 'children
lostItem: LostItem;
};

export const UserActionStatusList = ({ user, reporter, owner, lostItem, ...props }: UserActionStatusListProps): ReactNode => (
<div className="flex flex-col gap-4" {...props}>
<div className="flex items-center gap-3">
<Image
src={reporter.avatarUrl}
alt=""
width={36}
height={36}
priority
skeleton={{
className: 'rounded-full',
}}
className="h-9 w-9"
/>
<div className="flex flex-col gap-1">
<p className="text-base font-bold text-sage-12">
{reporter.name}
{reporter.id === user?.id ? <span className="text-sage-11"> (You)</span> : null}
</p>
<div className="flex flex-col gap-1 tablet:flex-row tablet:items-center">
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.reportedAt, 'MMM. dd, yyyy HH:mm')} reported</p>
{lostItem.deliveredAt && (
<>
<DotIcon className="hidden h-4 w-4 fill-sage-11 tablet:inline" />
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.deliveredAt, 'MMM. dd, yyyy HH:mm')} delivered</p>
</>
)}
</div>
</div>
</div>
{owner && lostItem.ownedAt && (
<div className="flex items-center gap-3" {...props}>
export const UserActionStatusList = ({ user, reporter, owner, lostItem, ...props }: UserActionStatusListProps): ReactNode => {
const isOwnerDisclose = (user && owner && (user.id === owner.id || owner.isDiscloseAsOwner)) ?? undefined;

return (
<div className="flex flex-col gap-4" {...props}>
<div className="flex items-center gap-3">
<Image
src={owner.avatarUrl}
src={reporter.avatarUrl}
alt=""
width={36}
height={36}
Expand All @@ -57,20 +32,54 @@ export const UserActionStatusList = ({ user, reporter, owner, lostItem, ...props
/>
<div className="flex flex-col gap-1">
<p className="text-base font-bold text-sage-12">
{owner.name}
{owner.id === user?.id ? <span className="text-sage-11"> (You)</span> : null}
{reporter.name}
{reporter.id === user?.id ? <span className="text-sage-11"> (You)</span> : null}
</p>
<div className="flex flex-col gap-1 tablet:flex-row tablet:items-center">
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.ownedAt, 'MMM. dd, yyyy HH:mm')} owned</p>
{lostItem.retrievedAt && (
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.reportedAt, 'MMM. dd, yyyy HH:mm')} reported</p>
{lostItem.deliveredAt && (
<>
<DotIcon className="hidden h-4 w-4 fill-sage-11 tablet:inline" />
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.retrievedAt, 'MMM. dd, yyyy HH:mm')} retrieved</p>
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.deliveredAt, 'MMM. dd, yyyy HH:mm')} delivered</p>
</>
)}
</div>
</div>
</div>
)}
</div>
);
{owner && lostItem.ownedAt && (
<div className="flex items-center gap-3" {...props}>
{isOwnerDisclose ? (
<Image
src={owner.avatarUrl}
alt=""
width={36}
height={36}
priority
skeleton={{
className: 'rounded-full',
}}
className="h-9 w-9"
/>
) : (
<UserAvatarPlaceholderIcon className="h-9 w-9 fill-sage-11" />
)}
<div className="flex flex-col gap-1">
<p className="text-base font-bold text-sage-12">
{isOwnerDisclose ? owner.name : 'Anonymous'}
{owner.id === user?.id ? <span className="text-sage-11"> (You)</span> : null}
</p>
<div className="flex flex-col gap-1 tablet:flex-row tablet:items-center">
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.ownedAt, 'MMM. dd, yyyy HH:mm')} owned</p>
{lostItem.retrievedAt && (
<>
<DotIcon className="hidden h-4 w-4 fill-sage-11 tablet:inline" />
<p className="text-sm text-sage-11 tablet:text-base">{formatDate(lostItem.retrievedAt, 'MMM. dd, yyyy HH:mm')} retrieved</p>
</>
)}
</div>
</div>
</div>
)}
</div>
);
};
4 changes: 3 additions & 1 deletion apps/website/src/common/model/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ export type User = {
email: string;
lostAndFoundState: LostAndFoundState;
avatarUrl: string;
isDiscloseAsOwner: boolean;
createdAt: Date;
};

export type UserPublicMeta = Pick<User, 'id' | 'name' | 'avatarUrl'>;
export type UserPublicMeta = Pick<User, 'id' | 'name' | 'avatarUrl' | 'isDiscloseAsOwner'>;

export const mockUser = (user: Partial<User> = {}): User => ({
id: 'e069eeb2-a239-44c7-9870-acc1af492264',
Expand All @@ -19,6 +20,7 @@ export const mockUser = (user: Partial<User> = {}): User => ({
email: '[email protected]',
lostAndFoundState: 'NONE',
avatarUrl: 'https://avatars.githubusercontent.com/u/1',
isDiscloseAsOwner: true,
createdAt: new Date(0),
...user,
});
1 change: 1 addition & 0 deletions apps/website/src/infra/graphql/document/create-user.gql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mutation CreateUser($user: UserCreateInput!) {
createdAt
email
id
isDiscloseAsOwner
lostAndFoundState
name
}
Expand Down
1 change: 1 addition & 0 deletions apps/website/src/infra/graphql/document/find-user.gql
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ query FindUser($where: UserWhereAuthIdInput!) {
createdAt
email
id
isDiscloseAsOwner
lostAndFoundState
name
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fragment UserPublicMeta on User {
avatarUrl
id
isDiscloseAsOwner
name
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mutation RelateFingerprintWithUser($hashedFingerprintId: String!, $where: UserWh
createdAt
email
id
isDiscloseAsOwner
lostAndFoundState
name
}
Expand Down
4 changes: 1 addition & 3 deletions apps/website/src/infra/graphql/document/report-lost-item.gql
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ mutation ReportLostItem($imageFiles: [Upload!]!, $lostItem: LostItemCreateInput!
ownedAt
reportedAt
reporter {
id
name
lostAndFoundState
...UserPublicMeta
}
retrievedAt
title
Expand Down
12 changes: 12 additions & 0 deletions apps/website/src/infra/graphql/document/update-user-disclosure.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
mutation UpdateUserDisclosure($isDiscloseAsOwner: Boolean!, $where: UserWhereAuthIdInput!) {
updateUserDisclosure(isDiscloseAsOwner: $isDiscloseAsOwner, where: $where) {
authId
avatarUrl
createdAt
email
id
isDiscloseAsOwner
lostAndFoundState
name
}
}
Loading

0 comments on commit d7d8e59

Please sign in to comment.