Skip to content

Commit

Permalink
Merge pull request #2 from divyenduz/refactor_rsc
Browse files Browse the repository at this point in the history
refactor(app-router): rewrite to use app router + rsc
  • Loading branch information
divyenduz authored Apr 19, 2024
2 parents 1a25336 + b0cdd6c commit e379544
Show file tree
Hide file tree
Showing 91 changed files with 1,957 additions and 6,504 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ env:

jobs:
build:
name: Test
name: Deploy
if: github.ref == 'refs/heads/main'
timeout-minutes: 30
runs-on: [ubuntu-latest]
steps:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"nx-cloud": "latest"
},
"dependencies": {
"typescript": "5.4.2"
"typescript": "5.4.5"
},
"nx": {
"targets": {
Expand Down
4 changes: 2 additions & 2 deletions packages/database/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
"pg:dump": "command -v pg_dump && PGPASSWORD=postgres pg_dump -U postgres -h localhost trackfootball@dev --schema-only --no-owner > ./prisma/schema.sql || echo 'pg_dump not found'"
},
"devDependencies": {
"@prisma/client": "5.11.0",
"prisma": "4.16.2",
"@prisma/client": "5.12.1",
"prisma": "5.12.1",
"vitest": "0.34.6"
}
}
Empty file.
25 changes: 25 additions & 0 deletions packages/next-js-app/app/actions/checkStravaToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { AwaitedUser } from 'app/layout'
import { checkStravaAccessToken } from 'repository/strava'
import { match } from 'ts-pattern'

export async function checkStravaToken(user: AwaitedUser) {
if (!user) {
console.error(
'Note: failed to get strava access token, user not found in context'
)
return 'NOT_WORKING'
}

const socialLogin = user?.socialLogin?.find((sl) => sl.platform === 'STRAVA')

// Note: if no social login, strava is not connected
if (!Boolean(socialLogin)) {
return 'NOT_CONNECTED'
}

const r = await checkStravaAccessToken(user.id)
return match(r)
.with(true, () => 'WORKING')
.with(false, () => 'NOT_WORKING')
.exhaustive()
}
30 changes: 30 additions & 0 deletions packages/next-js-app/app/actions/deletePost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use server'

import { Post, sql } from '@trackfootball/database'
import { MESSAGE_UNAUTHORIZED } from 'packages/auth/utils'
import { auth } from 'utils/auth'

export async function deletePost(postId: number) {
const user = await auth()

const post = (
await sql<Post[]>`
SELECT * FROM "Post"
WHERE "id" = ${postId}
`
)[0]

if (post?.userId !== user.id && user.type !== 'ADMIN') {
throw new Error(MESSAGE_UNAUTHORIZED)
}

const deletePost = (
await sql<Post[]>`
DELETE FROM "Post"
WHERE "id" = ${postId}
RETURNING *
`
)[0]

return { post: deletePost }
}
20 changes: 20 additions & 0 deletions packages/next-js-app/app/actions/disconnectStrava.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use server'

import { sql } from '@trackfootball/database'
import { revalidatePath } from 'next/cache'
import { auth } from 'utils/auth'

export async function disconnectStrava() {
const user = await auth()

const stravaLogin = user.socialLogin.find((sl) => sl.platform === 'STRAVA')
if (!Boolean(stravaLogin)) {
throw new Error("Trying to disconnect Strava login but it doesn't exist")
}
await sql`
DELETE FROM "SocialLogin"
WHERE "id" = ${stravaLogin!.id}
`
revalidatePath(`/athlete/${user.id}`)
return true
}
36 changes: 36 additions & 0 deletions packages/next-js-app/app/actions/getFeed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use server'

import { Field, Post, User, sql } from '@trackfootball/database'
import { FeatureCollection, LineString } from '@turf/helpers'

export type FeedItemType = Post & {
geoJson: FeatureCollection<LineString>
sprints: Array<FeatureCollection<LineString>>
runs: Array<FeatureCollection<LineString>>
Field: Field
User: User
}

