+132
-40
Index.html
+132
-40
Index.html
···
37
37
</script>
38
38
39
39
<script>
40
-
// links/links/distinct-dids?target=at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lgwdn7vd722r&collection=app.bsky.feed.like&path=.subject.uri
41
-
const constellationEndpoint = 'https://constellation.microcosm.blue/links/distinct-dids';
40
+
41
+
const constellationEndpoint = 'https://constellation.microcosm.blue';
42
+
43
+
async function callConstellationEndpoint(target, collection, path, cursor = null) {
44
+
try {
45
+
const url = new URL(`${constellationEndpoint}/links/distinct-dids`);
46
+
url.searchParams.append('target', target);
47
+
url.searchParams.append('collection', collection);
48
+
url.searchParams.append('path', path);
49
+
if (cursor) {
50
+
url.searchParams.append('cursor', cursor);
51
+
}
52
+
const response = await fetch(url);
53
+
if (!response.ok) {
54
+
throw new Error(`HTTP error! Status: ${response.status}`);
55
+
}
56
+
57
+
return await response.json();
58
+
} catch (error) {
59
+
console.error('Error calling constellation endpoint:', error);
60
+
throw error;
61
+
}
62
+
}
42
63
43
64
function isValidHttpUrl(string) {
44
65
let url;
···
55
76
//Form input
56
77
post_url: '',
57
78
winner_count: 1,
58
-
likes_only: false,
79
+
likes_only: true,
59
80
reposts_only: false,
60
81
likes_and_reposts: false,
61
82
62
83
error: '',
84
+
loading: false,
85
+
winners: [],
86
+
showResults: false,
63
87
64
88
validateCheckBoxes(event) {
65
89
const targetId = event.target.id;
···
69
93
},
70
94
async runGiveaway() {
71
95
this.error = '';
72
-
//Form validation
73
-
if (!isValidHttpUrl(this.post_url)) {
74
-
this.error = 'Invalid Bluesky post URL';
75
-
return;
76
-
}
77
-
78
-
if (this.winner_count < 1) {
79
-
this.error = 'SOMEBODY has to win';
80
-
}
81
-
82
-
if (!this.likes_only && !this.reposts_only && !this.likes_and_reposts) {
83
-
this.error = 'Well, you have to pick some way for them to win';
84
-
return;
85
-
}
96
+
this.loading = true;
97
+
this.winners = [];
98
+
this.showResults = false;
86
99
87
-
let atUri = '';
88
-
if (this.post_url.startsWith('at://')) {
89
-
atUri = this.post_url;
90
-
} else {
91
-
//More checks to make sure it's a bsky url
92
-
if (!this.post_url.startsWith('https://bsky.app/')) {
93
-
this.error = 'Link to the Bluesky post or at uri please';
100
+
try {
101
+
//Form validation
102
+
if (this.winner_count < 1) {
103
+
this.error = 'SOMEBODY has to win';
94
104
return;
95
105
}
96
-
const postSplit = this.post_url.split('/');
97
-
if (postSplit.length < 7) {
98
-
this.error = 'Invalid Bluesky post URL. Should look like https://bsky.app/profile/baileytownsend.dev/post/3lbq7o74fcc2d';
106
+
107
+
if (!this.likes_only && !this.reposts_only && !this.likes_and_reposts) {
108
+
this.error = 'Well, you have to pick some way for them to win';
99
109
return;
100
110
}
101
-
try {
102
-
const handle = postSplit[4];
103
-
const recordKey = postSplit[6];
104
111
105
-
let did = await window.resolveHandle(handle);
106
-
atUri = `at://${did}/app.bsky.feed.post/${recordKey}`;
112
+
let atUri = '';
113
+
if (this.post_url.startsWith('at://')) {
114
+
atUri = this.post_url;
115
+
} else {
116
+
//More checks to make sure it's a bsky url
117
+
if (!this.post_url.startsWith('https://bsky.app/')) {
118
+
this.error = 'Link to the Bluesky post or at uri please';
119
+
return;
120
+
}
121
+
const postSplit = this.post_url.split('/');
122
+
if (postSplit.length < 7) {
123
+
this.error = 'Invalid Bluesky post URL. Should look like https://bsky.app/profile/baileytownsend.dev/post/3lbq7o74fcc2d';
124
+
return;
125
+
}
126
+
try {
127
+
const handle = postSplit[4];
128
+
const recordKey = postSplit[6];
107
129
108
-
} catch (e) {
109
-
console.log(e);
110
-
this.error = e.message;
130
+
let did = await window.resolveHandle(handle);
131
+
atUri = `at://${did}/app.bsky.feed.post/${recordKey}`;
132
+
133
+
} catch (e) {
134
+
console.log(e);
135
+
this.error = e.message;
136
+
return;
137
+
}
138
+
}
139
+
140
+
141
+
// Determine which collections to fetch based on user selection
142
+
const collections = [];
143
+
if (this.likes_only || this.likes_and_reposts) {
144
+
collections.push('app.bsky.feed.like');
145
+
}
146
+
if (this.reposts_only || this.likes_and_reposts) {
147
+
collections.push('app.bsky.feed.repost');
148
+
}
149
+
150
+
// Path to extract the subject URI
151
+
const path = '.subject.uri';
152
+
153
+
// Fetch data for each collection
154
+
const results = [];
155
+
let cursor = null;
156
+
for (const collection of collections) {
157
+
console.log(`Fetching ${collection} data...`);
158
+
const response = await callConstellationEndpoint(atUri, collection, path);
159
+
console.log(`${collection} response:`, response);
160
+
if (response && response.linking_dids) {
161
+
cursor = response.cursor;
162
+
results.push(...response.linking_dids);
163
+
}
164
+
}
165
+
166
+
// Remove duplicates if fetching both likes and reposts
167
+
const uniqueDids = [...new Set(results)];
168
+
console.log('Unique DIDs:', uniqueDids);
169
+
170
+
// Select winners
171
+
if (uniqueDids.length === 0) {
172
+
this.error = 'No participants found for this post';
111
173
return;
112
174
}
113
-
}
114
175
176
+
const winnerCount = Math.min(this.winner_count, uniqueDids.length);
115
177
116
-
console.log(atUri);
178
+
// Randomly select winners
179
+
for (let i = 0; i < winnerCount; i++) {
180
+
const randomIndex = Math.floor(Math.random() * uniqueDids.length);
181
+
this.winners.push(uniqueDids[randomIndex]);
182
+
// Remove the winner to avoid duplicates
183
+
uniqueDids.splice(randomIndex, 1);
184
+
}
117
185
186
+
console.log('Winners:', this.winners);
187
+
this.showResults = true;
188
+
189
+
} catch (error) {
190
+
console.error('Error in runGiveaway:', error);
191
+
this.error = `Error fetching data: ${error.message}`;
192
+
} finally {
193
+
this.loading = false;
194
+
}
118
195
}
119
196
120
197
}))
···
178
255
179
256
</fieldset>
180
257
<span x-show="error" x-text="error" class="text-red-500 text-lg font-bold"></span>
181
-
<button type="submit" class="btn btn-neutral mt-4">I choose
182
-
you!
258
+
<button type="submit" class="btn btn-neutral mt-4" x-bind:disabled="loading">
259
+
<span x-show="!loading">I choose you!</span>
260
+
<span x-show="loading" class="loading loading-spinner"></span>
183
261
</button>
184
262
</fieldset>
185
263
</form>
186
-
<a href="https://tangled.sh/@baileytownsend.dev/at-giveaways" class="link">View on <span
264
+
265
+
<!-- Results Section -->
266
+
<div x-show="showResults" class="mt-6 p-4 bg-base-200 rounded-lg">
267
+
<h3 class="text-xl font-bold mb-2">🎉 Winners 🎉</h3>
268
+
<p class="mb-2">Total participants: <span x-text="winners.length"></span></p>
269
+
<ul class="list-disc pl-5">
270
+
<template x-for="winner in winners">
271
+
<li class="mb-1">
272
+
<span x-text="winner"></span>
273
+
</li>
274
+
</template>
275
+
</ul>
276
+
</div>
277
+
278
+
<a href="https://tangled.sh/@baileytownsend.dev/at-giveaways" class="link mt-4 block">View on <span
187
279
class="font-semibold italic">tangled.sh</span></a>
188
280
</div>
189
281
</div>