Skip to content

Commit

Permalink
Merge pull request #262 from hackdays-io/feature/ashistory
Browse files Browse the repository at this point in the history
Add assist credit hisotry
  • Loading branch information
yu23ki14 authored Jan 12, 2025
2 parents a20bddf + c13027d commit 75357c4
Show file tree
Hide file tree
Showing 9 changed files with 595 additions and 741 deletions.
111 changes: 111 additions & 0 deletions pkgs/frontend/app/components/assistcredit/History.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Box, HStack, Text, VStack } from "@chakra-ui/react";
import { TransferFractionToken_OrderBy } from "gql/graphql";
import { useNamesByAddresses } from "hooks/useENS";
import { useGetTransferFractionTokens } from "hooks/useFractionToken";
import { useGetHat } from "hooks/useHats";
import { type FC, useMemo } from "react";
import { FaChevronRight } from "react-icons/fa6";
import type { HatsDetailSchama } from "types/hats";
import { ipfs2https } from "utils/ipfs";
import { abbreviateAddress } from "utils/wallet";
import { HatsListItemParser } from "../common/HatsListItemParser";
import { UserIcon } from "../icon/UserIcon";

interface Props {
treeId: string;
limit?: number;
}

interface ItemProps {
from: string;
to: string;
hatId: string;
amount: number;
timestamp: number;
}

interface AssistCreditTextProps {
amount: number;
detail?: HatsDetailSchama;
}

const AssistCreaditText: FC<AssistCreditTextProps> = ({ detail, amount }) => {
return (
<Text as="span">
{detail?.data.name}のアシストクレジットを
<Box as="span" fontWeight="bold">
{amount}
</Box>
送りました!
</Text>
);
};

const AssistCreditItem: FC<ItemProps> = ({
from,
to,
hatId,
amount,
timestamp,
}) => {
const addresses = useMemo(() => {
return [from, to];
}, [from, to]);

const { names } = useNamesByAddresses(addresses);

const { hat } = useGetHat(hatId);

const fromUser = useMemo(() => {
return names?.[0]?.[0];
}, [names]);

const toUser = useMemo(() => {
return names?.[1]?.[0];
}, [names]);

return (
<HStack flexWrap="wrap" rowGap={0}>
<HatsListItemParser imageUri={hat?.imageUri} detailUri={hat?.details}>
<>
<UserIcon
size="25px"
userImageUrl={ipfs2https(fromUser?.text_records?.avatar)}
/>
<Text as="span">{fromUser?.name || abbreviateAddress(from)}</Text>
<UserIcon
size="25px"
userImageUrl={ipfs2https(toUser?.text_records?.avatar)}
/>
<Text as="span">{toUser?.name || abbreviateAddress(to)}</Text>
</>
<AssistCreaditText amount={amount} />
</HatsListItemParser>
</HStack>
);
};