export async function getFeed(cursor: number = 0, limit: number = 3) {
const maxPostId = (
await sql<{ max: number }[]>`SELECT MAX("id") FROM "Post"`
)[0].max

const posts = await sql<FeedItemType[]>`
SELECT row_to_json("Field".*::"Field") as "Field", row_to_json("User".*::"User") as "User", "Post".* FROM "Post"
LEFT JOIN "Field" ON "Post"."fieldId" = "Field"."id"
INNER JOIN "User" ON "Post"."userId" = "User"."id"
WHERE "Post"."id" <= ${cursor || maxPostId}
ORDER BY "Post"."startTime" DESC
LIMIT ${limit + 1}
`

let nextCursor: typeof cursor | null = null
const sortedPosts = posts.slice().sort((a, b) => b.id - a.id)
if (sortedPosts.length > limit) {
const nextItem = sortedPosts.pop()
nextCursor = nextItem!.id
}

return { posts: sortedPosts, nextCursor }
}
67 changes: 67 additions & 0 deletions packages/next-js-app/app/actions/refreshPost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use server'

import { Post, PostStatus, User, sql } from '@trackfootball/database'
import { Core } from '@trackfootball/sprint-detection'
import { durationToSeconds } from '@trackfootball/utils'
import { MESSAGE_UNAUTHORIZED } from 'packages/auth/utils'
import { postAddField } from 'packages/services/post/addField'
import { fetchStravaActivityGeoJson } from 'repository/strava'
import { auth } from 'utils/auth'

export async function refreshPost(postId: number) {
const user = await auth()

const post = (
await sql<Post[]>`
SELECT * FROM "Post"
WHERE "id" = ${postId}
`
)[0]

if (post?.userId !== user.id && user.type !== 'ADMIN') {
throw new Error(MESSAGE_UNAUTHORIZED)
}

const ownerUser = (
await sql<User[]>`
SELECT * FROM "User"
WHERE "id" = ${post.userId}
`
)[0]

const _key = parseInt(post.key)
const geoJson = await fetchStravaActivityGeoJson(_key, ownerUser.id)

if (geoJson instanceof Error) {
throw geoJson
}

if (!geoJson) {
throw new Error(`No geoJson found for Post id: ${postId}`)
}
const core = new Core(geoJson)

const updatedPost = (
await sql<Post[]>`
UPDATE "Post"
SET "geoJson" = ${geoJson as any},
"totalDistance" = ${core.totalDistance()},
"startTime" = ${new Date(core.getStartTime())},
"elapsedTime" = ${durationToSeconds(core.elapsedTime())},
"totalSprintTime" = ${durationToSeconds(core.totalSprintTime())},
"sprints" = ${core.sprints() as any},
"runs" = ${core.runs() as any},
"maxSpeed" = ${core.maxSpeed()},
"averageSpeed" = ${core.averageSpeed()},
"status" = ${PostStatus.COMPLETED}
WHERE "id" = ${postId}
RETURNING *
`
)[0]

await postAddField({
postId: post.id,
})

return { post: updatedPost }
}
77 changes: 77 additions & 0 deletions packages/next-js-app/app/activity/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Metadata, ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'
import { auth } from 'utils/auth'

import ActivityItem from '../../../components/organisms/Activity/ActivityItem'
import { getPost } from '../../../repository/post'

type Props = {
params: {
id: string
}
}

export function getHomepageUrl() {
const url = process.env.HOMEPAGE_URL || 'https://trackfootball.app'
return url
}

export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const id = params.id

const post = await getPost(parseInt(id))

const homepageUrl = getHomepageUrl()

const title = `${post?.text} | Activity | TrackFootball`
const description = `${post?.text} is a Football activity on TrackFootball`
const url = `${homepageUrl}/activity/${post?.id}`
const openGraph = {
title,
description,
type: 'website',
url,
}
const twitter = {
title,
description,
card: 'summary_large_image',
site: '@_TrackFootball',
creator: '@_TrackFootball',
domain: 'trackfootball.app',
url,
}

return {
title,
description,
openGraph,
twitter,
}
}

export default async function Activity({ params: { id } }: Props) {
const post = await getPost(parseInt(id))

if (!post) {
return notFound()
}

let user = null
try {
user = await auth()
} catch (e) {
console.error(e)
}

return (
<>
<div className="w-full max-w-4xl p-3 sm:p-5">
<ActivityItem post={post} user={user}></ActivityItem>
</div>
</>
)
}
Loading

0 comments on commit e379544

Please sign in to comment.