Skip to content

Commit 85b73d8

Browse files
committed
I think that's liking
1 parent 551e01f commit 85b73d8

File tree

6 files changed

+114
-25
lines changed

6 files changed

+114
-25
lines changed

app/composables/useAtproto.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { UserSession } from '#shared/schemas/userSession'
2+
import type { PackageLikes } from '~~/server/utils/atproto/utils/likes'
23

34
export function useAtproto() {
45
const { data: user, pending, clear } = useFetch<UserSession | null>('/api/auth/session')
@@ -13,3 +14,30 @@ export function useAtproto() {
1314

1415
return { user, pending, logout }
1516
}
17+
18+
export function useLikePackage(packageName: string) {
19+
const data = ref<PackageLikes | null>(null)
20+
const error = ref<Error | null>(null)
21+
const pending = ref(false)
22+
23+
const mutate = async () => {
24+
pending.value = true
25+
error.value = null
26+
27+
try {
28+
const result = await $fetch('/api/auth/social/like', {
29+
method: 'POST',
30+
body: { packageName },
31+
})
32+
data.value = result
33+
return result
34+
} catch (e) {
35+
error.value = e as Error
36+
throw e
37+
} finally {
38+
pending.value = false
39+
}
40+
}
41+
42+
return { data, error, pending, mutate }
43+
}

app/pages/package/[...package].vue

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,20 +356,20 @@ const canonicalUrl = computed(() => {
356356
const { user } = useAtproto()
357357
const showAuthModal = ref(false)
358358
359-
const { data: likesData } = useFetch(() => `/api/likes/${packageName.value}`, {
360-
default: () => ({ totalPackageLikes: 0, userHasLiked: false }),
359+
const { data: likesData } = useFetch(() => `/api/social/likes/${packageName.value}`, {
360+
default: () => ({ totalLikes: 0, userHasLiked: false }),
361361
})
362362
363-
// const { mutate: likePackage } = useLikePackage(subjectRef)
363+
const { mutate: likePackage } = useLikePackage(packageName.value)
364364
365365
const likeAction = async () => {
366366
if (user.value?.handle == null) {
367367
showAuthModal.value = true
368368
} else {
369-
// const result = await likePackage()
370-
// if (result?.likes) {
371-
// likesData.value = result.likes
372-
// }
369+
const result = await likePackage()
370+
if (result?.totalLikes) {
371+
likesData.value = result
372+
}
373373
}
374374
}
375375
@@ -537,9 +537,7 @@ defineOgImageComponent('Package', {
537537
class="w-4 h-4"
538538
aria-hidden="true"
539539
/>
540-
<span>{{
541-
formatCompactNumber(likesData?.totalPackageLikes ?? 0, { decimals: 1 })
542-
}}</span>
540+
<span>{{ formatCompactNumber(likesData?.totalLikes ?? 0, { decimals: 1 }) }}</span>
543541
</button>
544542

545543
<!-- Internal navigation: Docs + Code + Compare (hidden on mobile, shown in external links instead) -->
@@ -1113,6 +1111,7 @@ defineOgImageComponent('Package', {
11131111
</p>
11141112
<NuxtLink to="/" class="btn">{{ $t('common.go_back_home') }}</NuxtLink>
11151113
</div>
1114+
<AuthModal v-model:open="showAuthModal" />
11161115
</main>
11171116
</template>
11181117

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { Client } from '@atproto/lex'
2+
import { main as likeRecord } from '#shared/types/lexicons/dev/npmx/feed/like.defs'
3+
import * as dev from '#shared/types/lexicons/dev'
4+
import type { UriString } from '@atproto/lex'
5+
6+
export default eventHandlerWithOAuthSession(async (event, oAuthSession) => {
7+
const loggedInUsersDid = oAuthSession?.did.toString()
8+
9+
if (!oAuthSession || !loggedInUsersDid) {
10+
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' })
11+
}
12+
13+
const body = await readBody<{ packageName: string }>(event)
14+
15+
if (!body.packageName) {
16+
throw createError({
17+
status: 400,
18+
message: 'packageName is required',
19+
})
20+
}
21+
22+
const cachedFetch = event.context.cachedFetch
23+
if (!cachedFetch) {
24+
// TODO: Probably needs to add in a normal fetch if not provided
25+
// but ideally should not happen
26+
throw createError({
27+
status: 500,
28+
message: 'cachedFetch not provided in context',
29+
})
30+
}
31+
32+
const likesUtil = new PackageLikesUtils(cachedFetch)
33+
34+
const hasLiked = await likesUtil.hasTheUserLikedThePackage(body.packageName, loggedInUsersDid)
35+
if (hasLiked) {
36+
throw createError({
37+
status: 400,
38+
message: 'User has already liked the package',
39+
})
40+
}
41+
42+
const subjectRef = PACKAGE_SUBJECT_REF(body.packageName)
43+
const client = new Client(oAuthSession)
44+
45+
const like = dev.npmx.feed.like.$build({
46+
createdAt: new Date().toISOString(),
47+
//TODO test this?
48+
subjectRef: subjectRef as UriString,
49+
})
50+
51+
const result = await client.create(likeRecord, like)
52+
if (!result) {
53+
throw createError({
54+
status: 500,
55+
message: 'Failed to create like',
56+
})
57+
}
58+
59+
return await likesUtil.likeAPackageAndRetunLikes(body.packageName, loggedInUsersDid)
60+
})
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ export default eventHandlerWithOAuthSession(async (event, oAuthSession, _) => {
1616
})
1717
}
1818

19-
const likesUtlil = new PackageLikesUtils(cachedFetch)
20-
return await likesUtlil.getLikes(packageName, oAuthSession?.did.toString())
19+
const likesUtil = new PackageLikesUtils(cachedFetch)
20+
return await likesUtil.getLikes(packageName, oAuthSession?.did.toString())
2121
})

server/utils/atproto/oauth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { OAuthMetadataSchema } from '#shared/schemas/oauth'
99
// @ts-expect-error virtual file from oauth module
1010
import { clientUri } from '#oauth/config'
1111
// TODO: limit scope as features gets added. atproto just allows login so no scary login screen till we have scopes
12-
export const scope = 'atproto'
12+
export const scope = 'atproto repo:dev.npmx.feed.like'
1313

1414
export function getOauthClientMetadata() {
1515
const dev = import.meta.dev

server/utils/atproto/utils/likes.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,18 @@ export class PackageLikesUtils {
9494
userHasLiked = userCachedLike
9595
} else {
9696
userHasLiked = await this.constellationUserHasLiked(subjectRef, usersDid)
97-
await this.cache.set(CACHE_USER_LIKES_KEY(packageName, usersDid), true, CACHE_MAX_AGE)
97+
await this.cache.set(
98+
CACHE_USER_LIKES_KEY(packageName, usersDid),
99+
userHasLiked,
100+
CACHE_MAX_AGE,
101+
)
98102
}
99103
}
100104

101-
const packageLikes = {
102-
totalPackageLikes: totalLikes,
105+
return {
106+
totalLikes: totalLikes,
103107
userHasLiked,
104-
}
105-
106-
return packageLikes
108+
} as PackageLikes
107109
}
108110

109111
/**
@@ -132,19 +134,19 @@ export class PackageLikesUtils {
132134
*/
133135
async likeAPackageAndRetunLikes(packageName: string, usersDid: string): Promise<PackageLikes> {
134136
const totalLikesKey = CACHE_PACKAGE_TOTAL_KEY(packageName)
137+
const subjectRef = PACKAGE_SUBJECT_REF(packageName)
138+
135139
let totalLikes = await this.cache.get<number>(totalLikesKey)
136-
// If a cahce entry was found for total likes increase by 1
137-
if (totalLikes !== undefined) {
138-
await this.cache.set(totalLikesKey, totalLikes + 1, CACHE_MAX_AGE)
139-
} else {
140-
const subjectRef = PACKAGE_SUBJECT_REF(packageName)
140+
if (!totalLikes) {
141141
totalLikes = await this.constellationLikes(subjectRef)
142+
totalLikes = totalLikes + 1
143+
await this.cache.set(totalLikesKey, totalLikes, CACHE_MAX_AGE)
142144
}
143145
// We already know the user has not liked the package so set in the cache
144146
await this.cache.set(CACHE_USER_LIKES_KEY(packageName, usersDid), true, CACHE_MAX_AGE)
145147
return {
146148
totalLikes: totalLikes,
147149
userHasLiked: true,
148-
}
150+
} as PackageLikes
149151
}
150152
}

0 commit comments

Comments
 (0)