export const AssistCreditHistory: FC<Props> = ({ treeId, limit }) => {
const { data } = useGetTransferFractionTokens({
where: {
workspaceId: treeId,
},
orderBy: TransferFractionToken_OrderBy.BlockTimestamp,
first: limit,
});

return (
<VStack rowGap={4}>
{data?.transferFractionTokens.map((token) => (
<AssistCreditItem
key={`th_${token.id}`}
from={token.from}
to={token.to}
hatId={token.hatId}
amount={token.amount}
timestamp={token.blockTimestamp}
/>
))}
</VStack>
);
};
4 changes: 2 additions & 2 deletions pkgs/frontend/app/components/roles/MyRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ export const MyRole: FC<MyRoleProps> = (params) => {
<CommonButton
onClick={() => navigate(`/${treeId}/${hatId}/${address}`)}
>
See Detail
詳細をみる
</CommonButton>
<CommonButton
onClick={() =>
navigate(`/${treeId}/${hatId}/${address}/assistcredit/send`)
}
>
Transfer Assist Credit
アシストクレジットを送る
</CommonButton>
</VStack>
</HStack>
Expand Down
20 changes: 17 additions & 3 deletions pkgs/frontend/app/routes/$treeId._index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AspectRatio,
Box,
HStack,
Heading,
SimpleGrid,
Text,
Expand All @@ -12,6 +13,7 @@ import { useActiveWallet } from "hooks/useWallet";
import type { FC } from "react";
import { FaPlus } from "react-icons/fa6";
import { StickyNav } from "~/components/StickyNav";
import { AssistCreditHistory } from "~/components/assistcredit/History";
import { CommonButton } from "~/components/common/CommonButton";
import { HatsListItemParser } from "~/components/common/HatsListItemParser";
import { MyRole } from "~/components/roles/MyRole";
Expand All @@ -28,9 +30,21 @@ const WorkspaceTop: FC = () => {

return (
<>
<Box my={4}>
<HStack justify="space-between" alignItems="center" pb={4}>
<Heading>直近のアクティビティ</Heading>
<Link to={`/${treeId}/assistcredit-history`}>
<CommonButton size="xs" bgColor="blue.400">
もっと見る
</CommonButton>
</Link>
</HStack>
{treeId && <AssistCreditHistory limit={3} treeId={treeId} />}
</Box>

{/* My roles */}
<Box mb={4}>
<Heading pb={4}>My Roles</Heading>
<Box my={4}>
<Heading py={4}>自分の役割</Heading>
<VStack>
{tree?.hats
?.filter((h) => Number(h.levelAtLocalTree) >= 2)
Expand All @@ -49,7 +63,7 @@ const WorkspaceTop: FC = () => {

{/* All roles */}
<Box my={4}>
<Heading py={4}>All Roles</Heading>
<Heading py={4}>役割一覧</Heading>
<SimpleGrid columns={4} gap={4}>
{tree?.hats
?.filter((h) => Number(h.levelAtLocalTree) >= 2)
Expand Down
40 changes: 14 additions & 26 deletions pkgs/frontend/app/routes/$treeId_.assistcredit-history.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,25 @@
import { Box, Heading, List, Text } from "@chakra-ui/react";
import { Box, Heading } from "@chakra-ui/react";
import { useParams } from "@remix-run/react";
import { useGetTransferFractionTokens } from "hooks/useFractionToken";
import type { FC } from "react";
import { abbreviateAddress } from "utils/wallet";
import { StickyNav } from "~/components/StickyNav";
import { PageHeader } from "~/components/PageHeader";
import { AssistCreditHistory } from "~/components/assistcredit/History";

const WorkspaceMember: FC = () => {
const { treeId } = useParams();

const { data } = useGetTransferFractionTokens({
where: {
workspaceId: treeId,
},
});

return (
<>
{/* Members */}
<Box mb={4}>
<Heading pb={4}>Transaction History</Heading>
<List.Root>
{data?.transferFractionTokens.map((token) => (
<List.Item key={token.id}>
<Text>
{abbreviateAddress(token.from)}{abbreviateAddress(token.to)}{" "}
: {token.amount}
</Text>
</List.Item>
))}
</List.Root>
<Box my={4}>
<PageHeader
title={
<Heading size="lg" fontWeight="bold">
アクティビティ一覧
</Heading>
}
/>
<Box mt={5}>
{treeId && <AssistCreditHistory treeId={treeId} limit={100} />}
</Box>
<StickyNav />
</>
</Box>
);
};

Expand Down
75 changes: 26 additions & 49 deletions pkgs/frontend/gql/fragment-masking.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,87 @@
/* eslint-disable */
import type {
DocumentTypeDecoration,
ResultOf,
TypedDocumentNode,
} from "@graphql-typed-document-node/core";
import type { FragmentDefinitionNode } from "graphql";
import type { Incremental } from "./graphql";
import { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import { FragmentDefinitionNode } from 'graphql';
import { Incremental } from './graphql';

export type FragmentType<
TDocumentType extends DocumentTypeDecoration<any, any>,
> = TDocumentType extends DocumentTypeDecoration<infer TType, any>
? [TType] extends [{ " $fragmentName"?: infer TKey }]

export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
infer TType,
any
>
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
? TKey extends string
? { " $fragmentRefs"?: { [key in TKey]: TType } }
? { ' $fragmentRefs'?: { [key in TKey]: TType } }
: never
: never
: never;

// return non-nullable if `fragmentType` is non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
): TType;
// return nullable if `fragmentType` is undefined
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | undefined
): TType | undefined;
// return nullable if `fragmentType` is nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null
): TType | null;
// return nullable if `fragmentType` is nullable or undefined
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| FragmentType<DocumentTypeDecoration<TType, any>>
| null
| undefined,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
): TType | null | undefined;
// return array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>,
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>>
): Array<TType>;
// return array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| Array<FragmentType<DocumentTypeDecoration<TType, any>>>
| null
| undefined,
fragmentType: Array<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): Array<TType> | null | undefined;
// return readonly array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
): ReadonlyArray<TType>;
// return readonly array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
| null
| undefined,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): ReadonlyArray<TType> | null | undefined;
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType:
| FragmentType<DocumentTypeDecoration<TType, any>>
| Array<FragmentType<DocumentTypeDecoration<TType, any>>>
| ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
| null
| undefined,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | Array<FragmentType<DocumentTypeDecoration<TType, any>>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): TType | Array<TType> | ReadonlyArray<TType> | null | undefined {
return fragmentType as any;
}


export function makeFragmentData<
F extends DocumentTypeDecoration<any, any>,
FT extends ResultOf<F>,
FT extends ResultOf<F>
>(data: FT, _fragment: F): FragmentType<F> {
return data as FragmentType<F>;
}
export function isFragmentReady<TQuery, TFrag>(
queryNode: DocumentTypeDecoration<TQuery, any>,
fragmentNode: TypedDocumentNode<TFrag>,
data:
| FragmentType<TypedDocumentNode<Incremental<TFrag>, any>>
| null
| undefined,
data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined
): data is FragmentType<typeof fragmentNode> {
const deferredFields = (
queryNode as {
__meta__?: { deferredFields: Record<string, (keyof TFrag)[]> };
}
).__meta__?.deferredFields;
const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__
?.deferredFields;

if (!deferredFields) return true;

const fragDef = fragmentNode.definitions[0] as
| FragmentDefinitionNode
| undefined;
const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined;
const fragName = fragDef?.name?.value;

const fields = (fragName && deferredFields[fragName]) || [];
return fields.length > 0 && fields.every((field) => data && field in data);
return fields.length > 0 && fields.every(field => data && field in data);
}
Loading

0 comments on commit 75357c4

Please sign in to comment.