@ph-cms/client-sdk 0.1.43 → 0.1.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @ph-cms/client-sdk 0.1.43
1
+ # @ph-cms/client-sdk 0.1.45
2
2
 
3
3
  PH-CMS 클라이언트 SDK — 브라우저 및 React 애플리케이션을 위한 통합 클라이언트.
4
4
 
@@ -8,6 +8,7 @@ PH-CMS 클라이언트 SDK — 브라우저 및 React 애플리케이션을 위
8
8
  - Auth Provider 인터페이스 및 구현체 (`LocalAuthProvider`, `FirebaseAuthProvider`, `StaticAuthProvider`)
9
9
  - React Context 및 Hooks
10
10
  - React Query 통합
11
+ - 장소 검색 모듈 및 React hook
11
12
  - 자동 토큰 갱신 (Proactive + Reactive)
12
13
  - SSR (Server-Side Rendering) 지원 및 Cookie 연동 가능
13
14
  - Firebase ↔ PH-CMS 인증 동기화
@@ -59,6 +60,8 @@ import type {
59
60
  BlockUserRequest,
60
61
  BlockUserResponse,
61
62
  BlockedUserDto,
63
+ FollowStatusResponse,
64
+ FollowUserResponse,
62
65
  PagedBlockedUserListResponse,
63
66
  SuspendUserRequest,
64
67
  SuspendUserResponse,
@@ -100,6 +103,9 @@ import type {
100
103
  // Common
101
104
  PagedResponse,
102
105
  UserProfileDto,
106
+ IntegratedPlace,
107
+ PlaceSearchQuery,
108
+ PlaceSearchResponse,
103
109
  } from '@ph-cms/client-sdk';
104
110
  ```
105
111
 
@@ -107,6 +113,38 @@ import type {
107
113
 
108
114
  ---
109
115
 
116
+ ## Place Search
117
+
118
+ SDK에서 통합 장소 검색 API를 바로 호출할 수 있습니다.
119
+
120
+ ### Core Client
121
+
122
+ ```ts
123
+ const result = await client.place.search({
124
+ keyword: '강남역 맛집',
125
+ });
126
+ ```
127
+
128
+ ### React Hook
129
+
130
+ ```ts
131
+ import { usePlaceSearch } from '@ph-cms/client-sdk';
132
+
133
+ const { data, isLoading } = usePlaceSearch(
134
+ { keyword: '강남역 맛집' },
135
+ true,
136
+ );
137
+ ```
138
+
139
+ 응답의 각 장소 항목은 항상 동일한 키를 가집니다.
140
+
141
+ - `phoneNumber`: `string | null`
142
+ - `link`: `string | null`
143
+ - `photos`: `[]` 포함 항상 존재
144
+ - `reviews`: `[]` 포함 항상 존재
145
+
146
+ ---
147
+
110
148
  ## Authentication Architecture
111
149
 
112
150
  ### Overview
@@ -796,7 +834,7 @@ function UserLikedContents({ userUid }) {
796
834
 
797
835
  ### Public Profile (`useUserProfile`)
798
836
 
799
- 다른 사용자의 공개 프로필(이름, 아바타, 자기소개 등)을 조회할 때 사용하는 훅입니다.
837
+ 다른 사용자의 공개 프로필(이름, 아바타, 자기소개, 팔로워 수 등)을 조회할 때 사용하는 훅입니다.
800
838
 
801
839
  ```tsx
802
840
  import { useUserProfile } from '@ph-cms/client-sdk';
@@ -811,10 +849,47 @@ function UserProfileCard({ userId }) {
811
849
  <div>
812
850
  <img src={profile.avatar_url} alt={profile.display_name} />
813
851
  <h3>{profile.display_name} (@{profile.username})</h3>
852
+ <p>followers: {profile.follower_count}</p>
814
853
  <p>{profile.profile_data?.bio}</p>
815
854
  </div>
816
855
  );
817
856
  }
