@growsober/sdk 1.0.3 → 1.0.5
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/dist/api/client.js +10 -3
- package/dist/api/mutations/event-chat.d.ts +10 -27
- package/dist/api/mutations/index.d.ts +1 -0
- package/dist/api/mutations/index.js +2 -1
- package/dist/api/mutations/user-pins.d.ts +132 -0
- package/dist/api/mutations/user-pins.js +158 -0
- package/dist/api/queries/index.d.ts +1 -0
- package/dist/api/queries/index.js +2 -1
- package/dist/api/queries/user-pins.d.ts +134 -0
- package/dist/api/queries/user-pins.js +141 -0
- package/package.json +1 -1
- package/src/api/client.ts +9 -2
- package/src/api/mutations/index.ts +1 -0
- package/src/api/mutations/user-pins.ts +186 -0
- package/src/api/queries/index.ts +1 -0
- package/src/api/queries/user-pins.ts +193 -0
package/dist/api/client.js
CHANGED
|
@@ -35,8 +35,15 @@ function createApiClient(sdkConfig) {
|
|
|
35
35
|
}
|
|
36
36
|
return requestConfig;
|
|
37
37
|
}, (error) => Promise.reject(error));
|
|
38
|
-
// Response interceptor - handle 401
|
|
39
|
-
client.interceptors.response.use((response) =>
|
|
38
|
+
// Response interceptor - unwrap API response and handle 401
|
|
39
|
+
client.interceptors.response.use((response) => {
|
|
40
|
+
// API wraps all responses in {data: ..., meta: {...}}
|
|
41
|
+
// Unwrap to return just the data portion
|
|
42
|
+
if (response.data && typeof response.data === 'object' && 'data' in response.data) {
|
|
43
|
+
response.data = response.data.data;
|
|
44
|
+
}
|
|
45
|
+
return response;
|
|
46
|
+
}, async (error) => {
|
|
40
47
|
if (error.response?.status === 401) {
|
|
41
48
|
if (sdkConfig.refreshAccessToken) {
|
|
42
49
|
try {
|
|
@@ -58,4 +65,4 @@ function createApiClient(sdkConfig) {
|
|
|
58
65
|
});
|
|
59
66
|
return client;
|
|
60
67
|
}
|
|
61
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
68
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwaS9jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBWUEsb0NBR0M7QUFFRCxvQ0FLQztBQXRCRCxrREFBcUY7QUFTckYsSUFBSSxNQUFNLEdBQXFCLElBQUksQ0FBQztBQUNwQyxJQUFJLFNBQVMsR0FBeUIsSUFBSSxDQUFDO0FBbUVsQyw4QkFBUztBQWpFbEIsU0FBZ0IsWUFBWSxDQUFDLFNBQW9CO0lBQy9DLE1BQU0sR0FBRyxTQUFTLENBQUM7SUFDbkIsb0JBQUEsU0FBUyxHQUFHLGVBQWUsQ0FBQyxTQUFTLENBQUMsQ0FBQztBQUN6QyxDQUFDO0FBRUQsU0FBZ0IsWUFBWTtJQUMxQixJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDZixNQUFNLElBQUksS0FBSyxDQUFDLGdEQUFnRCxDQUFDLENBQUM7SUFDcEUsQ0FBQztJQUNELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRCxTQUFTLGVBQWUsQ0FBQyxTQUFvQjtJQUMzQyxNQUFNLE1BQU0sR0FBRyxlQUFLLENBQUMsTUFBTSxDQUFDO1FBQzFCLE9BQU8sRUFBRSxTQUFTLENBQUMsT0FBTztRQUMxQixPQUFPLEVBQUU7WUFDUCxjQUFjLEVBQUUsa0JBQWtCO1NBQ25DO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsdUNBQXVDO0lBQ3ZDLE1BQU0sQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FDN0IsS0FBSyxFQUFFLGFBQXlDLEVBQUUsRUFBRTtRQUNsRCxNQUFNLEtBQUssR0FBRyxNQUFNLFNBQVMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUMvQyxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ1YsYUFBYSxDQUFDLE9BQU8sQ0FBQyxhQUFhLEdBQUcsVUFBVSxLQUFLLEVBQUUsQ0FBQztRQUMxRCxDQUFDO1FBQ0QsT0FBTyxhQUFhLENBQUM7SUFDdkIsQ0FBQyxFQUNELENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUNqQyxDQUFDO0lBRUYsNERBQTREO0lBQzVELE1BQU0sQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FDOUIsQ0FBQyxRQUFRLEVBQUUsRUFBRTtRQUNYLHNEQUFzRDtRQUN0RCx5Q0FBeUM7UUFDekMsSUFBSSxRQUFRLENBQUMsSUFBSSxJQUFJLE9BQU8sUUFBUSxDQUFDLElBQUksS0FBSyxRQUFRLElBQUksTUFBTSxJQUFJLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNsRixRQUFRLENBQUMsSUFBSSxHQUFHLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQ3JDLENBQUM7UUFDRCxPQUFPLFFBQVEsQ0FBQztJQUNsQixDQUFDLEVBQ0QsS0FBSyxFQUFFLEtBQWlCLEVBQUUsRUFBRTtRQUMxQixJQUFJLEtBQUssQ0FBQyxRQUFRLEVBQUUsTUFBTSxLQUFLLEdBQUcsRUFBRSxDQUFDO1lBQ25DLElBQUksU0FBUyxDQUFDLGtCQUFrQixFQUFFLENBQUM7Z0JBQ2pDLElBQUksQ0FBQztvQkFDSCxNQUFNLFFBQVEsR0FBRyxNQUFNLFNBQVMsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO29CQUN0RCxJQUFJLFFBQVEsSUFBSSxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7d0JBQzdCLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLGFBQWEsR0FBRyxVQUFVLFFBQVEsRUFBRSxDQUFDO3dCQUMxRCxPQUFPLE1BQU0sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO29CQUN0QyxDQUFDO2dCQUNILENBQUM7Z0JBQUMsTUFBTSxDQUFDO29CQUNQLFNBQVMsQ0FBQyxjQUFjLEVBQUUsRUFBRSxDQUFDO2dCQUMvQixDQUFDO1lBQ0gsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLFNBQVMsQ0FBQyxjQUFjLEVBQUUsRUFBRSxDQUFDO1lBQy9CLENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQy9CLENBQUMsQ0FDRixDQUFDO0lBRUYsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBheGlvcywgeyBBeGlvc0luc3RhbmNlLCBBeGlvc0Vycm9yLCBJbnRlcm5hbEF4aW9zUmVxdWVzdENvbmZpZyB9IGZyb20gJ2F4aW9zJztcblxuZXhwb3J0IGludGVyZmFjZSBTREtDb25maWcge1xuICBiYXNlVVJMOiBzdHJpbmc7XG4gIGdldEFjY2Vzc1Rva2VuOiAoKSA9PiBzdHJpbmcgfCBudWxsIHwgUHJvbWlzZTxzdHJpbmcgfCBudWxsPjtcbiAgcmVmcmVzaEFjY2Vzc1Rva2VuPzogKCkgPT4gUHJvbWlzZTxzdHJpbmc+O1xuICBvblVuYXV0aG9yaXplZD86ICgpID0+IHZvaWQ7XG59XG5cbmxldCBjb25maWc6IFNES0NvbmZpZyB8IG51bGwgPSBudWxsO1xubGV0IGFwaUNsaWVudDogQXhpb3NJbnN0YW5jZSB8IG51bGwgPSBudWxsO1xuXG5leHBvcnQgZnVuY3Rpb24gY29uZmlndXJlU0RLKHNka0NvbmZpZzogU0RLQ29uZmlnKTogdm9pZCB7XG4gIGNvbmZpZyA9IHNka0NvbmZpZztcbiAgYXBpQ2xpZW50ID0gY3JlYXRlQXBpQ2xpZW50KHNka0NvbmZpZyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBcGlDbGllbnQoKTogQXhpb3NJbnN0YW5jZSB7XG4gIGlmICghYXBpQ2xpZW50KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdTREsgbm90IGNvbmZpZ3VyZWQuIENhbGwgY29uZmlndXJlU0RLKCkgZmlyc3QuJyk7XG4gIH1cbiAgcmV0dXJuIGFwaUNsaWVudDtcbn1cblxuZnVuY3Rpb24gY3JlYXRlQXBpQ2xpZW50KHNka0NvbmZpZzogU0RLQ29uZmlnKTogQXhpb3NJbnN0YW5jZSB7XG4gIGNvbnN0IGNsaWVudCA9IGF4aW9zLmNyZWF0ZSh7XG4gICAgYmFzZVVSTDogc2RrQ29uZmlnLmJhc2VVUkwsXG4gICAgaGVhZGVyczoge1xuICAgICAgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICB9LFxuICB9KTtcblxuICAvLyBSZXF1ZXN0IGludGVyY2VwdG9yIC0gYWRkIGF1dGggdG9rZW5cbiAgY2xpZW50LmludGVyY2VwdG9ycy5yZXF1ZXN0LnVzZShcbiAgICBhc3luYyAocmVxdWVzdENvbmZpZzogSW50ZXJuYWxBeGlvc1JlcXVlc3RDb25maWcpID0+IHtcbiAgICAgIGNvbnN0IHRva2VuID0gYXdhaXQgc2RrQ29uZmlnLmdldEFjY2Vzc1Rva2VuKCk7XG4gICAgICBpZiAodG9rZW4pIHtcbiAgICAgICAgcmVxdWVzdENvbmZpZy5oZWFkZXJzLkF1dGhvcml6YXRpb24gPSBgQmVhcmVyICR7dG9rZW59YDtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXF1ZXN0Q29uZmlnO1xuICAgIH0sXG4gICAgKGVycm9yKSA9PiBQcm9taXNlLnJlamVjdChlcnJvcilcbiAgKTtcblxuICAvLyBSZXNwb25zZSBpbnRlcmNlcHRvciAtIHVud3JhcCBBUEkgcmVzcG9uc2UgYW5kIGhhbmRsZSA0MDFcbiAgY2xpZW50LmludGVyY2VwdG9ycy5yZXNwb25zZS51c2UoXG4gICAgKHJlc3BvbnNlKSA9PiB7XG4gICAgICAvLyBBUEkgd3JhcHMgYWxsIHJlc3BvbnNlcyBpbiB7ZGF0YTogLi4uLCBtZXRhOiB7Li4ufX1cbiAgICAgIC8vIFVud3JhcCB0byByZXR1cm4ganVzdCB0aGUgZGF0YSBwb3J0aW9uXG4gICAgICBpZiAocmVzcG9uc2UuZGF0YSAmJiB0eXBlb2YgcmVzcG9uc2UuZGF0YSA9PT0gJ29iamVjdCcgJiYgJ2RhdGEnIGluIHJlc3BvbnNlLmRhdGEpIHtcbiAgICAgICAgcmVzcG9uc2UuZGF0YSA9IHJlc3BvbnNlLmRhdGEuZGF0YTtcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXNwb25zZTtcbiAgICB9LFxuICAgIGFzeW5jIChlcnJvcjogQXhpb3NFcnJvcikgPT4ge1xuICAgICAgaWYgKGVycm9yLnJlc3BvbnNlPy5zdGF0dXMgPT09IDQwMSkge1xuICAgICAgICBpZiAoc2RrQ29uZmlnLnJlZnJlc2hBY2Nlc3NUb2tlbikge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBuZXdUb2tlbiA9IGF3YWl0IHNka0NvbmZpZy5yZWZyZXNoQWNjZXNzVG9rZW4oKTtcbiAgICAgICAgICAgIGlmIChuZXdUb2tlbiAmJiBlcnJvci5jb25maWcpIHtcbiAgICAgICAgICAgICAgZXJyb3IuY29uZmlnLmhlYWRlcnMuQXV0aG9yaXphdGlvbiA9IGBCZWFyZXIgJHtuZXdUb2tlbn1gO1xuICAgICAgICAgICAgICByZXR1cm4gY2xpZW50LnJlcXVlc3QoZXJyb3IuY29uZmlnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIHNka0NvbmZpZy5vblVuYXV0aG9yaXplZD8uKCk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHNka0NvbmZpZy5vblVuYXV0aG9yaXplZD8uKCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlcnJvcik7XG4gICAgfVxuICApO1xuXG4gIHJldHVybiBjbGllbnQ7XG59XG5cbmV4cG9ydCB7IGFwaUNsaWVudCB9O1xuIl19
|
|
@@ -62,45 +62,28 @@ export declare function useMarkMessagesAsRead(eventId: string): import("@tanstac
|
|
|
62
62
|
*/
|
|
63
63
|
export declare function useSendEventChatMessage(eventId: string): import("@tanstack/react-query").UseMutationResult<{
|
|
64
64
|
id: string;
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
conversationId: string;
|
|
66
|
+
role: "USER" | "ASSISTANT" | "SYSTEM";
|
|
67
67
|
content: string;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
isEdited: boolean;
|
|
72
|
-
isDeleted: boolean;
|
|
68
|
+
model?: Record<string, never>;
|
|
69
|
+
promptTokens?: Record<string, never>;
|
|
70
|
+
completionTokens?: Record<string, never>;
|
|
73
71
|
createdAt: string;
|
|
74
|
-
updatedAt?: string;
|
|
75
|
-
userName?: string;
|
|
76
|
-
userImage?: string;
|
|
77
|
-
replyToContent?: string;
|
|
78
|
-
replyToUserName?: string;
|
|
79
72
|
}, Error, {
|
|
80
73
|
content: string;
|
|
81
|
-
messageType: "TEXT" | "IMAGE" | "SYSTEM" | "ANNOUNCEMENT";
|
|
82
|
-
imageUrl?: string;
|
|
83
|
-
replyToId?: string;
|
|
84
74
|
}, unknown>;
|
|
85
75
|
/**
|
|
86
76
|
* Update a message
|
|
87
77
|
*/
|
|
88
78
|
export declare function useUpdateEventChatMessage(eventId: string): import("@tanstack/react-query").UseMutationResult<{
|
|
89
79
|
id: string;
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
conversationId: string;
|
|
81
|
+
role: "USER" | "ASSISTANT" | "SYSTEM";
|
|
92
82
|
content: string;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
isEdited: boolean;
|
|
97
|
-
isDeleted: boolean;
|
|
83
|
+
model?: Record<string, never>;
|
|
84
|
+
promptTokens?: Record<string, never>;
|
|
85
|
+
completionTokens?: Record<string, never>;
|
|
98
86
|
createdAt: string;
|
|
99
|
-
updatedAt?: string;
|
|
100
|
-
userName?: string;
|
|
101
|
-
userImage?: string;
|
|
102
|
-
replyToContent?: string;
|
|
103
|
-
replyToUserName?: string;
|
|
104
87
|
}, Error, {
|
|
105
88
|
messageId: string;
|
|
106
89
|
} & {
|
|
@@ -36,4 +36,5 @@ __exportStar(require("./ambassadors"), exports);
|
|
|
36
36
|
__exportStar(require("./grow90"), exports);
|
|
37
37
|
__exportStar(require("./matching"), exports);
|
|
38
38
|
__exportStar(require("./event-chat"), exports);
|
|
39
|
-
|
|
39
|
+
__exportStar(require("./user-pins"), exports);
|
|
40
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL211dGF0aW9ucy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7R0FJRzs7Ozs7Ozs7Ozs7Ozs7OztBQUVILDBDQUF3QjtBQUN4Qix5Q0FBdUI7QUFDdkIsNkNBQTJCO0FBQzNCLDJDQUF5QjtBQUN6Qix5Q0FBdUI7QUFDdkIsNENBQTBCO0FBQzFCLHdDQUFzQjtBQUN0QixrREFBZ0M7QUFDaEMsMkNBQXlCO0FBQ3pCLGtEQUFnQztBQUNoQyw0Q0FBMEI7QUFDMUIsMENBQXdCO0FBQ3hCLHlDQUF1QjtBQUN2QixnREFBOEI7QUFDOUIsMkNBQXlCO0FBQ3pCLDZDQUEyQjtBQUMzQiwrQ0FBNkI7QUFDN0IsOENBQTRCIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNdXRhdGlvbiBIb29rc1xuICpcbiAqIFJlLWV4cG9ydHMgYWxsIG11dGF0aW9uIGhvb2tzIGZvciBBUEkgZW5kcG9pbnRzLlxuICovXG5cbmV4cG9ydCAqIGZyb20gJy4vYWRtaW4nO1xuZXhwb3J0ICogZnJvbSAnLi9hdXRoJztcbmV4cG9ydCAqIGZyb20gJy4vYm9va2luZ3MnO1xuZXhwb3J0ICogZnJvbSAnLi9ldmVudHMnO1xuZXhwb3J0ICogZnJvbSAnLi9odWJzJztcbmV4cG9ydCAqIGZyb20gJy4vbGlicmFyeSc7XG5leHBvcnQgKiBmcm9tICcuL21hcCc7XG5leHBvcnQgKiBmcm9tICcuL25vdGlmaWNhdGlvbnMnO1xuZXhwb3J0ICogZnJvbSAnLi9vZmZlcnMnO1xuZXhwb3J0ICogZnJvbSAnLi9zdWJzY3JpcHRpb25zJztcbmV4cG9ydCAqIGZyb20gJy4vc3VwcG9ydCc7XG5leHBvcnQgKiBmcm9tICcuL3VzZXJzJztcbmV4cG9ydCAqIGZyb20gJy4vamFjayc7XG5leHBvcnQgKiBmcm9tICcuL2FtYmFzc2Fkb3JzJztcbmV4cG9ydCAqIGZyb20gJy4vZ3JvdzkwJztcbmV4cG9ydCAqIGZyb20gJy4vbWF0Y2hpbmcnO1xuZXhwb3J0ICogZnJvbSAnLi9ldmVudC1jaGF0JztcbmV4cG9ydCAqIGZyb20gJy4vdXNlci1waW5zJztcbiJdfQ==
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Pins Mutation Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query mutation hooks for user pin operations.
|
|
5
|
+
* These hooks handle creating and deleting user pins
|
|
6
|
+
* ("I'm here now" / "I'll be there").
|
|
7
|
+
*
|
|
8
|
+
* @module api/mutations/user-pins
|
|
9
|
+
*/
|
|
10
|
+
import { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
|
|
11
|
+
import { UserPinResponse, PinType } from '../queries/user-pins';
|
|
12
|
+
export interface CreatePinRequest {
|
|
13
|
+
type: PinType;
|
|
14
|
+
latitude: number;
|
|
15
|
+
longitude: number;
|
|
16
|
+
locationName?: string;
|
|
17
|
+
activity: string;
|
|
18
|
+
venueId?: string;
|
|
19
|
+
scheduledTime?: string;
|
|
20
|
+
duration?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a new user pin
|
|
24
|
+
*
|
|
25
|
+
* @description
|
|
26
|
+
* Creates a new pin showing where the user is or will be.
|
|
27
|
+
* Any existing active pin is automatically deactivated.
|
|
28
|
+
*
|
|
29
|
+
* Two types of pins:
|
|
30
|
+
* - HERE_NOW: User is currently at this location (default 1 hour duration)
|
|
31
|
+
* - SCHEDULED: User will be at this location at a specific time (default 2 hour duration)
|
|
32
|
+
*
|
|
33
|
+
* @endpoint POST /api/v1/user-pins
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* Drop a "I'm here now" pin:
|
|
37
|
+
* ```tsx
|
|
38
|
+
* import { useCreateUserPin } from '@growsober/sdk';
|
|
39
|
+
*
|
|
40
|
+
* function DropPinButton({ location }) {
|
|
41
|
+
* const createPin = useCreateUserPin();
|
|
42
|
+
*
|
|
43
|
+
* const handleDropPin = () => {
|
|
44
|
+
* createPin.mutate({
|
|
45
|
+
* type: 'HERE_NOW',
|
|
46
|
+
* latitude: location.lat,
|
|
47
|
+
* longitude: location.lng,
|
|
48
|
+
* locationName: 'Fabrica Coffee',
|
|
49
|
+
* activity: 'Working remotely',
|
|
50
|
+
* duration: 120, // 2 hours
|
|
51
|
+
* });
|
|
52
|
+
* };
|
|
53
|
+
*
|
|
54
|
+
* return (
|
|
55
|
+
* <Button onPress={handleDropPin} loading={createPin.isPending}>
|
|
56
|
+
* Drop Pin
|
|
57
|
+
* </Button>
|
|
58
|
+
* );
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* Schedule a pin for later:
|
|
64
|
+
* ```tsx
|
|
65
|
+
* import { useCreateUserPin } from '@growsober/sdk';
|
|
66
|
+
*
|
|
67
|
+
* function SchedulePinForm() {
|
|
68
|
+
* const createPin = useCreateUserPin({
|
|
69
|
+
* onSuccess: () => {
|
|
70
|
+
* toast.show('Pin scheduled!');
|
|
71
|
+
* },
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* const handleSchedule = (data) => {
|
|
75
|
+
* createPin.mutate({
|
|
76
|
+
* type: 'SCHEDULED',
|
|
77
|
+
* latitude: data.venue.lat,
|
|
78
|
+
* longitude: data.venue.lng,
|
|
79
|
+
* locationName: data.venue.name,
|
|
80
|
+
* venueId: data.venue.id,
|
|
81
|
+
* activity: 'Coffee meetup',
|
|
82
|
+
* scheduledTime: data.time.toISOString(),
|
|
83
|
+
* duration: 90,
|
|
84
|
+
* });
|
|
85
|
+
* };
|
|
86
|
+
*
|
|
87
|
+
* return <ScheduleForm onSubmit={handleSchedule} />;
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @param options - TanStack Query mutation options
|
|
92
|
+
* @returns TanStack Query mutation result
|
|
93
|
+
*/
|
|
94
|
+
export declare function useCreateUserPin(options?: Omit<UseMutationOptions<UserPinResponse, Error, CreatePinRequest>, 'mutationFn'>): UseMutationResult<UserPinResponse, Error, CreatePinRequest>;
|
|
95
|
+
/**
|
|
96
|
+
* Delete a user pin
|
|
97
|
+
*
|
|
98
|
+
* @description
|
|
99
|
+
* Deactivates a user pin. Users can only delete their own pins.
|
|
100
|
+
*
|
|
101
|
+
* @endpoint DELETE /api/v1/user-pins/:id
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```tsx
|
|
105
|
+
* import { useDeleteUserPin } from '@growsober/sdk';
|
|
106
|
+
*
|
|
107
|
+
* function MyPinCard({ pin }) {
|
|
108
|
+
* const deletePin = useDeleteUserPin({
|
|
109
|
+
* onSuccess: () => {
|
|
110
|
+
* toast.show('Pin removed');
|
|
111
|
+
* },
|
|
112
|
+
* });
|
|
113
|
+
*
|
|
114
|
+
* return (
|
|
115
|
+
* <Card>
|
|
116
|
+
* <Text>{pin.activity}</Text>
|
|
117
|
+
* <Text>At {pin.locationName}</Text>
|
|
118
|
+
* <Button
|
|
119
|
+
* onPress={() => deletePin.mutate(pin.id)}
|
|
120
|
+
* loading={deletePin.isPending}
|
|
121
|
+
* >
|
|
122
|
+
* Remove Pin
|
|
123
|
+
* </Button>
|
|
124
|
+
* </Card>
|
|
125
|
+
* );
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @param options - TanStack Query mutation options
|
|
130
|
+
* @returns TanStack Query mutation result
|
|
131
|
+
*/
|
|
132
|
+
export declare function useDeleteUserPin(options?: Omit<UseMutationOptions<void, Error, string>, 'mutationFn'>): UseMutationResult<void, Error, string>;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* User Pins Mutation Hooks
|
|
4
|
+
*
|
|
5
|
+
* TanStack Query mutation hooks for user pin operations.
|
|
6
|
+
* These hooks handle creating and deleting user pins
|
|
7
|
+
* ("I'm here now" / "I'll be there").
|
|
8
|
+
*
|
|
9
|
+
* @module api/mutations/user-pins
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.useCreateUserPin = useCreateUserPin;
|
|
13
|
+
exports.useDeleteUserPin = useDeleteUserPin;
|
|
14
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
15
|
+
const client_1 = require("../client");
|
|
16
|
+
const user_pins_1 = require("../queries/user-pins");
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// MUTATION HOOKS
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Create a new user pin
|
|
22
|
+
*
|
|
23
|
+
* @description
|
|
24
|
+
* Creates a new pin showing where the user is or will be.
|
|
25
|
+
* Any existing active pin is automatically deactivated.
|
|
26
|
+
*
|
|
27
|
+
* Two types of pins:
|
|
28
|
+
* - HERE_NOW: User is currently at this location (default 1 hour duration)
|
|
29
|
+
* - SCHEDULED: User will be at this location at a specific time (default 2 hour duration)
|
|
30
|
+
*
|
|
31
|
+
* @endpoint POST /api/v1/user-pins
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* Drop a "I'm here now" pin:
|
|
35
|
+
* ```tsx
|
|
36
|
+
* import { useCreateUserPin } from '@growsober/sdk';
|
|
37
|
+
*
|
|
38
|
+
* function DropPinButton({ location }) {
|
|
39
|
+
* const createPin = useCreateUserPin();
|
|
40
|
+
*
|
|
41
|
+
* const handleDropPin = () => {
|
|
42
|
+
* createPin.mutate({
|
|
43
|
+
* type: 'HERE_NOW',
|
|
44
|
+
* latitude: location.lat,
|
|
45
|
+
* longitude: location.lng,
|
|
46
|
+
* locationName: 'Fabrica Coffee',
|
|
47
|
+
* activity: 'Working remotely',
|
|
48
|
+
* duration: 120, // 2 hours
|
|
49
|
+
* });
|
|
50
|
+
* };
|
|
51
|
+
*
|
|
52
|
+
* return (
|
|
53
|
+
* <Button onPress={handleDropPin} loading={createPin.isPending}>
|
|
54
|
+
* Drop Pin
|
|
55
|
+
* </Button>
|
|
56
|
+
* );
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* Schedule a pin for later:
|
|
62
|
+
* ```tsx
|
|
63
|
+
* import { useCreateUserPin } from '@growsober/sdk';
|
|
64
|
+
*
|
|
65
|
+
* function SchedulePinForm() {
|
|
66
|
+
* const createPin = useCreateUserPin({
|
|
67
|
+
* onSuccess: () => {
|
|
68
|
+
* toast.show('Pin scheduled!');
|
|
69
|
+
* },
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* const handleSchedule = (data) => {
|
|
73
|
+
* createPin.mutate({
|
|
74
|
+
* type: 'SCHEDULED',
|
|
75
|
+
* latitude: data.venue.lat,
|
|
76
|
+
* longitude: data.venue.lng,
|
|
77
|
+
* locationName: data.venue.name,
|
|
78
|
+
* venueId: data.venue.id,
|
|
79
|
+
* activity: 'Coffee meetup',
|
|
80
|
+
* scheduledTime: data.time.toISOString(),
|
|
81
|
+
* duration: 90,
|
|
82
|
+
* });
|
|
83
|
+
* };
|
|
84
|
+
*
|
|
85
|
+
* return <ScheduleForm onSubmit={handleSchedule} />;
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @param options - TanStack Query mutation options
|
|
90
|
+
* @returns TanStack Query mutation result
|
|
91
|
+
*/
|
|
92
|
+
function useCreateUserPin(options) {
|
|
93
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
94
|
+
return (0, react_query_1.useMutation)({
|
|
95
|
+
mutationFn: async (data) => {
|
|
96
|
+
const client = (0, client_1.getApiClient)();
|
|
97
|
+
const response = await client.post('/api/v1/user-pins', data);
|
|
98
|
+
return response.data;
|
|
99
|
+
},
|
|
100
|
+
onSuccess: () => {
|
|
101
|
+
// Invalidate pin queries to reflect new pin
|
|
102
|
+
queryClient.invalidateQueries({ queryKey: user_pins_1.userPinKeys.all });
|
|
103
|
+
},
|
|
104
|
+
...options,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Delete a user pin
|
|
109
|
+
*
|
|
110
|
+
* @description
|
|
111
|
+
* Deactivates a user pin. Users can only delete their own pins.
|
|
112
|
+
*
|
|
113
|
+
* @endpoint DELETE /api/v1/user-pins/:id
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```tsx
|
|
117
|
+
* import { useDeleteUserPin } from '@growsober/sdk';
|
|
118
|
+
*
|
|
119
|
+
* function MyPinCard({ pin }) {
|
|
120
|
+
* const deletePin = useDeleteUserPin({
|
|
121
|
+
* onSuccess: () => {
|
|
122
|
+
* toast.show('Pin removed');
|
|
123
|
+
* },
|
|
124
|
+
* });
|
|
125
|
+
*
|
|
126
|
+
* return (
|
|
127
|
+
* <Card>
|
|
128
|
+
* <Text>{pin.activity}</Text>
|
|
129
|
+
* <Text>At {pin.locationName}</Text>
|
|
130
|
+
* <Button
|
|
131
|
+
* onPress={() => deletePin.mutate(pin.id)}
|
|
132
|
+
* loading={deletePin.isPending}
|
|
133
|
+
* >
|
|
134
|
+
* Remove Pin
|
|
135
|
+
* </Button>
|
|
136
|
+
* </Card>
|
|
137
|
+
* );
|
|
138
|
+
* }
|
|
139
|
+
* ```
|
|
140
|
+
*
|
|
141
|
+
* @param options - TanStack Query mutation options
|
|
142
|
+
* @returns TanStack Query mutation result
|
|
143
|
+
*/
|
|
144
|
+
function useDeleteUserPin(options) {
|
|
145
|
+
const queryClient = (0, react_query_1.useQueryClient)();
|
|
146
|
+
return (0, react_query_1.useMutation)({
|
|
147
|
+
mutationFn: async (pinId) => {
|
|
148
|
+
const client = (0, client_1.getApiClient)();
|
|
149
|
+
await client.delete(`/api/v1/user-pins/${pinId}`);
|
|
150
|
+
},
|
|
151
|
+
onSuccess: () => {
|
|
152
|
+
// Invalidate pin queries to reflect deletion
|
|
153
|
+
queryClient.invalidateQueries({ queryKey: user_pins_1.userPinKeys.all });
|
|
154
|
+
},
|
|
155
|
+
...options,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci1waW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9tdXRhdGlvbnMvdXNlci1waW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7R0FRRzs7QUFzR0gsNENBb0JDO0FBdUNELDRDQWdCQztBQS9LRCx1REFLK0I7QUFDL0Isc0NBQXlDO0FBQ3pDLG9EQUE2RTtBQWlCN0UsK0VBQStFO0FBQy9FLGlCQUFpQjtBQUNqQiwrRUFBK0U7QUFFL0U7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBdUVHO0FBQ0gsU0FBZ0IsZ0JBQWdCLENBQzlCLE9BR0M7SUFFRCxNQUFNLFdBQVcsR0FBRyxJQUFBLDRCQUFjLEdBQUUsQ0FBQztJQUVyQyxPQUFPLElBQUEseUJBQVcsRUFBQztRQUNqQixVQUFVLEVBQUUsS0FBSyxFQUFFLElBQXNCLEVBQTRCLEVBQUU7WUFDckUsTUFBTSxNQUFNLEdBQUcsSUFBQSxxQkFBWSxHQUFFLENBQUM7WUFDOUIsTUFBTSxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsSUFBSSxDQUFrQixtQkFBbUIsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUMvRSxPQUFPLFFBQVEsQ0FBQyxJQUFJLENBQUM7UUFDdkIsQ0FBQztRQUNELFNBQVMsRUFBRSxHQUFHLEVBQUU7WUFDZCw0Q0FBNEM7WUFDNUMsV0FBVyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsUUFBUSxFQUFFLHVCQUFXLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUMvRCxDQUFDO1FBQ0QsR0FBRyxPQUFPO0tBQ1gsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FvQ0c7QUFDSCxTQUFnQixnQkFBZ0IsQ0FDOUIsT0FBcUU7SUFFckUsTUFBTSxXQUFXLEdBQUcsSUFBQSw0QkFBYyxHQUFFLENBQUM7SUFFckMsT0FBTyxJQUFBLHlCQUFXLEVBQUM7UUFDakIsVUFBVSxFQUFFLEtBQUssRUFBRSxLQUFhLEVBQWlCLEVBQUU7WUFDakQsTUFBTSxNQUFNLEdBQUcsSUFBQSxxQkFBWSxHQUFFLENBQUM7WUFDOUIsTUFBTSxNQUFNLENBQUMsTUFBTSxDQUFDLHFCQUFxQixLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ3BELENBQUM7UUFDRCxTQUFTLEVBQUUsR0FBRyxFQUFFO1lBQ2QsNkNBQTZDO1lBQzdDLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLFFBQVEsRUFBRSx1QkFBVyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUM7UUFDL0QsQ0FBQztRQUNELEdBQUcsT0FBTztLQUNYLENBQUMsQ0FBQztBQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFVzZXIgUGlucyBNdXRhdGlvbiBIb29rc1xuICpcbiAqIFRhblN0YWNrIFF1ZXJ5IG11dGF0aW9uIGhvb2tzIGZvciB1c2VyIHBpbiBvcGVyYXRpb25zLlxuICogVGhlc2UgaG9va3MgaGFuZGxlIGNyZWF0aW5nIGFuZCBkZWxldGluZyB1c2VyIHBpbnNcbiAqIChcIkknbSBoZXJlIG5vd1wiIC8gXCJJJ2xsIGJlIHRoZXJlXCIpLlxuICpcbiAqIEBtb2R1bGUgYXBpL211dGF0aW9ucy91c2VyLXBpbnNcbiAqL1xuXG5pbXBvcnQge1xuICB1c2VNdXRhdGlvbixcbiAgVXNlTXV0YXRpb25PcHRpb25zLFxuICBVc2VNdXRhdGlvblJlc3VsdCxcbiAgdXNlUXVlcnlDbGllbnQsXG59IGZyb20gJ0B0YW5zdGFjay9yZWFjdC1xdWVyeSc7XG5pbXBvcnQgeyBnZXRBcGlDbGllbnQgfSBmcm9tICcuLi9jbGllbnQnO1xuaW1wb3J0IHsgdXNlclBpbktleXMsIFVzZXJQaW5SZXNwb25zZSwgUGluVHlwZSB9IGZyb20gJy4uL3F1ZXJpZXMvdXNlci1waW5zJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gUkVRVUVTVCBUWVBFU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgaW50ZXJmYWNlIENyZWF0ZVBpblJlcXVlc3Qge1xuICB0eXBlOiBQaW5UeXBlO1xuICBsYXRpdHVkZTogbnVtYmVyO1xuICBsb25naXR1ZGU6IG51bWJlcjtcbiAgbG9jYXRpb25OYW1lPzogc3RyaW5nO1xuICBhY3Rpdml0eTogc3RyaW5nO1xuICB2ZW51ZUlkPzogc3RyaW5nO1xuICBzY2hlZHVsZWRUaW1lPzogc3RyaW5nOyAvLyBJU08gZGF0ZSBzdHJpbmcsIHJlcXVpcmVkIGZvciBTQ0hFRFVMRUQgdHlwZVxuICBkdXJhdGlvbj86IG51bWJlcjsgLy8gaG91cnMgKDEtOCksIGRlZmF1bHQgMiBmb3IgSEVSRV9OT1csIDIgZm9yIFNDSEVEVUxFRFxufVxuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBNVVRBVElPTiBIT09LU1xuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4vKipcbiAqIENyZWF0ZSBhIG5ldyB1c2VyIHBpblxuICpcbiAqIEBkZXNjcmlwdGlvblxuICogQ3JlYXRlcyBhIG5ldyBwaW4gc2hvd2luZyB3aGVyZSB0aGUgdXNlciBpcyBvciB3aWxsIGJlLlxuICogQW55IGV4aXN0aW5nIGFjdGl2ZSBwaW4gaXMgYXV0b21hdGljYWxseSBkZWFjdGl2YXRlZC5cbiAqXG4gKiBUd28gdHlwZXMgb2YgcGluczpcbiAqIC0gSEVSRV9OT1c6IFVzZXIgaXMgY3VycmVudGx5IGF0IHRoaXMgbG9jYXRpb24gKGRlZmF1bHQgMSBob3VyIGR1cmF0aW9uKVxuICogLSBTQ0hFRFVMRUQ6IFVzZXIgd2lsbCBiZSBhdCB0aGlzIGxvY2F0aW9uIGF0IGEgc3BlY2lmaWMgdGltZSAoZGVmYXVsdCAyIGhvdXIgZHVyYXRpb24pXG4gKlxuICogQGVuZHBvaW50IFBPU1QgL2FwaS92MS91c2VyLXBpbnNcbiAqXG4gKiBAZXhhbXBsZVxuICogRHJvcCBhIFwiSSdtIGhlcmUgbm93XCIgcGluOlxuICogYGBgdHN4XG4gKiBpbXBvcnQgeyB1c2VDcmVhdGVVc2VyUGluIH0gZnJvbSAnQGdyb3dzb2Jlci9zZGsnO1xuICpcbiAqIGZ1bmN0aW9uIERyb3BQaW5CdXR0b24oeyBsb2NhdGlvbiB9KSB7XG4gKiAgIGNvbnN0IGNyZWF0ZVBpbiA9IHVzZUNyZWF0ZVVzZXJQaW4oKTtcbiAqXG4gKiAgIGNvbnN0IGhhbmRsZURyb3BQaW4gPSAoKSA9PiB7XG4gKiAgICAgY3JlYXRlUGluLm11dGF0ZSh7XG4gKiAgICAgICB0eXBlOiAnSEVSRV9OT1cnLFxuICogICAgICAgbGF0aXR1ZGU6IGxvY2F0aW9uLmxhdCxcbiAqICAgICAgIGxvbmdpdHVkZTogbG9jYXRpb24ubG5nLFxuICogICAgICAgbG9jYXRpb25OYW1lOiAnRmFicmljYSBDb2ZmZWUnLFxuICogICAgICAgYWN0aXZpdHk6ICdXb3JraW5nIHJlbW90ZWx5JyxcbiAqICAgICAgIGR1cmF0aW9uOiAxMjAsIC8vIDIgaG91cnNcbiAqICAgICB9KTtcbiAqICAgfTtcbiAqXG4gKiAgIHJldHVybiAoXG4gKiAgICAgPEJ1dHRvbiBvblByZXNzPXtoYW5kbGVEcm9wUGlufSBsb2FkaW5nPXtjcmVhdGVQaW4uaXNQZW5kaW5nfT5cbiAqICAgICAgIERyb3AgUGluXG4gKiAgICAgPC9CdXR0b24+XG4gKiAgICk7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBAZXhhbXBsZVxuICogU2NoZWR1bGUgYSBwaW4gZm9yIGxhdGVyOlxuICogYGBgdHN4XG4gKiBpbXBvcnQgeyB1c2VDcmVhdGVVc2VyUGluIH0gZnJvbSAnQGdyb3dzb2Jlci9zZGsnO1xuICpcbiAqIGZ1bmN0aW9uIFNjaGVkdWxlUGluRm9ybSgpIHtcbiAqICAgY29uc3QgY3JlYXRlUGluID0gdXNlQ3JlYXRlVXNlclBpbih7XG4gKiAgICAgb25TdWNjZXNzOiAoKSA9PiB7XG4gKiAgICAgICB0b2FzdC5zaG93KCdQaW4gc2NoZWR1bGVkIScpO1xuICogICAgIH0sXG4gKiAgIH0pO1xuICpcbiAqICAgY29uc3QgaGFuZGxlU2NoZWR1bGUgPSAoZGF0YSkgPT4ge1xuICogICAgIGNyZWF0ZVBpbi5tdXRhdGUoe1xuICogICAgICAgdHlwZTogJ1NDSEVEVUxFRCcsXG4gKiAgICAgICBsYXRpdHVkZTogZGF0YS52ZW51ZS5sYXQsXG4gKiAgICAgICBsb25naXR1ZGU6IGRhdGEudmVudWUubG5nLFxuICogICAgICAgbG9jYXRpb25OYW1lOiBkYXRhLnZlbnVlLm5hbWUsXG4gKiAgICAgICB2ZW51ZUlkOiBkYXRhLnZlbnVlLmlkLFxuICogICAgICAgYWN0aXZpdHk6ICdDb2ZmZWUgbWVldHVwJyxcbiAqICAgICAgIHNjaGVkdWxlZFRpbWU6IGRhdGEudGltZS50b0lTT1N0cmluZygpLFxuICogICAgICAgZHVyYXRpb246IDkwLFxuICogICAgIH0pO1xuICogICB9O1xuICpcbiAqICAgcmV0dXJuIDxTY2hlZHVsZUZvcm0gb25TdWJtaXQ9e2hhbmRsZVNjaGVkdWxlfSAvPjtcbiAqIH1cbiAqIGBgYFxuICpcbiAqIEBwYXJhbSBvcHRpb25zIC0gVGFuU3RhY2sgUXVlcnkgbXV0YXRpb24gb3B0aW9uc1xuICogQHJldHVybnMgVGFuU3RhY2sgUXVlcnkgbXV0YXRpb24gcmVzdWx0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1c2VDcmVhdGVVc2VyUGluKFxuICBvcHRpb25zPzogT21pdDxcbiAgICBVc2VNdXRhdGlvbk9wdGlvbnM8VXNlclBpblJlc3BvbnNlLCBFcnJvciwgQ3JlYXRlUGluUmVxdWVzdD4sXG4gICAgJ211dGF0aW9uRm4nXG4gID5cbik6IFVzZU11dGF0aW9uUmVzdWx0PFVzZXJQaW5SZXNwb25zZSwgRXJyb3IsIENyZWF0ZVBpblJlcXVlc3Q+IHtcbiAgY29uc3QgcXVlcnlDbGllbnQgPSB1c2VRdWVyeUNsaWVudCgpO1xuXG4gIHJldHVybiB1c2VNdXRhdGlvbih7XG4gICAgbXV0YXRpb25GbjogYXN5bmMgKGRhdGE6IENyZWF0ZVBpblJlcXVlc3QpOiBQcm9taXNlPFVzZXJQaW5SZXNwb25zZT4gPT4ge1xuICAgICAgY29uc3QgY2xpZW50ID0gZ2V0QXBpQ2xpZW50KCk7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5wb3N0PFVzZXJQaW5SZXNwb25zZT4oJy9hcGkvdjEvdXNlci1waW5zJywgZGF0YSk7XG4gICAgICByZXR1cm4gcmVzcG9uc2UuZGF0YTtcbiAgICB9LFxuICAgIG9uU3VjY2VzczogKCkgPT4ge1xuICAgICAgLy8gSW52YWxpZGF0ZSBwaW4gcXVlcmllcyB0byByZWZsZWN0IG5ldyBwaW5cbiAgICAgIHF1ZXJ5Q2xpZW50LmludmFsaWRhdGVRdWVyaWVzKHsgcXVlcnlLZXk6IHVzZXJQaW5LZXlzLmFsbCB9KTtcbiAgICB9LFxuICAgIC4uLm9wdGlvbnMsXG4gIH0pO1xufVxuXG4vKipcbiAqIERlbGV0ZSBhIHVzZXIgcGluXG4gKlxuICogQGRlc2NyaXB0aW9uXG4gKiBEZWFjdGl2YXRlcyBhIHVzZXIgcGluLiBVc2VycyBjYW4gb25seSBkZWxldGUgdGhlaXIgb3duIHBpbnMuXG4gKlxuICogQGVuZHBvaW50IERFTEVURSAvYXBpL3YxL3VzZXItcGlucy86aWRcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHN4XG4gKiBpbXBvcnQgeyB1c2VEZWxldGVVc2VyUGluIH0gZnJvbSAnQGdyb3dzb2Jlci9zZGsnO1xuICpcbiAqIGZ1bmN0aW9uIE15UGluQ2FyZCh7IHBpbiB9KSB7XG4gKiAgIGNvbnN0IGRlbGV0ZVBpbiA9IHVzZURlbGV0ZVVzZXJQaW4oe1xuICogICAgIG9uU3VjY2VzczogKCkgPT4ge1xuICogICAgICAgdG9hc3Quc2hvdygnUGluIHJlbW92ZWQnKTtcbiAqICAgICB9LFxuICogICB9KTtcbiAqXG4gKiAgIHJldHVybiAoXG4gKiAgICAgPENhcmQ+XG4gKiAgICAgICA8VGV4dD57cGluLmFjdGl2aXR5fTwvVGV4dD5cbiAqICAgICAgIDxUZXh0PkF0IHtwaW4ubG9jYXRpb25OYW1lfTwvVGV4dD5cbiAqICAgICAgIDxCdXR0b25cbiAqICAgICAgICAgb25QcmVzcz17KCkgPT4gZGVsZXRlUGluLm11dGF0ZShwaW4uaWQpfVxuICogICAgICAgICBsb2FkaW5nPXtkZWxldGVQaW4uaXNQZW5kaW5nfVxuICogICAgICAgPlxuICogICAgICAgICBSZW1vdmUgUGluXG4gKiAgICAgICA8L0J1dHRvbj5cbiAqICAgICA8L0NhcmQ+XG4gKiAgICk7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFRhblN0YWNrIFF1ZXJ5IG11dGF0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIFRhblN0YWNrIFF1ZXJ5IG11dGF0aW9uIHJlc3VsdFxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlRGVsZXRlVXNlclBpbihcbiAgb3B0aW9ucz86IE9taXQ8VXNlTXV0YXRpb25PcHRpb25zPHZvaWQsIEVycm9yLCBzdHJpbmc+LCAnbXV0YXRpb25Gbic+XG4pOiBVc2VNdXRhdGlvblJlc3VsdDx2b2lkLCBFcnJvciwgc3RyaW5nPiB7XG4gIGNvbnN0IHF1ZXJ5Q2xpZW50ID0gdXNlUXVlcnlDbGllbnQoKTtcblxuICByZXR1cm4gdXNlTXV0YXRpb24oe1xuICAgIG11dGF0aW9uRm46IGFzeW5jIChwaW5JZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgICBjb25zdCBjbGllbnQgPSBnZXRBcGlDbGllbnQoKTtcbiAgICAgIGF3YWl0IGNsaWVudC5kZWxldGUoYC9hcGkvdjEvdXNlci1waW5zLyR7cGluSWR9YCk7XG4gICAgfSxcbiAgICBvblN1Y2Nlc3M6ICgpID0+IHtcbiAgICAgIC8vIEludmFsaWRhdGUgcGluIHF1ZXJpZXMgdG8gcmVmbGVjdCBkZWxldGlvblxuICAgICAgcXVlcnlDbGllbnQuaW52YWxpZGF0ZVF1ZXJpZXMoeyBxdWVyeUtleTogdXNlclBpbktleXMuYWxsIH0pO1xuICAgIH0sXG4gICAgLi4ub3B0aW9ucyxcbiAgfSk7XG59XG4iXX0=
|
|
@@ -38,4 +38,5 @@ __exportStar(require("./ambassadors"), exports);
|
|
|
38
38
|
__exportStar(require("./grow90"), exports);
|
|
39
39
|
__exportStar(require("./matching"), exports);
|
|
40
40
|
__exportStar(require("./event-chat"), exports);
|
|
41
|
-
|
|
41
|
+
__exportStar(require("./user-pins"), exports);
|
|
42
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL3F1ZXJpZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFSCwwQ0FBd0I7QUFDeEIseUNBQXVCO0FBQ3ZCLDZDQUEyQjtBQUMzQiwrQ0FBNkI7QUFDN0IsMkNBQXlCO0FBQ3pCLDJDQUF5QjtBQUN6Qix5Q0FBdUI7QUFDdkIsNENBQTBCO0FBQzFCLHdDQUFzQjtBQUN0QixrREFBZ0M7QUFDaEMsMkNBQXlCO0FBQ3pCLGtEQUFnQztBQUNoQyw0Q0FBMEI7QUFDMUIsMENBQXdCO0FBQ3hCLHlDQUF1QjtBQUN2QixnREFBOEI7QUFDOUIsMkNBQXlCO0FBQ3pCLDZDQUEyQjtBQUMzQiwrQ0FBNkI7QUFDN0IsOENBQTRCIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBRdWVyeSBIb29rc1xuICpcbiAqIFJlLWV4cG9ydHMgYWxsIHF1ZXJ5IGhvb2tzIGZvciBBUEkgZW5kcG9pbnRzLlxuICovXG5cbmV4cG9ydCAqIGZyb20gJy4vYWRtaW4nO1xuZXhwb3J0ICogZnJvbSAnLi9hdXRoJztcbmV4cG9ydCAqIGZyb20gJy4vYm9va2luZ3MnO1xuZXhwb3J0ICogZnJvbSAnLi9idXNpbmVzc2VzJztcbmV4cG9ydCAqIGZyb20gJy4vY2l0aWVzJztcbmV4cG9ydCAqIGZyb20gJy4vZXZlbnRzJztcbmV4cG9ydCAqIGZyb20gJy4vaHVicyc7XG5leHBvcnQgKiBmcm9tICcuL2xpYnJhcnknO1xuZXhwb3J0ICogZnJvbSAnLi9tYXAnO1xuZXhwb3J0ICogZnJvbSAnLi9ub3RpZmljYXRpb25zJztcbmV4cG9ydCAqIGZyb20gJy4vb2ZmZXJzJztcbmV4cG9ydCAqIGZyb20gJy4vc3Vic2NyaXB0aW9ucyc7XG5leHBvcnQgKiBmcm9tICcuL3N1cHBvcnQnO1xuZXhwb3J0ICogZnJvbSAnLi91c2Vycyc7XG5leHBvcnQgKiBmcm9tICcuL2phY2snO1xuZXhwb3J0ICogZnJvbSAnLi9hbWJhc3NhZG9ycyc7XG5leHBvcnQgKiBmcm9tICcuL2dyb3c5MCc7XG5leHBvcnQgKiBmcm9tICcuL21hdGNoaW5nJztcbmV4cG9ydCAqIGZyb20gJy4vZXZlbnQtY2hhdCc7XG5leHBvcnQgKiBmcm9tICcuL3VzZXItcGlucyc7XG4iXX0=
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Pins Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for user pin read operations.
|
|
5
|
+
* These hooks handle fetching user pins for the map feature
|
|
6
|
+
* ("I'm here now" / "I'll be there").
|
|
7
|
+
*
|
|
8
|
+
* @module api/queries/user-pins
|
|
9
|
+
*/
|
|
10
|
+
import { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
11
|
+
export type PinType = 'HERE_NOW' | 'SCHEDULED';
|
|
12
|
+
export interface UserPinResponse {
|
|
13
|
+
id: string;
|
|
14
|
+
userId: string;
|
|
15
|
+
type: PinType;
|
|
16
|
+
latitude: number;
|
|
17
|
+
longitude: number;
|
|
18
|
+
locationName: string | null;
|
|
19
|
+
activity: string;
|
|
20
|
+
venueId: string | null;
|
|
21
|
+
scheduledTime: string | null;
|
|
22
|
+
expiresAt: string;
|
|
23
|
+
duration: number | null;
|
|
24
|
+
isActive: boolean;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
user?: {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string | null;
|
|
29
|
+
profileImage: string | null;
|
|
30
|
+
};
|
|
31
|
+
venue?: {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
slug: string | null;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface NearbyPinsResponse {
|
|
38
|
+
pins: UserPinResponse[];
|
|
39
|
+
total: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Query key factory for user pin queries
|
|
43
|
+
*/
|
|
44
|
+
export declare const userPinKeys: {
|
|
45
|
+
all: readonly ["user-pins"];
|
|
46
|
+
nearby: (params: NearbyPinsParams) => readonly ["user-pins", "nearby", NearbyPinsParams];
|
|
47
|
+
me: () => readonly ["user-pins", "me"];
|
|
48
|
+
};
|
|
49
|
+
export interface NearbyPinsParams {
|
|
50
|
+
lat: number;
|
|
51
|
+
lng: number;
|
|
52
|
+
radius?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get nearby user pins
|
|
56
|
+
*
|
|
57
|
+
* @description
|
|
58
|
+
* Retrieves active user pins within a radius of a location.
|
|
59
|
+
* Shows where other GrowSober members are or will be.
|
|
60
|
+
*
|
|
61
|
+
* @endpoint GET /api/v1/user-pins/nearby
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* import { useNearbyPins } from '@growsober/sdk';
|
|
66
|
+
*
|
|
67
|
+
* function MapWithPins() {
|
|
68
|
+
* const { data, isLoading } = useNearbyPins({
|
|
69
|
+
* lat: 38.7097,
|
|
70
|
+
* lng: -9.1367,
|
|
71
|
+
* radius: 5, // 5km radius
|
|
72
|
+
* });
|
|
73
|
+
*
|
|
74
|
+
* return (
|
|
75
|
+
* <Map>
|
|
76
|
+
* {data?.pins.map(pin => (
|
|
77
|
+
* <Marker
|
|
78
|
+
* key={pin.id}
|
|
79
|
+
* position={[pin.latitude, pin.longitude]}
|
|
80
|
+
* icon={pin.type === 'HERE_NOW' ? 'here' : 'scheduled'}
|
|
81
|
+
* >
|
|
82
|
+
* <Popup>
|
|
83
|
+
* <p>{pin.user?.name}</p>
|
|
84
|
+
* <p>{pin.activity}</p>
|
|
85
|
+
* </Popup>
|
|
86
|
+
* </Marker>
|
|
87
|
+
* ))}
|
|
88
|
+
* </Map>
|
|
89
|
+
* );
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @param params - Location and radius parameters
|
|
94
|
+
* @param options - TanStack Query options
|
|
95
|
+
* @returns TanStack Query result with nearby pins
|
|
96
|
+
*/
|
|
97
|
+
export declare function useNearbyPins(params: NearbyPinsParams, options?: Omit<UseQueryOptions<NearbyPinsResponse>, 'queryKey' | 'queryFn'>): UseQueryResult<NearbyPinsResponse>;
|
|
98
|
+
/**
|
|
99
|
+
* Get current user's active pins
|
|
100
|
+
*
|
|
101
|
+
* @description
|
|
102
|
+
* Retrieves the authenticated user's active pins.
|
|
103
|
+
* Users can have at most one active pin at a time.
|
|
104
|
+
*
|
|
105
|
+
* @endpoint GET /api/v1/user-pins/me
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```tsx
|
|
109
|
+
* import { useMyPins } from '@growsober/sdk';
|
|
110
|
+
*
|
|
111
|
+
* function MyPinStatus() {
|
|
112
|
+
* const { data: pins, isLoading } = useMyPins();
|
|
113
|
+
*
|
|
114
|
+
* if (isLoading) return <Spinner />;
|
|
115
|
+
*
|
|
116
|
+
* if (!pins?.length) {
|
|
117
|
+
* return <Text>You haven't dropped a pin yet</Text>;
|
|
118
|
+
* }
|
|
119
|
+
*
|
|
120
|
+
* const pin = pins[0];
|
|
121
|
+
* return (
|
|
122
|
+
* <View>
|
|
123
|
+
* <Text>You're at {pin.locationName}</Text>
|
|
124
|
+
* <Text>Activity: {pin.activity}</Text>
|
|
125
|
+
* <Text>Expires: {formatTime(pin.expiresAt)}</Text>
|
|
126
|
+
* </View>
|
|
127
|
+
* );
|
|
128
|
+
* }
|
|
129
|
+
* ```
|
|
130
|
+
*
|
|
131
|
+
* @param options - TanStack Query options
|
|
132
|
+
* @returns TanStack Query result with user's active pins
|
|
133
|
+
*/
|
|
134
|
+
export declare function useMyPins(options?: Omit<UseQueryOptions<UserPinResponse[]>, 'queryKey' | 'queryFn'>): UseQueryResult<UserPinResponse[]>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* User Pins Query Hooks
|
|
4
|
+
*
|
|
5
|
+
* TanStack Query hooks for user pin read operations.
|
|
6
|
+
* These hooks handle fetching user pins for the map feature
|
|
7
|
+
* ("I'm here now" / "I'll be there").
|
|
8
|
+
*
|
|
9
|
+
* @module api/queries/user-pins
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.userPinKeys = void 0;
|
|
13
|
+
exports.useNearbyPins = useNearbyPins;
|
|
14
|
+
exports.useMyPins = useMyPins;
|
|
15
|
+
const react_query_1 = require("@tanstack/react-query");
|
|
16
|
+
const client_1 = require("../client");
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// QUERY KEYS
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Query key factory for user pin queries
|
|
22
|
+
*/
|
|
23
|
+
exports.userPinKeys = {
|
|
24
|
+
all: ['user-pins'],
|
|
25
|
+
nearby: (params) => [...exports.userPinKeys.all, 'nearby', params],
|
|
26
|
+
me: () => [...exports.userPinKeys.all, 'me'],
|
|
27
|
+
};
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// QUERY HOOKS
|
|
30
|
+
// ============================================================================
|
|
31
|
+
/**
|
|
32
|
+
* Get nearby user pins
|
|
33
|
+
*
|
|
34
|
+
* @description
|
|
35
|
+
* Retrieves active user pins within a radius of a location.
|
|
36
|
+
* Shows where other GrowSober members are or will be.
|
|
37
|
+
*
|
|
38
|
+
* @endpoint GET /api/v1/user-pins/nearby
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* import { useNearbyPins } from '@growsober/sdk';
|
|
43
|
+
*
|
|
44
|
+
* function MapWithPins() {
|
|
45
|
+
* const { data, isLoading } = useNearbyPins({
|
|
46
|
+
* lat: 38.7097,
|
|
47
|
+
* lng: -9.1367,
|
|
48
|
+
* radius: 5, // 5km radius
|
|
49
|
+
* });
|
|
50
|
+
*
|
|
51
|
+
* return (
|
|
52
|
+
* <Map>
|
|
53
|
+
* {data?.pins.map(pin => (
|
|
54
|
+
* <Marker
|
|
55
|
+
* key={pin.id}
|
|
56
|
+
* position={[pin.latitude, pin.longitude]}
|
|
57
|
+
* icon={pin.type === 'HERE_NOW' ? 'here' : 'scheduled'}
|
|
58
|
+
* >
|
|
59
|
+
* <Popup>
|
|
60
|
+
* <p>{pin.user?.name}</p>
|
|
61
|
+
* <p>{pin.activity}</p>
|
|
62
|
+
* </Popup>
|
|
63
|
+
* </Marker>
|
|
64
|
+
* ))}
|
|
65
|
+
* </Map>
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @param params - Location and radius parameters
|
|
71
|
+
* @param options - TanStack Query options
|
|
72
|
+
* @returns TanStack Query result with nearby pins
|
|
73
|
+
*/
|
|
74
|
+
function useNearbyPins(params, options) {
|
|
75
|
+
return (0, react_query_1.useQuery)({
|
|
76
|
+
queryKey: exports.userPinKeys.nearby(params),
|
|
77
|
+
queryFn: async () => {
|
|
78
|
+
const client = (0, client_1.getApiClient)();
|
|
79
|
+
const response = await client.get('/api/v1/user-pins/nearby', {
|
|
80
|
+
params: {
|
|
81
|
+
lat: params.lat,
|
|
82
|
+
lng: params.lng,
|
|
83
|
+
radius: params.radius,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
return response.data;
|
|
87
|
+
},
|
|
88
|
+
enabled: params.lat !== undefined && params.lng !== undefined,
|
|
89
|
+
staleTime: 30 * 1000, // 30 seconds - pins change frequently
|
|
90
|
+
...options,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get current user's active pins
|
|
95
|
+
*
|
|
96
|
+
* @description
|
|
97
|
+
* Retrieves the authenticated user's active pins.
|
|
98
|
+
* Users can have at most one active pin at a time.
|
|
99
|
+
*
|
|
100
|
+
* @endpoint GET /api/v1/user-pins/me
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```tsx
|
|
104
|
+
* import { useMyPins } from '@growsober/sdk';
|
|
105
|
+
*
|
|
106
|
+
* function MyPinStatus() {
|
|
107
|
+
* const { data: pins, isLoading } = useMyPins();
|
|
108
|
+
*
|
|
109
|
+
* if (isLoading) return <Spinner />;
|
|
110
|
+
*
|
|
111
|
+
* if (!pins?.length) {
|
|
112
|
+
* return <Text>You haven't dropped a pin yet</Text>;
|
|
113
|
+
* }
|
|
114
|
+
*
|
|
115
|
+
* const pin = pins[0];
|
|
116
|
+
* return (
|
|
117
|
+
* <View>
|
|
118
|
+
* <Text>You're at {pin.locationName}</Text>
|
|
119
|
+
* <Text>Activity: {pin.activity}</Text>
|
|
120
|
+
* <Text>Expires: {formatTime(pin.expiresAt)}</Text>
|
|
121
|
+
* </View>
|
|
122
|
+
* );
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @param options - TanStack Query options
|
|
127
|
+
* @returns TanStack Query result with user's active pins
|
|
128
|
+
*/
|
|
129
|
+
function useMyPins(options) {
|
|
130
|
+
return (0, react_query_1.useQuery)({
|
|
131
|
+
queryKey: exports.userPinKeys.me(),
|
|
132
|
+
queryFn: async () => {
|
|
133
|
+
const client = (0, client_1.getApiClient)();
|
|
134
|
+
const response = await client.get('/api/v1/user-pins/me');
|
|
135
|
+
return response.data;
|
|
136
|
+
},
|
|
137
|
+
staleTime: 60 * 1000, // 1 minute
|
|
138
|
+
...options,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci1waW5zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9xdWVyaWVzL3VzZXItcGlucy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7O0dBUUc7OztBQWdISCxzQ0FxQkM7QUFzQ0QsOEJBYUM7QUF0TEQsdURBQWtGO0FBQ2xGLHNDQUF5QztBQXVDekMsK0VBQStFO0FBQy9FLGFBQWE7QUFDYiwrRUFBK0U7QUFFL0U7O0dBRUc7QUFDVSxRQUFBLFdBQVcsR0FBRztJQUN6QixHQUFHLEVBQUUsQ0FBQyxXQUFXLENBQVU7SUFDM0IsTUFBTSxFQUFFLENBQUMsTUFBd0IsRUFBRSxFQUFFLENBQUMsQ0FBQyxHQUFHLG1CQUFXLENBQUMsR0FBRyxFQUFFLFFBQVEsRUFBRSxNQUFNLENBQVU7SUFDckYsRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUMsR0FBRyxtQkFBVyxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQVU7Q0FDOUMsQ0FBQztBQVlGLCtFQUErRTtBQUMvRSxjQUFjO0FBQ2QsK0VBQStFO0FBRS9FOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EwQ0c7QUFDSCxTQUFnQixhQUFhLENBQzNCLE1BQXdCLEVBQ3hCLE9BQTJFO0lBRTNFLE9BQU8sSUFBQSxzQkFBUSxFQUFDO1FBQ2QsUUFBUSxFQUFFLG1CQUFXLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUNwQyxPQUFPLEVBQUUsS0FBSyxJQUFpQyxFQUFFO1lBQy9DLE1BQU0sTUFBTSxHQUFHLElBQUEscUJBQVksR0FBRSxDQUFDO1lBQzlCLE1BQU0sUUFBUSxHQUFHLE1BQU0sTUFBTSxDQUFDLEdBQUcsQ0FBcUIsMEJBQTBCLEVBQUU7Z0JBQ2hGLE1BQU0sRUFBRTtvQkFDTixHQUFHLEVBQUUsTUFBTSxDQUFDLEdBQUc7b0JBQ2YsR0FBRyxFQUFFLE1BQU0sQ0FBQyxHQUFHO29CQUNmLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtpQkFDdEI7YUFDRixDQUFDLENBQUM7WUFDSCxPQUFPLFFBQVEsQ0FBQyxJQUFJLENBQUM7UUFDdkIsQ0FBQztRQUNELE9BQU8sRUFBRSxNQUFNLENBQUMsR0FBRyxLQUFLLFNBQVMsSUFBSSxNQUFNLENBQUMsR0FBRyxLQUFLLFNBQVM7UUFDN0QsU0FBUyxFQUFFLEVBQUUsR0FBRyxJQUFJLEVBQUUsc0NBQXNDO1FBQzVELEdBQUcsT0FBTztLQUNYLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQ0c7QUFDSCxTQUFnQixTQUFTLENBQ3ZCLE9BQTBFO0lBRTFFLE9BQU8sSUFBQSxzQkFBUSxFQUFDO1FBQ2QsUUFBUSxFQUFFLG1CQUFXLENBQUMsRUFBRSxFQUFFO1FBQzFCLE9BQU8sRUFBRSxLQUFLLElBQWdDLEVBQUU7WUFDOUMsTUFBTSxNQUFNLEdBQUcsSUFBQSxxQkFBWSxHQUFFLENBQUM7WUFDOUIsTUFBTSxRQUFRLEdBQUcsTUFBTSxNQUFNLENBQUMsR0FBRyxDQUFvQixzQkFBc0IsQ0FBQyxDQUFDO1lBQzdFLE9BQU8sUUFBUSxDQUFDLElBQUksQ0FBQztRQUN2QixDQUFDO1FBQ0QsU0FBUyxFQUFFLEVBQUUsR0FBRyxJQUFJLEVBQUUsV0FBVztRQUNqQyxHQUFHLE9BQU87S0FDWCxDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBVc2VyIFBpbnMgUXVlcnkgSG9va3NcbiAqXG4gKiBUYW5TdGFjayBRdWVyeSBob29rcyBmb3IgdXNlciBwaW4gcmVhZCBvcGVyYXRpb25zLlxuICogVGhlc2UgaG9va3MgaGFuZGxlIGZldGNoaW5nIHVzZXIgcGlucyBmb3IgdGhlIG1hcCBmZWF0dXJlXG4gKiAoXCJJJ20gaGVyZSBub3dcIiAvIFwiSSdsbCBiZSB0aGVyZVwiKS5cbiAqXG4gKiBAbW9kdWxlIGFwaS9xdWVyaWVzL3VzZXItcGluc1xuICovXG5cbmltcG9ydCB7IHVzZVF1ZXJ5LCBVc2VRdWVyeU9wdGlvbnMsIFVzZVF1ZXJ5UmVzdWx0IH0gZnJvbSAnQHRhbnN0YWNrL3JlYWN0LXF1ZXJ5JztcbmltcG9ydCB7IGdldEFwaUNsaWVudCB9IGZyb20gJy4uL2NsaWVudCc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFRZUEVTXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCB0eXBlIFBpblR5cGUgPSAnSEVSRV9OT1cnIHwgJ1NDSEVEVUxFRCc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVXNlclBpblJlc3BvbnNlIHtcbiAgaWQ6IHN0cmluZztcbiAgdXNlcklkOiBzdHJpbmc7XG4gIHR5cGU6IFBpblR5cGU7XG4gIGxhdGl0dWRlOiBudW1iZXI7XG4gIGxvbmdpdHVkZTogbnVtYmVyO1xuICBsb2NhdGlvbk5hbWU6IHN0cmluZyB8IG51bGw7XG4gIGFjdGl2aXR5OiBzdHJpbmc7XG4gIHZlbnVlSWQ6IHN0cmluZyB8IG51bGw7XG4gIHNjaGVkdWxlZFRpbWU6IHN0cmluZyB8IG51bGw7XG4gIGV4cGlyZXNBdDogc3RyaW5nO1xuICBkdXJhdGlvbjogbnVtYmVyIHwgbnVsbDtcbiAgaXNBY3RpdmU6IGJvb2xlYW47XG4gIGNyZWF0ZWRBdDogc3RyaW5nO1xuICB1c2VyPzoge1xuICAgIGlkOiBzdHJpbmc7XG4gICAgbmFtZTogc3RyaW5nIHwgbnVsbDtcbiAgICBwcm9maWxlSW1hZ2U6IHN0cmluZyB8IG51bGw7XG4gIH07XG4gIHZlbnVlPzoge1xuICAgIGlkOiBzdHJpbmc7XG4gICAgbmFtZTogc3RyaW5nO1xuICAgIHNsdWc6IHN0cmluZyB8IG51bGw7XG4gIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTmVhcmJ5UGluc1Jlc3BvbnNlIHtcbiAgcGluczogVXNlclBpblJlc3BvbnNlW107XG4gIHRvdGFsOiBudW1iZXI7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFFVRVJZIEtFWVNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBRdWVyeSBrZXkgZmFjdG9yeSBmb3IgdXNlciBwaW4gcXVlcmllc1xuICovXG5leHBvcnQgY29uc3QgdXNlclBpbktleXMgPSB7XG4gIGFsbDogWyd1c2VyLXBpbnMnXSBhcyBjb25zdCxcbiAgbmVhcmJ5OiAocGFyYW1zOiBOZWFyYnlQaW5zUGFyYW1zKSA9PiBbLi4udXNlclBpbktleXMuYWxsLCAnbmVhcmJ5JywgcGFyYW1zXSBhcyBjb25zdCxcbiAgbWU6ICgpID0+IFsuLi51c2VyUGluS2V5cy5hbGwsICdtZSddIGFzIGNvbnN0LFxufTtcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gUEFSQU0gVFlQRVNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IGludGVyZmFjZSBOZWFyYnlQaW5zUGFyYW1zIHtcbiAgbGF0OiBudW1iZXI7XG4gIGxuZzogbnVtYmVyO1xuICByYWRpdXM/OiBudW1iZXI7IC8vIGttLCBkZWZhdWx0IDVcbn1cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gUVVFUlkgSE9PS1Ncbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuLyoqXG4gKiBHZXQgbmVhcmJ5IHVzZXIgcGluc1xuICpcbiAqIEBkZXNjcmlwdGlvblxuICogUmV0cmlldmVzIGFjdGl2ZSB1c2VyIHBpbnMgd2l0aGluIGEgcmFkaXVzIG9mIGEgbG9jYXRpb24uXG4gKiBTaG93cyB3aGVyZSBvdGhlciBHcm93U29iZXIgbWVtYmVycyBhcmUgb3Igd2lsbCBiZS5cbiAqXG4gKiBAZW5kcG9pbnQgR0VUIC9hcGkvdjEvdXNlci1waW5zL25lYXJieVxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c3hcbiAqIGltcG9ydCB7IHVzZU5lYXJieVBpbnMgfSBmcm9tICdAZ3Jvd3NvYmVyL3Nkayc7XG4gKlxuICogZnVuY3Rpb24gTWFwV2l0aFBpbnMoKSB7XG4gKiAgIGNvbnN0IHsgZGF0YSwgaXNMb2FkaW5nIH0gPSB1c2VOZWFyYnlQaW5zKHtcbiAqICAgICBsYXQ6IDM4LjcwOTcsXG4gKiAgICAgbG5nOiAtOS4xMzY3LFxuICogICAgIHJhZGl1czogNSwgLy8gNWttIHJhZGl1c1xuICogICB9KTtcbiAqXG4gKiAgIHJldHVybiAoXG4gKiAgICAgPE1hcD5cbiAqICAgICAgIHtkYXRhPy5waW5zLm1hcChwaW4gPT4gKFxuICogICAgICAgICA8TWFya2VyXG4gKiAgICAgICAgICAga2V5PXtwaW4uaWR9XG4gKiAgICAgICAgICAgcG9zaXRpb249e1twaW4ubGF0aXR1ZGUsIHBpbi5sb25naXR1ZGVdfVxuICogICAgICAgICAgIGljb249e3Bpbi50eXBlID09PSAnSEVSRV9OT1cnID8gJ2hlcmUnIDogJ3NjaGVkdWxlZCd9XG4gKiAgICAgICAgID5cbiAqICAgICAgICAgICA8UG9wdXA+XG4gKiAgICAgICAgICAgICA8cD57cGluLnVzZXI/Lm5hbWV9PC9wPlxuICogICAgICAgICAgICAgPHA+e3Bpbi5hY3Rpdml0eX08L3A+XG4gKiAgICAgICAgICAgPC9Qb3B1cD5cbiAqICAgICAgICAgPC9NYXJrZXI+XG4gKiAgICAgICApKX1cbiAqICAgICA8L01hcD5cbiAqICAgKTtcbiAqIH1cbiAqIGBgYFxuICpcbiAqIEBwYXJhbSBwYXJhbXMgLSBMb2NhdGlvbiBhbmQgcmFkaXVzIHBhcmFtZXRlcnNcbiAqIEBwYXJhbSBvcHRpb25zIC0gVGFuU3RhY2sgUXVlcnkgb3B0aW9uc1xuICogQHJldHVybnMgVGFuU3RhY2sgUXVlcnkgcmVzdWx0IHdpdGggbmVhcmJ5IHBpbnNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZU5lYXJieVBpbnMoXG4gIHBhcmFtczogTmVhcmJ5UGluc1BhcmFtcyxcbiAgb3B0aW9ucz86IE9taXQ8VXNlUXVlcnlPcHRpb25zPE5lYXJieVBpbnNSZXNwb25zZT4sICdxdWVyeUtleScgfCAncXVlcnlGbic+XG4pOiBVc2VRdWVyeVJlc3VsdDxOZWFyYnlQaW5zUmVzcG9uc2U+IHtcbiAgcmV0dXJuIHVzZVF1ZXJ5KHtcbiAgICBxdWVyeUtleTogdXNlclBpbktleXMubmVhcmJ5KHBhcmFtcyksXG4gICAgcXVlcnlGbjogYXN5bmMgKCk6IFByb21pc2U8TmVhcmJ5UGluc1Jlc3BvbnNlPiA9PiB7XG4gICAgICBjb25zdCBjbGllbnQgPSBnZXRBcGlDbGllbnQoKTtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LmdldDxOZWFyYnlQaW5zUmVzcG9uc2U+KCcvYXBpL3YxL3VzZXItcGlucy9uZWFyYnknLCB7XG4gICAgICAgIHBhcmFtczoge1xuICAgICAgICAgIGxhdDogcGFyYW1zLmxhdCxcbiAgICAgICAgICBsbmc6IHBhcmFtcy5sbmcsXG4gICAgICAgICAgcmFkaXVzOiBwYXJhbXMucmFkaXVzLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gcmVzcG9uc2UuZGF0YTtcbiAgICB9LFxuICAgIGVuYWJsZWQ6IHBhcmFtcy5sYXQgIT09IHVuZGVmaW5lZCAmJiBwYXJhbXMubG5nICE9PSB1bmRlZmluZWQsXG4gICAgc3RhbGVUaW1lOiAzMCAqIDEwMDAsIC8vIDMwIHNlY29uZHMgLSBwaW5zIGNoYW5nZSBmcmVxdWVudGx5XG4gICAgLi4ub3B0aW9ucyxcbiAgfSk7XG59XG5cbi8qKlxuICogR2V0IGN1cnJlbnQgdXNlcidzIGFjdGl2ZSBwaW5zXG4gKlxuICogQGRlc2NyaXB0aW9uXG4gKiBSZXRyaWV2ZXMgdGhlIGF1dGhlbnRpY2F0ZWQgdXNlcidzIGFjdGl2ZSBwaW5zLlxuICogVXNlcnMgY2FuIGhhdmUgYXQgbW9zdCBvbmUgYWN0aXZlIHBpbiBhdCBhIHRpbWUuXG4gKlxuICogQGVuZHBvaW50IEdFVCAvYXBpL3YxL3VzZXItcGlucy9tZVxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c3hcbiAqIGltcG9ydCB7IHVzZU15UGlucyB9IGZyb20gJ0Bncm93c29iZXIvc2RrJztcbiAqXG4gKiBmdW5jdGlvbiBNeVBpblN0YXR1cygpIHtcbiAqICAgY29uc3QgeyBkYXRhOiBwaW5zLCBpc0xvYWRpbmcgfSA9IHVzZU15UGlucygpO1xuICpcbiAqICAgaWYgKGlzTG9hZGluZykgcmV0dXJuIDxTcGlubmVyIC8+O1xuICpcbiAqICAgaWYgKCFwaW5zPy5sZW5ndGgpIHtcbiAqICAgICByZXR1cm4gPFRleHQ+WW91IGhhdmVuJ3QgZHJvcHBlZCBhIHBpbiB5ZXQ8L1RleHQ+O1xuICogICB9XG4gKlxuICogICBjb25zdCBwaW4gPSBwaW5zWzBdO1xuICogICByZXR1cm4gKFxuICogICAgIDxWaWV3PlxuICogICAgICAgPFRleHQ+WW91J3JlIGF0IHtwaW4ubG9jYXRpb25OYW1lfTwvVGV4dD5cbiAqICAgICAgIDxUZXh0PkFjdGl2aXR5OiB7cGluLmFjdGl2aXR5fTwvVGV4dD5cbiAqICAgICAgIDxUZXh0PkV4cGlyZXM6IHtmb3JtYXRUaW1lKHBpbi5leHBpcmVzQXQpfTwvVGV4dD5cbiAqICAgICA8L1ZpZXc+XG4gKiAgICk7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFRhblN0YWNrIFF1ZXJ5IG9wdGlvbnNcbiAqIEByZXR1cm5zIFRhblN0YWNrIFF1ZXJ5IHJlc3VsdCB3aXRoIHVzZXIncyBhY3RpdmUgcGluc1xuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlTXlQaW5zKFxuICBvcHRpb25zPzogT21pdDxVc2VRdWVyeU9wdGlvbnM8VXNlclBpblJlc3BvbnNlW10+LCAncXVlcnlLZXknIHwgJ3F1ZXJ5Rm4nPlxuKTogVXNlUXVlcnlSZXN1bHQ8VXNlclBpblJlc3BvbnNlW10+IHtcbiAgcmV0dXJuIHVzZVF1ZXJ5KHtcbiAgICBxdWVyeUtleTogdXNlclBpbktleXMubWUoKSxcbiAgICBxdWVyeUZuOiBhc3luYyAoKTogUHJvbWlzZTxVc2VyUGluUmVzcG9uc2VbXT4gPT4ge1xuICAgICAgY29uc3QgY2xpZW50ID0gZ2V0QXBpQ2xpZW50KCk7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNsaWVudC5nZXQ8VXNlclBpblJlc3BvbnNlW10+KCcvYXBpL3YxL3VzZXItcGlucy9tZScpO1xuICAgICAgcmV0dXJuIHJlc3BvbnNlLmRhdGE7XG4gICAgfSxcbiAgICBzdGFsZVRpbWU6IDYwICogMTAwMCwgLy8gMSBtaW51dGVcbiAgICAuLi5vcHRpb25zLFxuICB9KTtcbn1cbiJdfQ==
|
package/package.json
CHANGED
package/src/api/client.ts
CHANGED
|
@@ -42,9 +42,16 @@ function createApiClient(sdkConfig: SDKConfig): AxiosInstance {
|
|
|
42
42
|
(error) => Promise.reject(error)
|
|
43
43
|
);
|
|
44
44
|
|
|
45
|
-
// Response interceptor - handle 401
|
|
45
|
+
// Response interceptor - unwrap API response and handle 401
|
|
46
46
|
client.interceptors.response.use(
|
|
47
|
-
(response) =>
|
|
47
|
+
(response) => {
|
|
48
|
+
// API wraps all responses in {data: ..., meta: {...}}
|
|
49
|
+
// Unwrap to return just the data portion
|
|
50
|
+
if (response.data && typeof response.data === 'object' && 'data' in response.data) {
|
|
51
|
+
response.data = response.data.data;
|
|
52
|
+
}
|
|
53
|
+
return response;
|
|
54
|
+
},
|
|
48
55
|
async (error: AxiosError) => {
|
|
49
56
|
if (error.response?.status === 401) {
|
|
50
57
|
if (sdkConfig.refreshAccessToken) {
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Pins Mutation Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query mutation hooks for user pin operations.
|
|
5
|
+
* These hooks handle creating and deleting user pins
|
|
6
|
+
* ("I'm here now" / "I'll be there").
|
|
7
|
+
*
|
|
8
|
+
* @module api/mutations/user-pins
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
useMutation,
|
|
13
|
+
UseMutationOptions,
|
|
14
|
+
UseMutationResult,
|
|
15
|
+
useQueryClient,
|
|
16
|
+
} from '@tanstack/react-query';
|
|
17
|
+
import { getApiClient } from '../client';
|
|
18
|
+
import { userPinKeys, UserPinResponse, PinType } from '../queries/user-pins';
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// REQUEST TYPES
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
export interface CreatePinRequest {
|
|
25
|
+
type: PinType;
|
|
26
|
+
latitude: number;
|
|
27
|
+
longitude: number;
|
|
28
|
+
locationName?: string;
|
|
29
|
+
activity: string;
|
|
30
|
+
venueId?: string;
|
|
31
|
+
scheduledTime?: string; // ISO date string, required for SCHEDULED type
|
|
32
|
+
duration?: number; // hours (1-8), default 2 for HERE_NOW, 2 for SCHEDULED
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// MUTATION HOOKS
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a new user pin
|
|
41
|
+
*
|
|
42
|
+
* @description
|
|
43
|
+
* Creates a new pin showing where the user is or will be.
|
|
44
|
+
* Any existing active pin is automatically deactivated.
|
|
45
|
+
*
|
|
46
|
+
* Two types of pins:
|
|
47
|
+
* - HERE_NOW: User is currently at this location (default 1 hour duration)
|
|
48
|
+
* - SCHEDULED: User will be at this location at a specific time (default 2 hour duration)
|
|
49
|
+
*
|
|
50
|
+
* @endpoint POST /api/v1/user-pins
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* Drop a "I'm here now" pin:
|
|
54
|
+
* ```tsx
|
|
55
|
+
* import { useCreateUserPin } from '@growsober/sdk';
|
|
56
|
+
*
|
|
57
|
+
* function DropPinButton({ location }) {
|
|
58
|
+
* const createPin = useCreateUserPin();
|
|
59
|
+
*
|
|
60
|
+
* const handleDropPin = () => {
|
|
61
|
+
* createPin.mutate({
|
|
62
|
+
* type: 'HERE_NOW',
|
|
63
|
+
* latitude: location.lat,
|
|
64
|
+
* longitude: location.lng,
|
|
65
|
+
* locationName: 'Fabrica Coffee',
|
|
66
|
+
* activity: 'Working remotely',
|
|
67
|
+
* duration: 120, // 2 hours
|
|
68
|
+
* });
|
|
69
|
+
* };
|
|
70
|
+
*
|
|
71
|
+
* return (
|
|
72
|
+
* <Button onPress={handleDropPin} loading={createPin.isPending}>
|
|
73
|
+
* Drop Pin
|
|
74
|
+
* </Button>
|
|
75
|
+
* );
|
|
76
|
+
* }
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* Schedule a pin for later:
|
|
81
|
+
* ```tsx
|
|
82
|
+
* import { useCreateUserPin } from '@growsober/sdk';
|
|
83
|
+
*
|
|
84
|
+
* function SchedulePinForm() {
|
|
85
|
+
* const createPin = useCreateUserPin({
|
|
86
|
+
* onSuccess: () => {
|
|
87
|
+
* toast.show('Pin scheduled!');
|
|
88
|
+
* },
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* const handleSchedule = (data) => {
|
|
92
|
+
* createPin.mutate({
|
|
93
|
+
* type: 'SCHEDULED',
|
|
94
|
+
* latitude: data.venue.lat,
|
|
95
|
+
* longitude: data.venue.lng,
|
|
96
|
+
* locationName: data.venue.name,
|
|
97
|
+
* venueId: data.venue.id,
|
|
98
|
+
* activity: 'Coffee meetup',
|
|
99
|
+
* scheduledTime: data.time.toISOString(),
|
|
100
|
+
* duration: 90,
|
|
101
|
+
* });
|
|
102
|
+
* };
|
|
103
|
+
*
|
|
104
|
+
* return <ScheduleForm onSubmit={handleSchedule} />;
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @param options - TanStack Query mutation options
|
|
109
|
+
* @returns TanStack Query mutation result
|
|
110
|
+
*/
|
|
111
|
+
export function useCreateUserPin(
|
|
112
|
+
options?: Omit<
|
|
113
|
+
UseMutationOptions<UserPinResponse, Error, CreatePinRequest>,
|
|
114
|
+
'mutationFn'
|
|
115
|
+
>
|
|
116
|
+
): UseMutationResult<UserPinResponse, Error, CreatePinRequest> {
|
|
117
|
+
const queryClient = useQueryClient();
|
|
118
|
+
|
|
119
|
+
return useMutation({
|
|
120
|
+
mutationFn: async (data: CreatePinRequest): Promise<UserPinResponse> => {
|
|
121
|
+
const client = getApiClient();
|
|
122
|
+
const response = await client.post<UserPinResponse>('/api/v1/user-pins', data);
|
|
123
|
+
return response.data;
|
|
124
|
+
},
|
|
125
|
+
onSuccess: () => {
|
|
126
|
+
// Invalidate pin queries to reflect new pin
|
|
127
|
+
queryClient.invalidateQueries({ queryKey: userPinKeys.all });
|
|
128
|
+
},
|
|
129
|
+
...options,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Delete a user pin
|
|
135
|
+
*
|
|
136
|
+
* @description
|
|
137
|
+
* Deactivates a user pin. Users can only delete their own pins.
|
|
138
|
+
*
|
|
139
|
+
* @endpoint DELETE /api/v1/user-pins/:id
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```tsx
|
|
143
|
+
* import { useDeleteUserPin } from '@growsober/sdk';
|
|
144
|
+
*
|
|
145
|
+
* function MyPinCard({ pin }) {
|
|
146
|
+
* const deletePin = useDeleteUserPin({
|
|
147
|
+
* onSuccess: () => {
|
|
148
|
+
* toast.show('Pin removed');
|
|
149
|
+
* },
|
|
150
|
+
* });
|
|
151
|
+
*
|
|
152
|
+
* return (
|
|
153
|
+
* <Card>
|
|
154
|
+
* <Text>{pin.activity}</Text>
|
|
155
|
+
* <Text>At {pin.locationName}</Text>
|
|
156
|
+
* <Button
|
|
157
|
+
* onPress={() => deletePin.mutate(pin.id)}
|
|
158
|
+
* loading={deletePin.isPending}
|
|
159
|
+
* >
|
|
160
|
+
* Remove Pin
|
|
161
|
+
* </Button>
|
|
162
|
+
* </Card>
|
|
163
|
+
* );
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* @param options - TanStack Query mutation options
|
|
168
|
+
* @returns TanStack Query mutation result
|
|
169
|
+
*/
|
|
170
|
+
export function useDeleteUserPin(
|
|
171
|
+
options?: Omit<UseMutationOptions<void, Error, string>, 'mutationFn'>
|
|
172
|
+
): UseMutationResult<void, Error, string> {
|
|
173
|
+
const queryClient = useQueryClient();
|
|
174
|
+
|
|
175
|
+
return useMutation({
|
|
176
|
+
mutationFn: async (pinId: string): Promise<void> => {
|
|
177
|
+
const client = getApiClient();
|
|
178
|
+
await client.delete(`/api/v1/user-pins/${pinId}`);
|
|
179
|
+
},
|
|
180
|
+
onSuccess: () => {
|
|
181
|
+
// Invalidate pin queries to reflect deletion
|
|
182
|
+
queryClient.invalidateQueries({ queryKey: userPinKeys.all });
|
|
183
|
+
},
|
|
184
|
+
...options,
|
|
185
|
+
});
|
|
186
|
+
}
|
package/src/api/queries/index.ts
CHANGED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Pins Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for user pin read operations.
|
|
5
|
+
* These hooks handle fetching user pins for the map feature
|
|
6
|
+
* ("I'm here now" / "I'll be there").
|
|
7
|
+
*
|
|
8
|
+
* @module api/queries/user-pins
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
12
|
+
import { getApiClient } from '../client';
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// TYPES
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
export type PinType = 'HERE_NOW' | 'SCHEDULED';
|
|
19
|
+
|
|
20
|
+
export interface UserPinResponse {
|
|
21
|
+
id: string;
|
|
22
|
+
userId: string;
|
|
23
|
+
type: PinType;
|
|
24
|
+
latitude: number;
|
|
25
|
+
longitude: number;
|
|
26
|
+
locationName: string | null;
|
|
27
|
+
activity: string;
|
|
28
|
+
venueId: string | null;
|
|
29
|
+
scheduledTime: string | null;
|
|
30
|
+
expiresAt: string;
|
|
31
|
+
duration: number | null;
|
|
32
|
+
isActive: boolean;
|
|
33
|
+
createdAt: string;
|
|
34
|
+
user?: {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string | null;
|
|
37
|
+
profileImage: string | null;
|
|
38
|
+
};
|
|
39
|
+
venue?: {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
slug: string | null;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface NearbyPinsResponse {
|
|
47
|
+
pins: UserPinResponse[];
|
|
48
|
+
total: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// QUERY KEYS
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Query key factory for user pin queries
|
|
57
|
+
*/
|
|
58
|
+
export const userPinKeys = {
|
|
59
|
+
all: ['user-pins'] as const,
|
|
60
|
+
nearby: (params: NearbyPinsParams) => [...userPinKeys.all, 'nearby', params] as const,
|
|
61
|
+
me: () => [...userPinKeys.all, 'me'] as const,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// PARAM TYPES
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
export interface NearbyPinsParams {
|
|
69
|
+
lat: number;
|
|
70
|
+
lng: number;
|
|
71
|
+
radius?: number; // km, default 5
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// QUERY HOOKS
|
|
76
|
+
// ============================================================================
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get nearby user pins
|
|
80
|
+
*
|
|
81
|
+
* @description
|
|
82
|
+
* Retrieves active user pins within a radius of a location.
|
|
83
|
+
* Shows where other GrowSober members are or will be.
|
|
84
|
+
*
|
|
85
|
+
* @endpoint GET /api/v1/user-pins/nearby
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```tsx
|
|
89
|
+
* import { useNearbyPins } from '@growsober/sdk';
|
|
90
|
+
*
|
|
91
|
+
* function MapWithPins() {
|
|
92
|
+
* const { data, isLoading } = useNearbyPins({
|
|
93
|
+
* lat: 38.7097,
|
|
94
|
+
* lng: -9.1367,
|
|
95
|
+
* radius: 5, // 5km radius
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* return (
|
|
99
|
+
* <Map>
|
|
100
|
+
* {data?.pins.map(pin => (
|
|
101
|
+
* <Marker
|
|
102
|
+
* key={pin.id}
|
|
103
|
+
* position={[pin.latitude, pin.longitude]}
|
|
104
|
+
* icon={pin.type === 'HERE_NOW' ? 'here' : 'scheduled'}
|
|
105
|
+
* >
|
|
106
|
+
* <Popup>
|
|
107
|
+
* <p>{pin.user?.name}</p>
|
|
108
|
+
* <p>{pin.activity}</p>
|
|
109
|
+
* </Popup>
|
|
110
|
+
* </Marker>
|
|
111
|
+
* ))}
|
|
112
|
+
* </Map>
|
|
113
|
+
* );
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @param params - Location and radius parameters
|
|
118
|
+
* @param options - TanStack Query options
|
|
119
|
+
* @returns TanStack Query result with nearby pins
|
|
120
|
+
*/
|
|
121
|
+
export function useNearbyPins(
|
|
122
|
+
params: NearbyPinsParams,
|
|
123
|
+
options?: Omit<UseQueryOptions<NearbyPinsResponse>, 'queryKey' | 'queryFn'>
|
|
124
|
+
): UseQueryResult<NearbyPinsResponse> {
|
|
125
|
+
return useQuery({
|
|
126
|
+
queryKey: userPinKeys.nearby(params),
|
|
127
|
+
queryFn: async (): Promise<NearbyPinsResponse> => {
|
|
128
|
+
const client = getApiClient();
|
|
129
|
+
const response = await client.get<NearbyPinsResponse>('/api/v1/user-pins/nearby', {
|
|
130
|
+
params: {
|
|
131
|
+
lat: params.lat,
|
|
132
|
+
lng: params.lng,
|
|
133
|
+
radius: params.radius,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
return response.data;
|
|
137
|
+
},
|
|
138
|
+
enabled: params.lat !== undefined && params.lng !== undefined,
|
|
139
|
+
staleTime: 30 * 1000, // 30 seconds - pins change frequently
|
|
140
|
+
...options,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get current user's active pins
|
|
146
|
+
*
|
|
147
|
+
* @description
|
|
148
|
+
* Retrieves the authenticated user's active pins.
|
|
149
|
+
* Users can have at most one active pin at a time.
|
|
150
|
+
*
|
|
151
|
+
* @endpoint GET /api/v1/user-pins/me
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```tsx
|
|
155
|
+
* import { useMyPins } from '@growsober/sdk';
|
|
156
|
+
*
|
|
157
|
+
* function MyPinStatus() {
|
|
158
|
+
* const { data: pins, isLoading } = useMyPins();
|
|
159
|
+
*
|
|
160
|
+
* if (isLoading) return <Spinner />;
|
|
161
|
+
*
|
|
162
|
+
* if (!pins?.length) {
|
|
163
|
+
* return <Text>You haven't dropped a pin yet</Text>;
|
|
164
|
+
* }
|
|
165
|
+
*
|
|
166
|
+
* const pin = pins[0];
|
|
167
|
+
* return (
|
|
168
|
+
* <View>
|
|
169
|
+
* <Text>You're at {pin.locationName}</Text>
|
|
170
|
+
* <Text>Activity: {pin.activity}</Text>
|
|
171
|
+
* <Text>Expires: {formatTime(pin.expiresAt)}</Text>
|
|
172
|
+
* </View>
|
|
173
|
+
* );
|
|
174
|
+
* }
|
|
175
|
+
* ```
|
|
176
|
+
*
|
|
177
|
+
* @param options - TanStack Query options
|
|
178
|
+
* @returns TanStack Query result with user's active pins
|
|
179
|
+
*/
|
|
180
|
+
export function useMyPins(
|
|
181
|
+
options?: Omit<UseQueryOptions<UserPinResponse[]>, 'queryKey' | 'queryFn'>
|
|
182
|
+
): UseQueryResult<UserPinResponse[]> {
|
|
183
|
+
return useQuery({
|
|
184
|
+
queryKey: userPinKeys.me(),
|
|
185
|
+
queryFn: async (): Promise<UserPinResponse[]> => {
|
|
186
|
+
const client = getApiClient();
|
|
187
|
+
const response = await client.get<UserPinResponse[]>('/api/v1/user-pins/me');
|
|
188
|
+
return response.data;
|
|
189
|
+
},
|
|
190
|
+
staleTime: 60 * 1000, // 1 minute
|
|
191
|
+
...options,
|
|
192
|
+
});
|
|
193
|
+
}
|