alpha
Login
or
Join now
dunkirk.sh
/
smokie
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
This repository has no description
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
feat: select hackatime keys
author
Kieran Klukas
date
1 year ago
(Apr 20, 2025, 2:26 PM -0400)
commit
57ef6acf
57ef6acfd82f4942711a31ca6e843dfb600d8f02
parent
f3369747
f336974701ae3de48c2c7bc36def2eb49af8a003
+133
-11
2 changed files
Expand all
Collapse all
Unified
Split
src
features
takes
handlers
settings.ts
libs
hackatime.ts
+87
-2
src/features/takes/handlers/settings.ts
Reviewed
···
3
3
import { eq } from "drizzle-orm";
4
4
import { users as usersTable } from "../../../libs/schema";
5
5
import {
6
6
+
fetchRecentProjectKeys,
6
7
getHackatimeName,
7
8
HACKATIME_VERSIONS,
8
9
type HackatimeVersion,
···
20
21
repo_link: string | undefined;
21
22
demo_link: string | undefined;
22
23
hackatime_version: string;
24
24
+
hackatime_keys: string[];
23
25
} = {
24
26
project_name: "",
25
27
project_description: "",
26
28
repo_link: undefined,
27
29
demo_link: undefined,
28
30
hackatime_version: "v2",
31
31
+
hackatime_keys: [],
29
32
};
30
33
31
34
if (prefill) {
···
45
48
repo_link: existingUser.repoLink || undefined,
46
49
demo_link: existingUser.demoLink || undefined,
47
50
hackatime_version: existingUser.hackatimeVersion,
51
51
+
hackatime_keys: existingUser.hackatimeKeys
52
52
+
? JSON.parse(existingUser.hackatimeKeys)
53
53
+
: [],
48
54
};
49
55
}
50
56
} catch (error) {
···
64
70
type: "plain_text",
65
71
text: "Submit",
66
72
},
67
67
-
clear_on_close: true,
68
73
callback_id: "takes_setup_submit",
69
74
blocks: [
70
75
{
···
179
184
})),
180
185
},
181
186
},
187
187
+
{
188
188
+
type: "input",
189
189
+
block_id: "project_keys",
190
190
+
label: {
191
191
+
type: "plain_text",
192
192
+
text: "Project Keys",
193
193
+
},
194
194
+
element: {
195
195
+
type: "multi_static_select",
196
196
+
action_id: "project_keys_input",
197
197
+
initial_options: initialValues.hackatime_keys.map(
198
198
+
(key) => ({
199
199
+
text: {
200
200
+
type: "plain_text",
201
201
+
text: key,
202
202
+
},
203
203
+
value: key,
204
204
+
}),
205
205
+
),
206
206
+
options: (
207
207
+
await fetchRecentProjectKeys(
208
208
+
user,
209
209
+
10,
210
210
+
initialValues.hackatime_version as HackatimeVersion,
211
211
+
)
212
212
+
).map((key) => ({
213
213
+
text: {
214
214
+
type: "plain_text",
215
215
+
text: key,
216
216
+
},
217
217
+
value: key,
218
218
+
})),
219
219
+
},
220
220
+
},
182
221
],
183
222
},
184
223
});
···
193
232
const file = values.project_banner?.project_banner_input?.files?.[0]
194
233
?.url_private_download as string;
195
234
196
196
-
console.log(file);
235
235
+
const hackatimeKeys = JSON.stringify(
236
236
+
values.project_keys?.project_keys_input?.selected_options?.map(
237
237
+
(option) => option.value,
238
238
+
) || [],
239
239
+
);
197
240
198
241
try {
199
242
const projectBannerUrl = file
···
222
265
| string
223
266
| undefined,
224
267
hackatimeVersion,
268
268
+
hackatimeKeys,
225
269
})
226
270
.onConflictDoUpdate({
227
271
target: usersTable.id,
···
238
282
| string
239
283
| undefined,
240
284
hackatimeVersion,
285
285
+
hackatimeKeys,
241
286
},
242
287
});
288
288
+
289
289
+
// Update the view to show the latest Hackatime project keys
290
290
+
await slackClient.views.update({
291
291
+
view_id: payload.view.id,
292
292
+
view: {
293
293
+
type: "modal",
294
294
+
title: {
295
295
+
type: "plain_text",
296
296
+
text: "Add your hackatime keys",
297
297
+
},
298
298
+
blocks: [
299
299
+
{
300
300
+
type: "section",
301
301
+
text: {
302
302
+
type: "mrkdwn",
303
303
+
text: ":white_check_mark: Your project has been updated successfully!",
304
304
+
},
305
305
+
},
306
306
+
{
307
307
+
type: "section",
308
308
+
text: {
309
309
+
type: "mrkdwn",
310
310
+
text: "*Hackatime Project Keys:*",
311
311
+
},
312
312
+
},
313
313
+
{
314
314
+
type: "section",
315
315
+
text: {
316
316
+
type: "mrkdwn",
317
317
+
text: Object.values(HACKATIME_VERSIONS)
318
318
+
.map(
319
319
+
(v) =>
320
320
+
`• *${getHackatimeName(v.id)}*: \`${v.id}\``,
321
321
+
)
322
322
+
.join("\n"),
323
323
+
},
324
324
+
},
325
325
+
],
326
326
+
},
327
327
+
});
243
328
} catch (error) {
244
329
console.error("Error processing file:", error);
245
330
throw error;
+46
-9
src/libs/hackatime.ts
Reviewed
···
59
59
userId: string,
60
60
version: HackatimeVersion = "v2",
61
61
projectKeys?: string[],
62
62
+
from?: Date,
63
63
+
to?: Date,
62
64
) {
63
65
const apiUrl = getHackatimeApiUrl(version);
64
64
-
const response = await fetch(
65
65
-
`${apiUrl}/summary?user=${userId}&interval=month`,
66
66
-
{
67
67
-
headers: {
68
68
-
accept: "application/json",
69
69
-
Authorization: "Bearer 2ce9e698-8a16-46f0-b49a-ac121bcfd608",
70
70
-
},
66
66
+
const params = new URLSearchParams({
67
67
+
user: userId,
68
68
+
});
69
69
+
if (!from || !to) {
70
70
+
params.append("interval", "month");
71
71
+
} else if (from && to) {
72
72
+
params.append("from", from.toISOString());
73
73
+
params.append("to", to.toISOString());
74
74
+
}
75
75
+
76
76
+
const response = await fetch(`${apiUrl}/summary?${params.toString()}`, {
77
77
+
headers: {
78
78
+
accept: "application/json",
79
79
+
Authorization: "Bearer 2ce9e698-8a16-46f0-b49a-ac121bcfd608",
71
80
},
72
72
-
);
81
81
+
});
73
82
74
83
if (!response.ok) {
75
84
throw new Error(
76
76
-
`Failed to fetch Hackatime summary: ${response.status} ${response.statusText}`,
85
85
+
`Failed to fetch Hackatime summary: ${response.status} ${response.statusText}: ${await response.text()}`,
77
86
);
78
87
}
79
88
···
110
119
projectsKeys: projectsKeys,
111
120
};
112
121
}
122
122
+
123
123
+
/**
124
124
+
* Fetches the most recent project keys from a user's Hackatime data
125
125
+
* @param userId The user ID to fetch the project keys for
126
126
+
* @param limit The maximum number of projects to return (defaults to 10)
127
127
+
* @param version The Hackatime version to use (defaults to v2)
128
128
+
* @returns A promise that resolves to an array of recent project keys
129
129
+
*/
130
130
+
export async function fetchRecentProjectKeys(
131
131
+
userId: string,
132
132
+
limit = 10,
133
133
+
version: HackatimeVersion = "v2",
134
134
+
): Promise<string[]> {
135
135
+
const summary = await fetchHackatimeSummary(userId, version);
136
136
+
137
137
+
// Extract projects and sort by most recent
138
138
+
const sortedProjects =
139
139
+
summary.projects?.sort(
140
140
+
(a: { last_used_at: string }, b: { last_used_at: string }) =>
141
141
+
new Date(b.last_used_at).getTime() -
142
142
+
new Date(a.last_used_at).getTime(),
143
143
+
) || [];
144
144
+
145
145
+
// Return the keys of the most recent projects up to the limit
146
146
+
return sortedProjects
147
147
+
.slice(0, limit)
148
148
+
.map((project: { key: string }) => project.key);
149
149
+
}