857
+ ```
858
+
859
+ ### Follow Status / Follow / Unfollow
860
+
861
+ 특정 사용자에 대한 팔로우 상태를 조회하고, 팔로우/언팔로우할 수 있습니다.
862
+
863
+ ```tsx
864
+ import {
865
+ useFollowStatus,
866
+ useFollowUser,
867
+ useUnfollowUser,
868
+ } from '@ph-cms/client-sdk';
869
+
870
+ function FollowButton({ userUid }: { userUid: string }) {
871
+ const { data: status, isLoading } = useFollowStatus(userUid);
872
+ const { mutate: followUser, isPending: isFollowingPending } = useFollowUser();
873
+ const { mutate: unfollowUser, isPending: isUnfollowingPending } = useUnfollowUser();
874
+
875
+ if (isLoading) return <button disabled>Loading...</button>;
876
+
877
+ const isFollowing = status?.is_following ?? false;
878
+ const isPending = isFollowingPending || isUnfollowingPending;
879
+
880
+ return (
881
+ <button
882
+ disabled={isPending}
883
+ onClick={() => {
884
+ if (isFollowing) unfollowUser(userUid);
885
+ else followUser(userUid);
886
+ }}
887
+ >
888
+ {isFollowing ? '언팔로우' : '팔로우'}
889
+ </button>
890
+ );
891
+ }
892
+ ```
818
893
 
819
894
  ### `useLikedStats` (Liked Statistics)
820
895
 
@@ -1214,6 +1289,35 @@ const blockedUsers = await client.user.getBlockedUsers('my-user-uid', { page: 1,
1214
1289
  const { data: blockedList } = useBlockedUsers('my-user-uid', { page: 1 });
1215
1290
  ```
1216
1291
 
1292
+ ## User Follow (사용자 팔로우)
1293
+
1294
+ 특정 사용자를 팔로우하거나 언팔로우할 수 있습니다. (인증 필수)
1295
+
1296
+ ```ts
1297
+ // 1. 팔로우 상태 조회
1298
+ const followStatus = await client.user.getFollowStatus('target-user-uid');
1299
+ // => { is_following: false }
1300
+
1301
+ // React Hook 사용
1302
+ const { data: status } = useFollowStatus('target-user-uid');
1303
+
1304
+ // 2. 사용자 팔로우
1305
+ const followResult = await client.user.follow('target-user-uid');
1306
+ // => { success: true, message: 'User followed', is_following: true }
1307
+
1308
+ // React Hook 사용
1309
+ const { mutate: followUser } = useFollowUser();
1310
+ followUser('target-user-uid');
1311
+
1312
+ // 3. 사용자 언팔로우
1313
+ const unfollowResult = await client.user.unfollow('target-user-uid');
1314
+ // => { success: true, message: 'User unfollowed', is_following: false }
1315
+
1316
+ // React Hook 사용
1317
+ const { mutate: unfollowUser } = useUnfollowUser();
1318
+ unfollowUser('target-user-uid');
1319
+ ```
1320
+
1217
1321
  ## Global Ban (글로벌 밴 - 관리자 전용)
1218
1322
 
1219
1323
  특정 사용자의 활동을 전역적으로 정지시키거나 해제할 수 있습니다. (관리자 권한 필수)
@@ -1651,6 +1755,9 @@ const result = await content.list({ channelUid: 'my-channel' });
1651
1755
  | 메서드 | 설명 |
1652
1756
  |---|---|
1653
1757
  | `getProfile(uid: string)` | 유저의 공개 프로필 정보 조회 → `UserProfileDto` |
1758
+ | `getFollowStatus(uid: string)` | 현재 로그인 사용자의 대상 유저 팔로우 상태 조회 → `FollowStatusResponse` |
1759
+ | `follow(uid: string)` | 특정 사용자 팔로우 → `FollowUserResponse` |
1760
+ | `unfollow(uid: string)` | 특정 사용자 언팔로우 → `FollowUserResponse` |
1654
1761
  | `updateProfile(uid: string, data: UpdateUserProfileRequest)` | 유저의 프로필 정보 업데이트 → `UserDto` |
1655
1762
  | `getLikedContents(uid: string, params?: Partial<ListLikedContentQuery>)` | 사용자가 좋아요 누른 콘텐츠 목록 조회 → `PagedContentListResponse` |
1656
1763
  | `getLikedStats(uid: string)` | 사용자가 좋아요 누른 콘텐츠 타입별 통계 조회 → `LikedStatsByTypeResponse` |
