@comergehq/studio 0.1.2 → 0.1.3

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.
Files changed (172) hide show
  1. package/dist/index.js +255 -245
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +213 -203
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +8 -5
  6. package/src/components/chat/ChatComposer.tsx +277 -0
  7. package/src/components/chat/ChatHeader.tsx +31 -0
  8. package/src/components/chat/ChatMessageBubble.tsx +69 -0
  9. package/src/components/chat/ChatMessageList.tsx +137 -0
  10. package/src/components/chat/ChatPage.tsx +69 -0
  11. package/src/components/chat/ForkNoticeBanner.tsx +66 -0
  12. package/src/components/chat/MultilineTextInput.tsx +46 -0
  13. package/src/components/chat/ScrollToBottomButton.tsx +78 -0
  14. package/src/components/chat/TypingIndicator.tsx +54 -0
  15. package/src/components/chat/index.ts +28 -0
  16. package/src/components/comments/AppCommentsSheet.tsx +213 -0
  17. package/src/components/comments/CommentRow.tsx +63 -0
  18. package/src/components/comments/formatTimeAgo.ts +3 -0
  19. package/src/components/comments/index.ts +3 -0
  20. package/src/components/comments/useAppComments.ts +74 -0
  21. package/src/components/comments/useAppDetails.ts +35 -0
  22. package/src/components/comments/useIosKeyboardSnapFix.ts +24 -0
  23. package/src/components/dialogs/ConfirmMergeRequestDialog.tsx +156 -0
  24. package/src/components/dialogs/index.ts +4 -0
  25. package/src/components/draw/DrawColorPicker.tsx +77 -0
  26. package/src/components/draw/DrawModeOverlay.tsx +144 -0
  27. package/src/components/draw/DrawSurface.tsx +127 -0
  28. package/src/components/draw/DrawToolbar.tsx +253 -0
  29. package/src/components/draw/index.ts +15 -0
  30. package/src/components/draw/optionalHaptics.ts +15 -0
  31. package/src/components/draw/strokes.ts +21 -0
  32. package/src/components/draw/types.ts +9 -0
  33. package/src/components/floating-draggable-button/FloatingDraggableButton.tsx +323 -0
  34. package/src/components/floating-draggable-button/constants.ts +17 -0
  35. package/src/components/floating-draggable-button/index.ts +4 -0
  36. package/src/components/floating-draggable-button/types.ts +63 -0
  37. package/src/components/icons/MergeIcon.tsx +14 -0
  38. package/src/components/icons/StudioIcons.tsx +66 -0
  39. package/src/components/index.ts +17 -0
  40. package/src/components/merge-requests/MergeRequestStatusCard.tsx +179 -0
  41. package/src/components/merge-requests/ReviewMergeRequestActionButton.tsx +62 -0
  42. package/src/components/merge-requests/ReviewMergeRequestCard.tsx +192 -0
  43. package/src/components/merge-requests/ReviewMergeRequestCarousel.tsx +132 -0
  44. package/src/components/merge-requests/index.ts +7 -0
  45. package/src/components/merge-requests/mergeRequestStatusDisplay.ts +23 -0
  46. package/src/components/merge-requests/toIsoString.ts +9 -0
  47. package/src/components/merge-requests/useControlledExpansion.ts +16 -0
  48. package/src/components/models/index.ts +9 -0
  49. package/src/components/models/types.ts +43 -0
  50. package/src/components/overlays/EdgeGlowFrame.tsx +105 -0
  51. package/src/components/overlays/index.ts +4 -0
  52. package/src/components/preview/PreviewHeroCard.tsx +58 -0
  53. package/src/components/preview/PreviewImage.tsx +22 -0
  54. package/src/components/preview/PreviewMetaRow.tsx +70 -0
  55. package/src/components/preview/PreviewPage.tsx +36 -0
  56. package/src/components/preview/PreviewPlaceholder.tsx +72 -0
  57. package/src/components/preview/PreviewStatusBadge.tsx +63 -0
  58. package/src/components/preview/StatsBar.tsx +109 -0
  59. package/src/components/preview/index.ts +22 -0
  60. package/src/components/primitives/Avatar.tsx +68 -0
  61. package/src/components/primitives/Button.tsx +102 -0
  62. package/src/components/primitives/Card.tsx +30 -0
  63. package/src/components/primitives/Divider.tsx +17 -0
  64. package/src/components/primitives/Icon.tsx +40 -0
  65. package/src/components/primitives/MarkdownText.tsx +72 -0
  66. package/src/components/primitives/Modal.tsx +53 -0
  67. package/src/components/primitives/Surface.tsx +42 -0
  68. package/src/components/primitives/Text.tsx +83 -0
  69. package/src/components/primitives/index.ts +35 -0
  70. package/src/components/primitives/types.ts +30 -0
  71. package/src/components/studio-sheet/StudioBottomSheet.tsx +114 -0
  72. package/src/components/studio-sheet/StudioSheetBackground.tsx +63 -0
  73. package/src/components/studio-sheet/StudioSheetHeader.tsx +35 -0
  74. package/src/components/studio-sheet/StudioSheetHeaderIconButton.tsx +109 -0
  75. package/src/components/studio-sheet/StudioSheetPager.tsx +66 -0
  76. package/src/components/studio-sheet/index.ts +18 -0
  77. package/src/components/studio-sheet/types.ts +5 -0
  78. package/src/components/utils/color.ts +25 -0
  79. package/src/components/utils/formatTimeAgo.ts +19 -0
  80. package/src/core/logger.ts +42 -0
  81. package/src/core/services/http/baseUrl.ts +3 -0
  82. package/src/core/services/http/index.ts +128 -0
  83. package/src/core/services/http/public.ts +14 -0
  84. package/src/core/services/supabase/auth.ts +41 -0
  85. package/src/core/services/supabase/client.ts +43 -0
  86. package/src/core/services/supabase/index.ts +7 -0
  87. package/src/data/agent/remote.ts +30 -0
  88. package/src/data/agent/repository.ts +34 -0
  89. package/src/data/agent/types.ts +28 -0
  90. package/src/data/apps/bundles/remote.ts +47 -0
  91. package/src/data/apps/bundles/repository.ts +35 -0
  92. package/src/data/apps/bundles/types.ts +27 -0
  93. package/src/data/apps/images/remote.ts +61 -0
  94. package/src/data/apps/images/repository.ts +47 -0
  95. package/src/data/apps/remote.ts +97 -0
  96. package/src/data/apps/repository.ts +185 -0
  97. package/src/data/apps/types.ts +206 -0
  98. package/src/data/attachment/remote.ts +32 -0
  99. package/src/data/attachment/repository.ts +40 -0
  100. package/src/data/attachment/types.ts +42 -0
  101. package/src/data/base-remote.ts +3 -0
  102. package/src/data/base-repository.ts +11 -0
  103. package/src/data/comments/likes/remote.ts +87 -0
  104. package/src/data/comments/likes/repository.ts +61 -0
  105. package/src/data/comments/likes/types.ts +47 -0
  106. package/src/data/comments/remote.ts +71 -0
  107. package/src/data/comments/repository.ts +53 -0
  108. package/src/data/comments/types.ts +60 -0
  109. package/src/data/github/remote.ts +23 -0
  110. package/src/data/github/repository.ts +35 -0
  111. package/src/data/github/types.ts +23 -0
  112. package/src/data/home/remote.ts +24 -0
  113. package/src/data/home/repository.ts +28 -0
  114. package/src/data/home/types.ts +70 -0
  115. package/src/data/index.ts +3 -0
  116. package/src/data/likes/remote.ts +57 -0
  117. package/src/data/likes/repository.ts +47 -0
  118. package/src/data/likes/types.ts +46 -0
  119. package/src/data/me/remote.ts +28 -0
  120. package/src/data/me/repository.ts +30 -0
  121. package/src/data/me/types.ts +14 -0
  122. package/src/data/merge-requests/remote.ts +76 -0
  123. package/src/data/merge-requests/repository.ts +66 -0
  124. package/src/data/merge-requests/types.ts +33 -0
  125. package/src/data/messages/remote.ts +21 -0
  126. package/src/data/messages/repository.ts +104 -0
  127. package/src/data/messages/types.ts +20 -0
  128. package/src/data/public/studio-config/remote.ts +19 -0
  129. package/src/data/public/studio-config/repository.ts +23 -0
  130. package/src/data/public/studio-config/types.ts +6 -0
  131. package/src/data/ratings/remote.ts +76 -0
  132. package/src/data/ratings/repository.ts +63 -0
  133. package/src/data/ratings/types.ts +57 -0
  134. package/src/data/threads/remote.ts +40 -0
  135. package/src/data/threads/repository.ts +41 -0
  136. package/src/data/threads/types.ts +25 -0
  137. package/src/data/types.ts +8 -0
  138. package/src/data/users/remote.ts +31 -0
  139. package/src/data/users/repository.ts +45 -0
  140. package/src/data/users/types.ts +15 -0
  141. package/src/index.ts +6 -0
  142. package/src/studio/ComergeStudio.tsx +246 -0
  143. package/src/studio/bootstrap/StudioBootstrap.tsx +45 -0
  144. package/src/studio/bootstrap/useStudioBootstrap.ts +51 -0
  145. package/src/studio/hooks/useApp.ts +83 -0
  146. package/src/studio/hooks/useAppStats.ts +111 -0
  147. package/src/studio/hooks/useAttachmentUpload.ts +59 -0
  148. package/src/studio/hooks/useBundleManager.ts +389 -0
  149. package/src/studio/hooks/useMergeRequests.ts +173 -0
  150. package/src/studio/hooks/useStudioActions.ts +96 -0
  151. package/src/studio/hooks/useThreadMessages.ts +85 -0
  152. package/src/studio/lib/chat.ts +34 -0
  153. package/src/studio/ui/ChatPanel.tsx +154 -0
  154. package/src/studio/ui/ConfirmMergeFlow.tsx +55 -0
  155. package/src/studio/ui/PreviewPanel.tsx +131 -0
  156. package/src/studio/ui/RuntimeRenderer.tsx +40 -0
  157. package/src/studio/ui/StudioOverlay.tsx +257 -0
  158. package/src/studio/ui/preview-panel/PressableCardRow.tsx +49 -0
  159. package/src/studio/ui/preview-panel/PreviewCollaborateSection.tsx +174 -0
  160. package/src/studio/ui/preview-panel/PreviewCustomizeSection.tsx +160 -0
  161. package/src/studio/ui/preview-panel/PreviewHeroSection.tsx +56 -0
  162. package/src/studio/ui/preview-panel/PreviewMetaSection.tsx +67 -0
  163. package/src/studio/ui/preview-panel/PreviewPanelHeader.tsx +48 -0
  164. package/src/studio/ui/preview-panel/SectionTitle.tsx +31 -0
  165. package/src/studio/ui/preview-panel/usePreviewPanelData.ts +132 -0
  166. package/src/studio/ui/preview-panel/utils.ts +29 -0
  167. package/src/theme/index.ts +5 -0
  168. package/src/theme/tokens.ts +118 -0
  169. package/src/theme/types.ts +90 -0
  170. package/src/theme/useTheme.ts +11 -0
  171. package/dist/assets/images/merge.svg +0 -3
  172. package/dist/merge-72UG27QV.svg +0 -3
