Skip to content

Commit 2486c0f

Browse files
committed
I think that's liking
1 parent ade7a4c commit 2486c0f

6 files changed

Lines changed: 114 additions & 25 deletions

File tree

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
@@ -338,20 +338,20 @@ const canonicalUrl = computed(() => {
338338
const { user } = useAtproto()
339339
const showAuthModal = ref(false)
340340
341-
const { data: likesData } = useFetch(() => `/api/likes/${packageName.value}`, {
342-
default: () => ({ totalPackageLikes: 0, userHasLiked: false }),
341+
const { data: likesData } = useFetch(() => `/api/social/likes/${packageName.value}`, {
342+
default: () => ({ totalLikes: 0, userHasLiked: false }),
343343
})
344344
345-
// const { mutate: likePackage } = useLikePackage(subjectRef)
345+
const { mutate: likePackage } = useLikePackage(packageName.value)
346346
347347
const likeAction = async () => {
348348
if (user.value?.handle == null) {
349349
showAuthModal.value = true
350350
} else {
351-
// const result = await likePackage()
352-
// if (result?.likes) {
353-
// likesData.value = result.likes
354-
// }
351+
const result = await likePackage()
352+
if (result?.totalLikes) {
353+
likesData.value = result
354+
}
355355
}
356356
}
357357
@@ -519,9 +519,7 @@ defineOgImageComponent('Package', {
519519
class="w-4 h-4"
520520
aria-hidden="true"
521521
/>
522-
<span>{{
523-
formatCompactNumber(likesData?.totalPackageLikes ?? 0, { decimals: 1 })
524-
}}</span>
522+
<span>{{ formatCompactNumber(likesData?.totalLikes ?? 0, { decimals: 1 }) }}</span>
525523
</button>
526524

527525
<!-- Internal navigation: Docs + Code + Compare (hidden on mobile, shown in external links instead) -->
@@ -1154,6 +1152,7 @@ defineOgImageComponent('Package', {
11541152
</p>
11551153
<NuxtLink to="/" class="btn">{{ $t('common.go_back_home') }}</NuxtLink>
11561154
</div>
1155+
<AuthModal v-model:open="showAuthModal" />
11571156
</main>
11581157
</template>
11591158

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
@@ -7,7 +7,7 @@ import { useOAuthStorage } from '#server/utils/atproto/storage'
77
import { UNSET_NUXT_SESSION_PASSWORD } from '#shared/utils/constants'
88
import { OAuthMetadataSchema } from '#shared/schemas/oauth'
99
// TODO: limit scope as features gets added. atproto just allows login so no scary login screen till we have scopes
10-
export const scope = 'atproto'
10+
export const scope = 'atproto repo:dev.npmx.feed.like'
1111

1212
export function getOauthClientMetadata() {
1313
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)