package/dist/client.d.ts CHANGED
@@ -4,6 +4,7 @@ import { AuthModule } from './modules/auth';
4
4
  import { ChannelModule } from './modules/channel';
5
5
  import { ContentModule } from './modules/content';
6
6
  import { MediaModule } from './modules/media';
7
+ import { PlaceModule } from './modules/place';
7
8
  import { TermsModule } from './modules/terms';
8
9
  import { UserModule } from './modules/user';
9
10
  export interface PHCMSClientConfig {
@@ -25,6 +26,7 @@ export declare class PHCMSClient {
25
26
  readonly channel: ChannelModule;
26
27
  readonly terms: TermsModule;
27
28
  readonly media: MediaModule;
29
+ readonly place: PlaceModule;
28
30
  readonly user: UserModule;
29
31
  /**
30
32
  * Whether a token refresh is currently in flight (used by the 401
package/dist/client.js CHANGED
@@ -11,6 +11,7 @@ const auth_1 = require("./modules/auth");
11
11
  const channel_1 = require("./modules/channel");
12
12
  const content_1 = require("./modules/content");
13
13
  const media_1 = require("./modules/media");
14
+ const place_1 = require("./modules/place");
14
15
  const terms_1 = require("./modules/terms");
15
16
  const user_1 = require("./modules/user");
16
17
  class PHCMSClient {
@@ -68,6 +69,7 @@ class PHCMSClient {
68
69
  this.channel = new channel_1.ChannelModule(this.axiosInstance, normalizedApiPrefix);
69
70
  this.terms = new terms_1.TermsModule(this.axiosInstance, normalizedApiPrefix);
70
71
  this.media = new media_1.MediaModule(this.axiosInstance, normalizedApiPrefix);
72
+ this.place = new place_1.PlaceModule(this.axiosInstance, normalizedApiPrefix);
71
73
  this.user = new user_1.UserModule(this.axiosInstance, normalizedApiPrefix);
72
74
  // Wire the refresh function into the auth provider so it can
73
75
  // proactively refresh tokens inside `getToken()` without needing
package/dist/core.d.ts CHANGED
@@ -8,6 +8,7 @@ export * from './modules/auth';
8
8
  export * from './modules/channel';
9
9
  export * from './modules/content';
10
10
  export * from './modules/media';
11
+ export * from './modules/place';
11
12
  export * from './modules/terms';
12
13
  export * from './modules/user';
13
14
  export * from './types';
package/dist/core.js CHANGED
@@ -27,6 +27,7 @@ __exportStar(require("./modules/auth"), exports);
27
27
  __exportStar(require("./modules/channel"), exports);
28
28
  __exportStar(require("./modules/content"), exports);
29
29
  __exportStar(require("./modules/media"), exports);
30
+ __exportStar(require("./modules/place"), exports);
30
31
  __exportStar(require("./modules/terms"), exports);
31
32
  __exportStar(require("./modules/user"), exports);
32
33
  __exportStar(require("./types"), exports);
@@ -23,6 +23,7 @@ export declare const useAuth: () => {
23
23
  username: string | null;
24
24
  display_name: string;
25
25
  avatar_url: string | null;
26
+ follower_count: number;
26
27
  phone_number: string | null;
27
28
  email_verified_at: string | null;
28
29
  phone_verified_at: string | null;
@@ -59,6 +60,7 @@ export declare const useAuth: () => {
59
60
  username: string | null;
60
61
  display_name: string;
61
62
  avatar_url: string | null;
63
+ follower_count: number;
62
64
  phone_number: string | null;
63
65
  email_verified_at: string | null;
64
66
  phone_verified_at: string | null;
@@ -98,6 +100,7 @@ export declare const useAuth: () => {
98
100
  username: string | null;
99
101
  display_name: string;
100
102
  avatar_url: string | null;
103
+ follower_count: number;
101
104
  phone_number: string | null;
102
105
  email_verified_at: string | null;
103
106
  phone_verified_at: string | null;
@@ -137,6 +140,7 @@ export declare const useAuth: () => {
137
140
  username: string | null;
138
141
  display_name: string;
139
142
  avatar_url: string | null;
143
+ follower_count: number;
140
144
  phone_number: string | null;
141
145
  email_verified_at: string | null;
142
146
  phone_verified_at: string | null;
@@ -173,6 +177,7 @@ export declare const useAuth: () => {
173
177
  username: string | null;
174
178
  display_name: string;
175
179
  avatar_url: string | null;
180
+ follower_count: number;
176
181
  phone_number: string | null;
177
182
  email_verified_at: string | null;
178
183
  phone_verified_at: string | null;
@@ -209,6 +214,7 @@ export declare const useAuth: () => {
209
214
  username: string | null;
210
215
  display_name: string;
211
216
  avatar_url: string | null;
217
+ follower_count: number;
212
218
  phone_number: string | null;
213
219
  email_verified_at: string | null;
214
220
  phone_verified_at: string | null;
@@ -251,6 +257,7 @@ export declare const useAuth: () => {
251
257
  username: string | null;
252
258
  display_name: string;
253
259
  avatar_url: string | null;
260
+ follower_count: number;
254
261
  phone_number: string | null;
255
262
  email_verified_at: string | null;
256
263
  phone_verified_at: string | null;
@@ -290,6 +297,7 @@ export declare const useAuth: () => {
290
297
  username: string | null;
291
298
  display_name: string;
292
299
  avatar_url: string | null;
300
+ follower_count: number;
293
301
  phone_number: string | null;
294
302
  email_verified_at: string | null;
295
303
  phone_verified_at: string | null;
@@ -329,6 +337,7 @@ export declare const useAuth: () => {
329
337
  username: string | null;
330
338
  display_name: string;
331
339
  avatar_url: string | null;
340
+ follower_count: number;
332
341
  phone_number: string | null;
333
342
  email_verified_at: string | null;
334
343
  phone_verified_at: string | null;
@@ -365,6 +374,7 @@ export declare const useAuth: () => {
365
374
  username: string | null;
366
375
  display_name: string;
367
376
  avatar_url: string | null;
377
+ follower_count: number;
368
378
  phone_number: string | null;
369
379
  email_verified_at: string | null;
370
380
  phone_verified_at: string | null;
@@ -401,6 +411,7 @@ export declare const useAuth: () => {
401
411
  username: string | null;
402
412
  display_name: string;
403
413
  avatar_url: string | null;
414
+ follower_count: number;
404
415
  phone_number: string | null;
405
416
  email_verified_at: string | null;
406
417
  phone_verified_at: string | null;
@@ -443,6 +454,7 @@ export declare const useLogin: () => import("@tanstack/react-query").UseMutation
443
454
  username: string | null;
444
455
  display_name: string;
445
456
  avatar_url: string | null;
457
+ follower_count: number;
446
458
  phone_number: string | null;
447
459
  email_verified_at: string | null;
448
460
  phone_verified_at: string | null;
@@ -483,6 +495,7 @@ export declare const useLoginAnonymous: () => import("@tanstack/react-query").Us
483
495
  username: string | null;
484
496
  display_name: string;
485
497
  avatar_url: string | null;
498
+ follower_count: number;
486
499
  phone_number: string | null;
487
500
  email_verified_at: string | null;
488
501
  phone_verified_at: string | null;
@@ -519,6 +532,7 @@ export declare const useUpgradeAnonymous: () => import("@tanstack/react-query").
519
532
  username: string | null;
520
533
  display_name: string;
521
534
  avatar_url: string | null;
535
+ follower_count: number;
522
536
  phone_number: string | null;
523
537
  email_verified_at: string | null;
524
538
  phone_verified_at: string | null;
@@ -0,0 +1,38 @@
1
+ import { PlaceSearchQuery } from '@ph-cms/api-contract';
2
+ export declare const placeKeys: {
3
+ all: readonly ["places"];
4
+ search: (params: PlaceSearchQuery) => readonly ["places", "search", {
5
+ keyword: string;
6
+ lat?: number | undefined;
7
+ lng?: number | undefined;
8
+ }];
9
+ };
10
+ export declare const usePlaceSearch: (params: PlaceSearchQuery, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<{
11
+ items: {
12
+ id: string;
13
+ provider: "google" | "naver";
14
+ name: string;
15
+ address: string;
16
+ location: {
17
+ lat: number;
18
+ lng: number;
19
+ };
20
+ category: string;
21
+ phoneNumber: string | null;
22
+ link: string | null;
23
+ photos: {
24
+ url: string;
25
+ width: number | null;
26
+ height: number | null;
27
+ attribution: string | null;
28
+ }[];
29
+ reviews: {
30
+ date: string | null;
31
+ title: string;
32
+ author: string | null;
33
+ link: string;
34
+ snippet: string;
35
+ }[];
36
+ }[];
37
+ keyword: string;
38
+ }, Error>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePlaceSearch = exports.placeKeys = void 0;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ const context_1 = require("../context");
6
+ exports.placeKeys = {
7
+ all: ['places'],
8
+ search: (params) => [...exports.placeKeys.all, 'search', params],
9
+ };
10
+ const usePlaceSearch = (params, enabled = true) => {
11
+ const client = (0, context_1.usePHCMS)();
12
+ return (0, react_query_1.useQuery)({
13
+ queryKey: exports.placeKeys.search(params),
14
+ queryFn: () => client.place.search(params),
15
+ staleTime: 1000 * 60 * 5,
16
+ enabled: enabled && !!params.keyword,
17
+ });
18
+ };
19
+ exports.usePlaceSearch = usePlaceSearch;
@@ -3,6 +3,11 @@ export declare const termsKeys: {
3
3
  all: readonly ["terms"];
4
4
  lists: () => readonly ["terms", "list"];
5
5
  };
6
+ export declare const userKeys: {
7
+ all: readonly ["users"];
8
+ profile: (uid: string) => readonly ["users", "profile", string];
9
+ followStatus: (uid: string) => readonly ["users", "follow-status", string];
10
+ };
6
11
  /**
7
12
  * Access the current user profile and status.
8
13
  */
@@ -14,6 +19,7 @@ export declare const useUser: () => {
14
19
  username: string | null;
15
20
  display_name: string;
16
21
  avatar_url: string | null;
22
+ follower_count: number;
17
23
  phone_number: string | null;
18
24
  email_verified_at: string | null;
19
25
  phone_verified_at: string | null;
@@ -51,8 +57,15 @@ export declare const useUserProfile: (uid: string) => import("@tanstack/react-qu
51
57
  username: string | null;
52
58
  display_name: string;
53
59
  avatar_url: string | null;
60
+ follower_count: number;
54
61
  profile_data: Record<string, any>;
55
62
  }, Error>;
63
+ /**
64
+ * Hook to fetch follow status for a target user.
65
+ */
66
+ export declare const useFollowStatus: (uid: string, enabled?: boolean) => import("@tanstack/react-query").UseQueryResult<{
67
+ is_following: boolean;
68
+ }, Error>;
56
69
  /**
57
70
  * Hook to update a user's profile.
58
71
  * Typically used by a user to update their own profile.
@@ -64,6 +77,7 @@ export declare const useUpdateProfile: () => import("@tanstack/react-query").Use
64
77
  username: string | null;
65
78
  display_name: string;
66
79
  avatar_url: string | null;
80
+ follower_count: number;
67
81
  phone_number: string | null;
68
82
  email_verified_at: string | null;
69
83
  phone_verified_at: string | null;
@@ -92,6 +106,22 @@ export declare const useUpdateProfile: () => import("@tanstack/react-query").Use
92
106
  uid: string;
93
107
  data: UpdateUserProfileRequest | Record<string, any>;
94
108
  }, unknown>;
109
+ /**
110
+ * Hook to follow a user.
111
+ */
112
+ export declare const useFollowUser: () => import("@tanstack/react-query").UseMutationResult<{
113
+ message: string;
114
+ is_following: boolean;
115
+ success: boolean;
116
+ }, Error, string, unknown>;
117
+ /**
118
+ * Hook to unfollow a user.
119
+ */
120
+ export declare const useUnfollowUser: () => import("@tanstack/react-query").UseMutationResult<{
121
+ message: string;
122
+ is_following: boolean;
123
+ success: boolean;
124
+ }, Error, string, unknown>;
95
125
  /**
96
126
  * Hook to fetch contents liked by a specific user.
97
127
  */
@@ -1,12 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useUnsuspendUser = exports.useSuspendUser = exports.useBlockedUsers = exports.useUnblockUser = exports.useBlockUser = exports.useAgreeTerms = exports.useChannelTerms = exports.useLikedStats = exports.useLikedContents = exports.useUpdateProfile = exports.useUserProfile = exports.useUser = exports.termsKeys = void 0;
3
+ exports.useUnsuspendUser = exports.useSuspendUser = exports.useBlockedUsers = exports.useUnblockUser = exports.useBlockUser = exports.useAgreeTerms = exports.useChannelTerms = exports.useLikedStats = exports.useLikedContents = exports.useUnfollowUser = exports.useFollowUser = exports.useUpdateProfile = exports.useFollowStatus = exports.useUserProfile = exports.useUser = exports.userKeys = exports.termsKeys = void 0;
4
4
  const react_query_1 = require("@tanstack/react-query");
5
5
  const context_1 = require("../context");
6
6
  exports.termsKeys = {
7
7
  all: ['terms'],
8
8
  lists: () => [...exports.termsKeys.all, 'list'],
9
9
  };
10
+ exports.userKeys = {
11
+ all: ['users'],
12
+ profile: (uid) => [...exports.userKeys.all, 'profile', uid],
13
+ followStatus: (uid) => [...exports.userKeys.all, 'follow-status', uid],
14
+ };
10
15
  /**
11
16
  * Access the current user profile and status.
12
17
  */
@@ -21,12 +26,24 @@ exports.useUser = useUser;
21
26
  const useUserProfile = (uid) => {
22
27
  const client = (0, context_1.usePHCMS)();
23
28
  return (0, react_query_1.useQuery)({
24
- queryKey: ['user-profile', uid],
29
+ queryKey: exports.userKeys.profile(uid),
25
30
  queryFn: () => client.user.getProfile(uid),
26
31
  enabled: !!uid,
27
32
  });
28
33
  };
29
34
  exports.useUserProfile = useUserProfile;
35
+ /**
36
+ * Hook to fetch follow status for a target user.
37
+ */
38
+ const useFollowStatus = (uid, enabled = true) => {
39
+ const client = (0, context_1.usePHCMS)();
40
+ return (0, react_query_1.useQuery)({
41
+ queryKey: exports.userKeys.followStatus(uid),
42
+ queryFn: () => client.user.getFollowStatus(uid),
43
+ enabled: !!uid && enabled,
44
+ });
45
+ };
46
+ exports.useFollowStatus = useFollowStatus;
30
47
  /**
31
48
  * Hook to update a user's profile.
32
49
  * Typically used by a user to update their own profile.
@@ -43,6 +60,36 @@ const useUpdateProfile = () => {
43
60
  });
44
61
  };
45
62
  exports.useUpdateProfile = useUpdateProfile;
63
+ /**
64
+ * Hook to follow a user.
65
+ */
66
+ const useFollowUser = () => {
67
+ const client = (0, context_1.usePHCMS)();
68
+ const queryClient = (0, react_query_1.useQueryClient)();
69
+ return (0, react_query_1.useMutation)({
70
+ mutationFn: (uid) => client.user.follow(uid),
71
+ onSuccess: (_data, uid) => {
72
+ queryClient.invalidateQueries({ queryKey: exports.userKeys.followStatus(uid) });
73
+ queryClient.invalidateQueries({ queryKey: exports.userKeys.profile(uid) });
74
+ },
75
+ });
76
+ };
77
+ exports.useFollowUser = useFollowUser;
78
+ /**
79
+ * Hook to unfollow a user.
80
+ */
81
+ const useUnfollowUser = () => {
82
+ const client = (0, context_1.usePHCMS)();
83
+ const queryClient = (0, react_query_1.useQueryClient)();
84
+ return (0, react_query_1.useMutation)({
85
+ mutationFn: (uid) => client.user.unfollow(uid),
86
+ onSuccess: (_data, uid) => {
87
+ queryClient.invalidateQueries({ queryKey: exports.userKeys.followStatus(uid) });
88
+ queryClient.invalidateQueries({ queryKey: exports.userKeys.profile(uid) });
89
+ },
90
+ });
91
+ };
92
+ exports.useUnfollowUser = useUnfollowUser;
46
93
  /**
47
94
  * Hook to fetch contents liked by a specific user.
48
95
  */
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export * from './modules/auth';
10
10
  export * from './modules/channel';
11
11
  export * from './modules/content';
12
12
  export * from './modules/media';
13
+ export * from './modules/place';
13
14
  export * from './modules/terms';
14
15
  export * from './modules/user';
15
16
  export * from './context';
@@ -18,5 +19,6 @@ export * from './hooks/useContent';
18
19
  export * from './hooks/useStampTour';
19
20
  export * from './hooks/useFirebaseAuthSync';
20
21
  export * from './hooks/useMedia';
22
+ export * from './hooks/usePlace';
21
23
  export * from './hooks/useUser';
22
24
  export * from './types';
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ __exportStar(require("./modules/auth"), exports);
26
26
  __exportStar(require("./modules/channel"), exports);
27
27
  __exportStar(require("./modules/content"), exports);
28
28
  __exportStar(require("./modules/media"), exports);
29
+ __exportStar(require("./modules/place"), exports);
29
30
  __exportStar(require("./modules/terms"), exports);
30
31
  __exportStar(require("./modules/user"), exports);
31
32
  __exportStar(require("./context"), exports);
@@ -34,5 +35,6 @@ __exportStar(require("./hooks/useContent"), exports);
34
35
  __exportStar(require("./hooks/useStampTour"), exports);
35
36
  __exportStar(require("./hooks/useFirebaseAuthSync"), exports);
36
37
  __exportStar(require("./hooks/useMedia"), exports);
38
+ __exportStar(require("./hooks/usePlace"), exports);
37
39
  __exportStar(require("./hooks/useUser"), exports);
38
40
  __exportStar(require("./types"), exports);
@@ -0,0 +1,8 @@
1
+ import { PlaceSearchQuery, PlaceSearchResponse } from "@ph-cms/api-contract";
2
+ import { AxiosInstance } from "axios";
3
+ export declare class PlaceModule {
4
+ private client;
5
+ private prefix;
6
+ constructor(client: AxiosInstance, prefix?: string);
7
+ search(params: PlaceSearchQuery): Promise<PlaceSearchResponse>;
8
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlaceModule = void 0;
4
+ const api_contract_1 = require("@ph-cms/api-contract");
5
+ const errors_1 = require("../errors");
6
+ class PlaceModule {
7
+ constructor(client, prefix = '/api') {
8
+ this.client = client;
9
+ this.prefix = prefix;
10
+ }
11
+ async search(params) {
12
+ const validation = api_contract_1.PlaceSearchQuerySchema.safeParse(params);
13
+ if (!validation.success) {
14
+ throw new errors_1.ValidationError("Invalid place search params", validation.error.errors);
15
+ }
16
+ return this.client.get(`${this.prefix}/places/search`, {
17
+ params,
18
+ });
19
+ }
20
+ }
21
+ exports.PlaceModule = PlaceModule;
@@ -1,4 +1,4 @@
1
- import { LikedStatsByTypeResponse, ListLikedContentQuery, PagedContentListResponse, UpdateUserProfileRequest, UserDto, UserProfileDto, BlockUserRequest, BlockUserResponse, PagedBlockedUserListResponse, SuspendUserRequest, SuspendUserResponse } from "@ph-cms/api-contract";
1
+ import { LikedStatsByTypeResponse, ListLikedContentQuery, PagedContentListResponse, UpdateUserProfileRequest, UserDto, UserProfileDto, FollowStatusResponse, FollowUserResponse, BlockUserRequest, BlockUserResponse, PagedBlockedUserListResponse, SuspendUserRequest, SuspendUserResponse } from "@ph-cms/api-contract";
2
2
  import { AxiosInstance } from "axios";
3
3
  export declare class UserModule {
4
4
  private client;
@@ -10,6 +10,24 @@ export declare class UserModule {
10
10
  * @param uid - The UID of the user.
11
11
  */
12
12
  getProfile(uid: string): Promise<UserProfileDto>;
13
+ /**
14
+ * Retrieves whether the current authenticated user follows the target user.
15
+ *
16
+ * @param uid - The UID of the target user.
17
+ */
18
+ getFollowStatus(uid: string): Promise<FollowStatusResponse>;
19
+ /**
20
+ * Follows a user.
21
+ *
22
+ * @param uid - The UID of the target user.
23
+ */
24
+ follow(uid: string): Promise<FollowUserResponse>;
25
+ /**
26
+ * Unfollows a user.
27
+ *
28
+ * @param uid - The UID of the target user.
29
+ */
30
+ unfollow(uid: string): Promise<FollowUserResponse>;
13
31
  /**
14
32
  * Updates a user's profile.
15
33
  *
@@ -16,6 +16,36 @@ class UserModule {
16
16
  async getProfile(uid) {
17
17
  return this.client.get(`${this.prefix}/users/${uid}/profile`);
18
18
  }
19
+ /**
20
+ * Retrieves whether the current authenticated user follows the target user.
21
+ *
22
+ * @param uid - The UID of the target user.
23
+ */
24
+ async getFollowStatus(uid) {
25
+ if (!uid)
26
+ throw new errors_1.ValidationError("UID is required", []);
27
+ return this.client.get(`${this.prefix}/users/${uid}/follow-status`);
28
+ }
29
+ /**
30
+ * Follows a user.
31
+ *
32
+ * @param uid - The UID of the target user.
33
+ */
34
+ async follow(uid) {
35
+ if (!uid)
36
+ throw new errors_1.ValidationError("UID is required", []);
37
+ return this.client.post(`${this.prefix}/users/${uid}/follow`);
38
+ }
39
+ /**
40
+ * Unfollows a user.
41
+ *
42
+ * @param uid - The UID of the target user.
43
+ */
44
+ async unfollow(uid) {
45
+ if (!uid)
46
+ throw new errors_1.ValidationError("UID is required", []);
47
+ return this.client.delete(`${this.prefix}/users/${uid}/follow`);
48
+ }
19
49
  /**
20
50
  * Updates a user's profile.
21
51
  *
package/dist/types.d.ts CHANGED
@@ -5,7 +5,7 @@ export type { AuthStatus, PHCMSContextType, PHCMSProviderProps } from './context
5
5
  export type { FirebaseAuthSyncProps, UseFirebaseAuthSyncOptions, UseFirebaseAuthSyncReturn } from './hooks/useFirebaseAuthSync';
6
6
  export type { StampAvailability, CheckStampAvailabilityParams } from './hooks/useStampTour';
7
7
  export type { AnonymousLoginRequest, AuthResponse, FirebaseExchangeRequest, FirebaseRegisterRequest, LoginRequest, RefreshTokenRequest, RegisterRequest, } from '@ph-cms/api-contract';
8
- export type { UserDto, UserProfileDto } from '@ph-cms/api-contract';
8
+ export type { UserDto, UserProfileDto, FollowStatusResponse, FollowUserResponse, } from '@ph-cms/api-contract';
9
9
  export type { ChannelDto, CheckHierarchyQuery } from '@ph-cms/api-contract';
10
10
  export type { ContentDto, ContentStatDto, ContentMediaDto, ContentStatus, CreateContentRequest, ListContentQuery, PagedContentListResponse, UpdateContentRequest, } from '@ph-cms/api-contract';
11
11
  export type { HierarchySetDto } from '@ph-cms/api-contract';
@@ -13,4 +13,5 @@ export type { PermissionPolicySetDto } from '@ph-cms/api-contract';
13
13
  export type { TermDto } from '@ph-cms/api-contract';
14
14
  export type { GeoJSON } from '@ph-cms/api-contract';
15
15
  export type { MediaUploadTicketBatchRequest, MediaUploadTicketBatchResponse, MediaUploadTicketRequest, MediaUploadTicketResponse } from '@ph-cms/api-contract';
16
+ export type { IntegratedPlace, PlaceSearchQuery, PlaceSearchResponse, } from '@ph-cms/api-contract';
16
17
  export type { PagedResponse } from '@ph-cms/api-contract';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ph-cms/client-sdk",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "description": "Unified PH-CMS Client SDK (React + Core)",
5
5
  "keywords": [],
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "LICENSE"
31
31
  ],
32
32
  "dependencies": {
33
- "@ph-cms/api-contract": "^0.1.20",
33
+ "@ph-cms/api-contract": "^0.1.22",
34
34
  "@tanstack/react-query": "^5.0.0",
35
35
  "axios": "^1.6.0",
36
36
  "zod": "^3.22.4"