@@ -0,0 +1,47 @@
1
+ export type AppCommentLike = {
2
+ id: string;
3
+ appId: string;
4
+ commentId: string;
5
+ userId: string;
6
+ source: string | null;
7
+ context: Record<string, unknown>;
8
+ createdAt: string;
9
+ updatedAt: string;
10
+ };
11
+
12
+ export type ListAppCommentLikesQuery = {
13
+ page?: number;
14
+ pageSize?: number;
15
+ userId?: string;
16
+ };
17
+
18
+ export type CreateAppCommentLikeInput = {
19
+ source?: string;
20
+ context?: Record<string, unknown>;
21
+ };
22
+
23
+ export type AppCommentLikeStats = {
24
+ total?: number;
25
+ };
26
+
27
+ export type AppCommentLikeList = {
28
+ items: AppCommentLike[];
29
+ pageInfo: {
30
+ page: number;
31
+ pageSize: number;
32
+ total: number;
33
+ hasMore: boolean;
34
+ };
35
+ stats?: AppCommentLikeStats;
36
+ };
37
+
38
+ export type AppCommentLikeMutationResult = {
39
+ like: AppCommentLike;
40
+ stats?: AppCommentLikeStats;
41
+ };
42
+
43
+ export type AppCommentLikeStatsResult = {
44
+ stats?: AppCommentLikeStats;
45
+ };
46
+
47
+
@@ -0,0 +1,71 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import { BaseRemote } from '../base-remote';
4
+ import type {
5
+ AppComment,
6
+ AppCommentList,
7
+ CreateAppCommentInput,
8
+ ListAppCommentsQuery,
9
+ UpdateAppCommentInput,
10
+ } from './types';
11
+
12
+ export interface AppCommentsRemoteDataSource {
13
+ list(appId: string, query?: ListAppCommentsQuery): Promise<ServiceResponse<AppCommentList>>;
14
+ getById(appId: string, commentId: string): Promise<ServiceResponse<AppComment>>;
15
+ create(appId: string, payload: CreateAppCommentInput): Promise<ServiceResponse<AppComment>>;
16
+ update(
17
+ appId: string,
18
+ commentId: string,
19
+ payload: UpdateAppCommentInput
20
+ ): Promise<ServiceResponse<AppComment>>;
21
+ remove(appId: string, commentId: string): Promise<ServiceResponse<AppComment>>;
22
+ }
23
+
24
+ class AppCommentsRemoteDataSourceImpl extends BaseRemote implements AppCommentsRemoteDataSource {
25
+ async list(appId: string, query?: ListAppCommentsQuery): Promise<ServiceResponse<AppCommentList>> {
26
+ const params = query ? { ...query } : undefined;
27
+ const { data } = await api.get<ServiceResponse<AppCommentList>>(
28
+ `/v1/apps/${encodeURIComponent(appId)}/comments`,
29
+ { params }
30
+ );
31
+ return data;
32
+ }
33
+
34
+ async getById(appId: string, commentId: string): Promise<ServiceResponse<AppComment>> {
35
+ const { data } = await api.get<ServiceResponse<AppComment>>(
36
+ `/v1/apps/${encodeURIComponent(appId)}/comments/${encodeURIComponent(commentId)}`
37
+ );
38
+ return data;
39
+ }
40
+
41
+ async create(appId: string, payload: CreateAppCommentInput): Promise<ServiceResponse<AppComment>> {
42
+ const { data } = await api.post<ServiceResponse<AppComment>>(
43
+ `/v1/apps/${encodeURIComponent(appId)}/comments`,
44
+ payload
45
+ );
46
+ return data;
47
+ }
48
+
49
+ async update(
50
+ appId: string,
51
+ commentId: string,
52
+ payload: UpdateAppCommentInput
53
+ ): Promise<ServiceResponse<AppComment>> {
54
+ const { data } = await api.patch<ServiceResponse<AppComment>>(
55
+ `/v1/apps/${encodeURIComponent(appId)}/comments/${encodeURIComponent(commentId)}`,
56
+ payload
57
+ );
58
+ return data;
59
+ }
60
+
61
+ async remove(appId: string, commentId: string): Promise<ServiceResponse<AppComment>> {
62
+ const { data } = await api.delete<ServiceResponse<AppComment>>(
63
+ `/v1/apps/${encodeURIComponent(appId)}/comments/${encodeURIComponent(commentId)}`
64
+ );
65
+ return data;
66
+ }
67
+ }
68
+
69
+ export const appCommentsRemoteDataSource: AppCommentsRemoteDataSource = new AppCommentsRemoteDataSourceImpl();
70
+
71
+
@@ -0,0 +1,53 @@
1
+ import type { AppCommentsRemoteDataSource } from './remote';
2
+ import { appCommentsRemoteDataSource } from './remote';
3
+ import type {
4
+ AppComment,
5
+ AppCommentList,
6
+ CreateAppCommentInput,
7
+ ListAppCommentsQuery,
8
+ UpdateAppCommentInput,
9
+ } from './types';
10
+ import { BaseRepository } from '../../data/base-repository';
11
+
12
+ export interface AppCommentsRepository {
13
+ list(appId: string, query?: ListAppCommentsQuery): Promise<AppCommentList>;
14
+ getById(appId: string, commentId: string): Promise<AppComment>;
15
+ create(appId: string, payload: CreateAppCommentInput): Promise<AppComment>;
16
+ update(appId: string, commentId: string, payload: UpdateAppCommentInput): Promise<AppComment>;
17
+ remove(appId: string, commentId: string): Promise<AppComment>;
18
+ }
19
+
20
+ class AppCommentsRepositoryImpl extends BaseRepository implements AppCommentsRepository {
21
+ constructor(private readonly remote: AppCommentsRemoteDataSource) {
22
+ super();
23
+ }
24
+
25
+ async list(appId: string, query?: ListAppCommentsQuery): Promise<AppCommentList> {
26
+ const res = await this.remote.list(appId, query);
27
+ return this.unwrapOrThrow(res);
28
+ }
29
+
30
+ async getById(appId: string, commentId: string): Promise<AppComment> {
31
+ const res = await this.remote.getById(appId, commentId);
32
+ return this.unwrapOrThrow(res);
33
+ }
34
+
35
+ async create(appId: string, payload: CreateAppCommentInput): Promise<AppComment> {
36
+ const res = await this.remote.create(appId, payload);
37
+ return this.unwrapOrThrow(res);
38
+ }
39
+
40
+ async update(appId: string, commentId: string, payload: UpdateAppCommentInput): Promise<AppComment> {
41
+ const res = await this.remote.update(appId, commentId, payload);
42
+ return this.unwrapOrThrow(res);
43
+ }
44
+
45
+ async remove(appId: string, commentId: string): Promise<AppComment> {
46
+ const res = await this.remote.remove(appId, commentId);
47
+ return this.unwrapOrThrow(res);
48
+ }
49
+ }
50
+
51
+ export const appCommentsRepository: AppCommentsRepository = new AppCommentsRepositoryImpl(appCommentsRemoteDataSource);
52
+
53
+
@@ -0,0 +1,60 @@
1
+ export type CommentType = 'general' | 'review' | 'bug' | 'question' | 'idea' | 'other';
2
+
3
+ export type CommentMediaItem = {
4
+ id?: string;
5
+ url: string;
6
+ thumbnailUrl?: string;
7
+ type: 'image' | 'video' | 'file' | 'other';
8
+ alt?: string;
9
+ metadata?: Record<string, unknown>;
10
+ };
11
+
12
+ export type AppComment = {
13
+ id: string;
14
+ appId: string;
15
+ authorId: string;
16
+ commentType: CommentType;
17
+ description: string | null;
18
+ body: string | null;
19
+ bodyRich: Record<string, unknown> | null;
20
+ media: CommentMediaItem[];
21
+ metadata: Record<string, unknown>;
22
+ parentCommentId: string | null;
23
+ isDeleted: boolean;
24
+ deletedAt: string | null;
25
+ deletedBy: string | null;
26
+ lastEditedBy: string | null;
27
+ createdAt: string;
28
+ updatedAt: string;
29
+ };
30
+
31
+ export type CreateAppCommentInput = {
32
+ commentType?: CommentType;
33
+ description?: string | null;
34
+ body?: string | null;
35
+ bodyRich?: Record<string, unknown> | null;
36
+ media?: CommentMediaItem[];
37
+ metadata?: Record<string, unknown>;
38
+ parentCommentId?: string | null;
39
+ };
40
+
41
+ export type UpdateAppCommentInput = Partial<CreateAppCommentInput>;
42
+
43
+ export type ListAppCommentsQuery = {
44
+ page?: number;
45
+ pageSize?: number;
46
+ parentCommentId?: string | null;
47
+ includeDeleted?: boolean;
48
+ };
49
+
50
+ export type AppCommentList = {
51
+ items: AppComment[];
52
+ pageInfo: {
53
+ page: number;
54
+ pageSize: number;
55
+ total: number;
56
+ hasMore: boolean;
57
+ };
58
+ };
59
+
60
+
@@ -0,0 +1,23 @@
1
+ import { api } from '../../core/services/http';
2
+ import { BaseRemote } from '../base-remote';
3
+ import type { ServiceResponse } from '../types';
4
+ import type { GithubOAuthStart, GithubReposResponse } from './types';
5
+
6
+ export interface GithubRemoteDataSource {
7
+ getOAuthUrl(): Promise<ServiceResponse<GithubOAuthStart>>;
8
+ listRepos(): Promise<ServiceResponse<GithubReposResponse>>;
9
+ }
10
+
11
+ class GithubRemoteDataSourceImpl extends BaseRemote implements GithubRemoteDataSource {
12
+ async getOAuthUrl(): Promise<ServiceResponse<GithubOAuthStart>> {
13
+ const { data } = await api.get<ServiceResponse<GithubOAuthStart>>('/v1/github/oauth/start');
14
+ return data;
15
+ }
16
+
17
+ async listRepos(): Promise<ServiceResponse<GithubReposResponse>> {
18
+ const { data } = await api.get<ServiceResponse<GithubReposResponse>>('/v1/github/repos');
19
+ return data;
20
+ }
21
+ }
22
+
23
+ export const githubRemoteDataSource: GithubRemoteDataSource = new GithubRemoteDataSourceImpl();
@@ -0,0 +1,35 @@
1
+ import { BaseRepository } from '../../data/base-repository';
2
+ import type { GithubRemoteDataSource } from './remote';
3
+ import { githubRemoteDataSource } from './remote';
4
+ import type { GithubOAuthStart, GithubRepo, GithubReposResponse } from './types';
5
+
6
+ export interface GithubRepository {
7
+ getOAuthUrl(): Promise<GithubOAuthStart>;
8
+ listRepos(): Promise<GithubRepo[]>;
9
+ }
10
+
11
+ class GithubRepositoryImpl extends BaseRepository implements GithubRepository {
12
+ constructor(private readonly remote: GithubRemoteDataSource) {
13
+ super();
14
+ }
15
+
16
+ async getOAuthUrl(): Promise<GithubOAuthStart> {
17
+ const res = await this.remote.getOAuthUrl();
18
+ return this.unwrapOrThrow(res);
19
+ }
20
+
21
+ async listRepos(): Promise<GithubRepo[]> {
22
+ const res = await this.remote.listRepos();
23
+ const payload = this.unwrapOrThrow(res) as GithubReposResponse;
24
+ return (payload.repositories || []).map((repo) => ({
25
+ id: repo.id,
26
+ name: repo.name,
27
+ fullName: repo.full_name,
28
+ private: repo.private,
29
+ defaultBranch: repo.default_branch,
30
+ ownerLogin: repo.owner?.login ?? null,
31
+ }));
32
+ }
33
+ }
34
+
35
+ export const githubRepository: GithubRepository = new GithubRepositoryImpl(githubRemoteDataSource);
@@ -0,0 +1,23 @@
1
+ export type GithubOAuthStart = {
2
+ url: string;
3
+ };
4
+
5
+ export type GithubRepo = {
6
+ id: number;
7
+ name: string;
8
+ fullName: string;
9
+ private: boolean;
10
+ defaultBranch?: string;
11
+ ownerLogin?: string | null;
12
+ };
13
+
14
+ export type GithubReposResponse = {
15
+ repositories: {
16
+ id: number;
17
+ name: string;
18
+ full_name: string;
19
+ private: boolean;
20
+ default_branch?: string;
21
+ owner?: { login?: string | null };
22
+ }[];
23
+ };
@@ -0,0 +1,24 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import { BaseRemote } from '../base-remote';
4
+ import type { HomeSection, HomeSummary } from './types';
5
+
6
+ export interface HomeRemoteDataSource {
7
+ getSummary(): Promise<ServiceResponse<HomeSummary>>;
8
+ getSections(): Promise<ServiceResponse<HomeSection[]>>;
9
+ }
10
+
11
+ class HomeRemoteDataSourceImpl extends BaseRemote implements HomeRemoteDataSource {
12
+ async getSummary(): Promise<ServiceResponse<HomeSummary>> {
13
+ const { data } = await api.get<ServiceResponse<HomeSummary>>('/v1/home/summary');
14
+ return data;
15
+ }
16
+
17
+ async getSections(): Promise<ServiceResponse<HomeSection[]>> {
18
+ const { data } = await api.get<ServiceResponse<HomeSection[]>>('/v1/home/sections');
19
+ return data;
20
+ }
21
+ }
22
+
23
+ export const homeRemoteDataSource: HomeRemoteDataSource = new HomeRemoteDataSourceImpl();
24
+
@@ -0,0 +1,28 @@
1
+ import type { HomeRemoteDataSource } from './remote';
2
+ import { homeRemoteDataSource } from './remote';
3
+ import type { HomeSection, HomeSummary } from './types';
4
+ import { BaseRepository } from '../../data/base-repository';
5
+
6
+ export interface HomeRepository {
7
+ getSummary(): Promise<HomeSummary>;
8
+ getSections(): Promise<HomeSection[]>;
9
+ }
10
+
11
+ class HomeRepositoryImpl extends BaseRepository implements HomeRepository {
12
+ constructor(private readonly remote: HomeRemoteDataSource) {
13
+ super();
14
+ }
15
+
16
+ async getSummary(): Promise<HomeSummary> {
17
+ const res = await this.remote.getSummary();
18
+ return this.unwrapOrThrow(res);
19
+ }
20
+
21
+ async getSections(): Promise<HomeSection[]> {
22
+ const res = await this.remote.getSections();
23
+ return this.unwrapOrThrow(res);
24
+ }
25
+ }
26
+
27
+ export const homeRepository: HomeRepository = new HomeRepositoryImpl(homeRemoteDataSource);
28
+
@@ -0,0 +1,70 @@
1
+ export type HomeProfile = {
2
+ name: string | null;
3
+ email: string | null;
4
+ avatar: string | null;
5
+ };
6
+
7
+ export type HomeNotificationGroup = {
8
+ appId: string;
9
+ appName: string;
10
+ threadId: string | null;
11
+ count: number;
12
+ };
13
+
14
+ export type HomeNotifications = {
15
+ totalCount: number;
16
+ groups: HomeNotificationGroup[];
17
+ };
18
+
19
+ export type HomeSummary = {
20
+ profile: HomeProfile;
21
+ notifications: HomeNotifications;
22
+ };
23
+
24
+ export type HomeSectionAppStats = {
25
+ totalDownloads: number;
26
+ totalDownloadUsers: number;
27
+ totalLikes: number;
28
+ totalComments: number;
29
+ totalRatings: number;
30
+ averageRating: number | null;
31
+ totalMergeRequests: number;
32
+ totalForks: number;
33
+ };
34
+
35
+ export type HomeSectionAppDetails = {
36
+ id: string;
37
+ name: string;
38
+ description: string | null;
39
+ isLiked?: boolean;
40
+ url: string | null;
41
+ platform: string | null;
42
+ isPublic: boolean;
43
+ status: string | null;
44
+ lastUpdatedAt: string;
45
+ stats: HomeSectionAppStats | null;
46
+ };
47
+
48
+ export type HomeSectionApp = {
49
+ id: string;
50
+ appId: string;
51
+ sortOrder: number;
52
+ createdAt: string;
53
+ updatedAt: string;
54
+ app: HomeSectionAppDetails;
55
+ };
56
+
57
+ export type HomeSection = {
58
+ id: string;
59
+ title: string;
60
+ subtitle: string | null;
61
+ description: string | null;
62
+ layoutType: 'vertical_list' | 'horizontal_list' | 'grid' | 'carousel';
63
+ isPublished: boolean;
64
+ sortOrder: number;
65
+ metadata: Record<string, unknown>;
66
+ createdAt: string;
67
+ updatedAt: string;
68
+ listOfApps: HomeSectionApp[];
69
+ };
70
+
@@ -0,0 +1,3 @@
1
+ export * from '../core/services/supabase';
2
+
3
+
@@ -0,0 +1,57 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import { BaseRemote } from '../base-remote';
4
+ import type {
5
+ AppLikeList,
6
+ AppLikeMutationResult,
7
+ AppLikeStatsResult,
8
+ CreateAppLikeInput,
9
+ ListAppLikesQuery,
10
+ } from './types';
11
+
12
+ export interface AppLikesRemoteDataSource {
13
+ list(appId: string, query?: ListAppLikesQuery): Promise<ServiceResponse<AppLikeList>>;
14
+ create(appId: string, payload: CreateAppLikeInput): Promise<ServiceResponse<AppLikeMutationResult>>;
15
+ removeById(appId: string, likeId: string): Promise<ServiceResponse<AppLikeStatsResult>>;
16
+ removeMine(appId: string): Promise<ServiceResponse<AppLikeStatsResult>>;
17
+ }
18
+
19
+ class AppLikesRemoteDataSourceImpl extends BaseRemote implements AppLikesRemoteDataSource {
20
+ async list(appId: string, query?: ListAppLikesQuery): Promise<ServiceResponse<AppLikeList>> {
21
+ const params = query ? { ...query } : undefined;
22
+ const { data } = await api.get<ServiceResponse<AppLikeList>>(
23
+ `/v1/apps/${encodeURIComponent(appId)}/likes`,
24
+ { params }
25
+ );
26
+ return data;
27
+ }
28
+
29
+ async create(
30
+ appId: string,
31
+ payload: CreateAppLikeInput
32
+ ): Promise<ServiceResponse<AppLikeMutationResult>> {
33
+ const { data } = await api.post<ServiceResponse<AppLikeMutationResult>>(
34
+ `/v1/apps/${encodeURIComponent(appId)}/likes`,
35
+ payload
36
+ );
37
+ return data;
38
+ }
39
+
40
+ async removeById(appId: string, likeId: string): Promise<ServiceResponse<AppLikeStatsResult>> {
41
+ const { data } = await api.delete<ServiceResponse<AppLikeStatsResult>>(
42
+ `/v1/apps/${encodeURIComponent(appId)}/likes/${encodeURIComponent(likeId)}`
43
+ );
44
+ return data;
45
+ }
46
+
47
+ async removeMine(appId: string): Promise<ServiceResponse<AppLikeStatsResult>> {
48
+ const { data } = await api.delete<ServiceResponse<AppLikeStatsResult>>(
49
+ `/v1/apps/${encodeURIComponent(appId)}/likes/me`
50
+ );
51
+ return data;
52
+ }
53
+ }
54
+
55
+ export const appLikesRemoteDataSource: AppLikesRemoteDataSource = new AppLikesRemoteDataSourceImpl();
56
+
57
+
@@ -0,0 +1,47 @@
1
+ import type { AppLikesRemoteDataSource } from './remote';
2
+ import { appLikesRemoteDataSource } from './remote';
3
+ import type {
4
+ AppLikeList,
5
+ AppLikeMutationResult,
6
+ AppLikeStatsResult,
7
+ CreateAppLikeInput,
8
+ ListAppLikesQuery,
9
+ } from './types';
10
+ import { BaseRepository } from '../../data/base-repository';
11
+
12
+ export interface AppLikesRepository {
13
+ list(appId: string, query?: ListAppLikesQuery): Promise<AppLikeList>;
14
+ create(appId: string, payload: CreateAppLikeInput): Promise<AppLikeMutationResult>;
15
+ removeById(appId: string, likeId: string): Promise<AppLikeStatsResult>;
16
+ removeMine(appId: string): Promise<AppLikeStatsResult>;
17
+ }
18
+
19
+ class AppLikesRepositoryImpl extends BaseRepository implements AppLikesRepository {
20
+ constructor(private readonly remote: AppLikesRemoteDataSource) {
21
+ super();
22
+ }
23
+
24
+ async list(appId: string, query?: ListAppLikesQuery): Promise<AppLikeList> {
25
+ const res = await this.remote.list(appId, query);
26
+ return this.unwrapOrThrow(res);
27
+ }
28
+
29
+ async create(appId: string, payload: CreateAppLikeInput): Promise<AppLikeMutationResult> {
30
+ const res = await this.remote.create(appId, payload);
31
+ return this.unwrapOrThrow(res);
32
+ }
33
+
34
+ async removeById(appId: string, likeId: string): Promise<AppLikeStatsResult> {
35
+ const res = await this.remote.removeById(appId, likeId);
36
+ return this.unwrapOrThrow(res);
37
+ }
38
+
39
+ async removeMine(appId: string): Promise<AppLikeStatsResult> {
40
+ const res = await this.remote.removeMine(appId);
41
+ return this.unwrapOrThrow(res);
42
+ }
43
+ }
44
+
45
+ export const appLikesRepository: AppLikesRepository = new AppLikesRepositoryImpl(appLikesRemoteDataSource);
46
+
47
+
@@ -0,0 +1,46 @@
1
+ export type AppLike = {
2
+ id: string;
3
+ appId: string;
4
+ userId: string;
5
+ source: string | null;
6
+ context: Record<string, unknown>;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ };
10
+
11
+ export type ListAppLikesQuery = {
12
+ page?: number;
13
+ pageSize?: number;
14
+ userId?: string;
15
+ };
16
+
17
+ export type CreateAppLikeInput = {
18
+ source?: string;
19
+ context?: Record<string, unknown>;
20
+ };
21
+
22
+ export type AppLikeStats = {
23
+ total?: number;
24
+ };
25
+
26
+ export type AppLikeList = {
27
+ items: AppLike[];
28
+ pageInfo: {
29
+ page: number;
30
+ pageSize: number;
31
+ total: number;
32
+ hasMore: boolean;
33
+ };
34
+ stats: AppLikeStats;
35
+ };
36
+
37
+ export type AppLikeMutationResult = {
38
+ like: AppLike;
39
+ stats?: AppLikeStats;
40
+ };
41
+
42
+ export type AppLikeStatsResult = {
43
+ stats?: AppLikeStats;
44
+ };
45
+
46
+
@@ -0,0 +1,28 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import { BaseRemote } from '../base-remote';
4
+ import type { MeProfile } from './types';
5
+
6
+ export interface MeRemoteDataSource {
7
+ getMe(): Promise<ServiceResponse<MeProfile>>;
8
+ setExpoPushToken(expoPushToken: string): Promise<ServiceResponse<{ expoPushToken: string }>>;
9
+ }
10
+
11
+ class MeRemoteDataSourceImpl extends BaseRemote implements MeRemoteDataSource {
12
+ async getMe(): Promise<ServiceResponse<MeProfile>> {
13
+ const { data } = await api.get<ServiceResponse<MeProfile>>('/v1/me');
14
+ return data;
15
+ }
16
+
17
+ async setExpoPushToken(expoPushToken: string): Promise<ServiceResponse<{ expoPushToken: string }>> {
18
+ const { data } = await api.post<ServiceResponse<{ expoPushToken: string }>>(
19
+ '/v1/me/expo-push-token',
20
+ { expoPushToken }
21
+ );
22
+ return data;
23
+ }
24
+ }
25
+
26
+ export const meRemoteDataSource: MeRemoteDataSource = new MeRemoteDataSourceImpl();
27
+
28
+
@@ -0,0 +1,30 @@
1
+ import type { MeRemoteDataSource } from './remote';
2
+ import { meRemoteDataSource } from './remote';
3
+ import type { MeProfile } from './types';
4
+ import { BaseRepository } from '../../data/base-repository';
5
+
6
+ export interface MeRepository {
7
+ getMe(): Promise<MeProfile>;
8
+ setExpoPushToken(expoPushToken: string): Promise<string>;
9
+ }
10
+
11
+ class MeRepositoryImpl extends BaseRepository implements MeRepository {
12
+ constructor(private readonly remote: MeRemoteDataSource) {
13
+ super();
14
+ }
15
+
16
+ async getMe(): Promise<MeProfile> {
17
+ const res = await this.remote.getMe();
18
+ return this.unwrapOrThrow(res);
19
+ }
20
+
21
+ async setExpoPushToken(expoPushToken: string): Promise<string> {
22
+ const res = await this.remote.setExpoPushToken(expoPushToken);
23
+ const out = this.unwrapOrThrow(res);
24
+ return out.expoPushToken;
25
+ }
26
+ }
27
+
28
+ export const meRepository: MeRepository = new MeRepositoryImpl(meRemoteDataSource);
29
+
30
+
@@ -0,0 +1,14 @@
1
+ export type MeProfile = {
2
+ id: string;
3
+ name: string | null;
4
+ email: string | null;
5
+ phone: string | null;
6
+ isVerified: boolean;
7
+ avatar: string | null;
8
+ organizationId: string | null;
9
+ createdAt: string;
10
+ updatedAt: string;
11
+ lastLoginAt: string | null;
12
+ };
13
+
14
+