diff --git a/app/api/add-news/route.ts b/app/api/add-news/route.ts index b95307b..e0b578a 100644 --- a/app/api/add-news/route.ts +++ b/app/api/add-news/route.ts @@ -4,7 +4,7 @@ import { createHash } from "crypto"; export async function POST(request: Request) { try { - const { secretKey, content } = await request.json(); + const { secretKey, content, longitude, latitude } = await request.json(); if (!secretKey || !content) { throw new Error("secretKey and content required"); } @@ -12,7 +12,7 @@ export async function POST(request: Request) { const pwd = md5.update(secretKey).digest("hex"); const { rows } = await sql`SELECT * FROM Users WHERE pwd=${pwd}`; if (rows.length === 1) { - await sql`INSERT INTO news (content, "ownerId") VALUES (${content}, ${rows[0].id});`; + await sql`INSERT INTO news ("ownerId", content, longitude, latitude) VALUES (${rows[0].id}, ${content}, ${longitude}, ${latitude});`; return NextResponse.json({ message: "Succeeded to publish", result: { diff --git a/app/api/create-news-table/route.ts b/app/api/create-news-table/route.ts index 63721fd..0f35fab 100644 --- a/app/api/create-news-table/route.ts +++ b/app/api/create-news-table/route.ts @@ -9,7 +9,9 @@ export async function GET(request: Request) { "ownerId" integer, "content" varchar(1024), "picture" varchar(255), - "createdTime" TIMESTAMP DEFAULT CURRENT_TIMESTAMP + "createdTime" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + "latitude" float, + "longitude" float ); `; return NextResponse.json({ result }, { status: 200 }); diff --git a/app/api/create-users-table/route.ts b/app/api/create-users-table/route.ts index 2655c3b..78743af 100644 --- a/app/api/create-users-table/route.ts +++ b/app/api/create-users-table/route.ts @@ -8,7 +8,8 @@ export async function GET() { "id" serial NOT NULL, "username" varchar(255), "avatarId" int, - "pwd" varchar(255) + "pwd" varchar(255), + "createdTime" TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); `; return NextResponse.json({ result }, { status: 200 }); diff --git a/app/home/Cobe/index.tsx b/app/home/Cobe/index.tsx index 149b815..2031d06 100644 --- a/app/home/Cobe/index.tsx +++ b/app/home/Cobe/index.tsx @@ -1,17 +1,21 @@ "use client"; import createGlobe from "cobe"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useSpring } from "react-spring"; +import { useStores } from "@/hooks/useStores"; +import { observer } from "mobx-react-lite"; -export function Cobe() { +export const Cobe = observer(() => { + const { news, location } = useStores(); + const focusRef = useRef([-1, -1]); const canvasRef = useRef(null); const pointerInteracting = useRef<{ x: number; y: number } | null>(null); const pointerInteractionMovement = useRef({ x: 0, y: 0, }); - const [{ rx, ry }, api] = useSpring(() => ({ + const [{ rx, ry }, springApi] = useSpring(() => ({ rx: 0, ry: 0, config: { @@ -21,10 +25,20 @@ export function Cobe() { precision: 0.001, }, })); + const locationToAngles = (long: number, lat: number) => { + return [ + Math.PI - ((long * Math.PI) / 180 - Math.PI / 2), + (lat * Math.PI) / 180, + ]; + }; useEffect(() => { let phi = 0; let theta = 0; let width = 0; + let currentPhi = 0; + let currentTheta = 0; + const doublePi = Math.PI * 2; + if (canvasRef.current) { width = canvasRef.current.offsetWidth; const onResize = () => @@ -45,19 +59,32 @@ export function Cobe() { markerColor: [251 / 255, 100 / 255, 21 / 255], glowColor: [1.2, 1.2, 1.2], markers: [ - // longitude latitude { location: [37.7595, -122.4367], size: 0.03 }, { location: [40.7128, -74.006], size: 0.1 }, ], onRender: (state) => { - // This prevents rotation while dragging - if (!pointerInteracting.current) { - // Called on every animation frame. - // `state` will be an empty object, return updated params. - phi += 0.005; + const isAutoRotation = + focusRef.current[0] === -1 && focusRef.current[1] === -1; + if (isAutoRotation) { + if (!pointerInteracting.current) { + phi += 0.005; + } + state.phi = phi + rx.get(); + state.theta = theta + ry.get(); + } else { + state.phi = currentPhi; + state.theta = currentTheta; + const [focusPhi, focusTheta] = focusRef.current; + const distPositive = (focusPhi - currentPhi + doublePi) % doublePi; + const distNegative = (currentPhi - focusPhi + doublePi) % doublePi; + // Control the speed + if (distPositive < distNegative) { + currentPhi += distPositive * 0.08; + } else { + currentPhi -= distNegative * 0.08; + } + currentTheta = currentTheta * 0.92 + focusTheta * 0.08; } - state.phi = phi + rx.get(); - state.theta = theta + ry.get(); state.width = width * 2; state.height = width * 2; }, @@ -68,6 +95,16 @@ export function Cobe() { }; } }, []); + useEffect(() => { + if (news.showPublishModal) { + focusRef.current = locationToAngles( + location.longitude, + location.latitude + ); + } else { + focusRef.current = [-1, -1]; + } + }, [news.showPublishModal]); return (
); -} +}); diff --git a/app/home/Location/index.tsx b/app/home/Location/index.tsx new file mode 100644 index 0000000..ec92333 --- /dev/null +++ b/app/home/Location/index.tsx @@ -0,0 +1,19 @@ +"use client"; + +import React, { useEffect } from "react"; +import { observer } from "mobx-react-lite"; +import { useStores } from "@/hooks/useStores"; +import { useGeolocation } from "@uidotdev/usehooks"; + +export const Location = observer(() => { + const locationResult = useGeolocation(); + const { location } = useStores(); + + useEffect(() => { + if (locationResult.longitude && locationResult.latitude) { + location.updateLongitude(locationResult.longitude); + location.updateAltitude(locationResult.latitude); + } + }, [locationResult]); + return <>; +}); diff --git a/app/home/News/index.tsx b/app/home/News/index.tsx index 54bc4d4..e23347f 100644 --- a/app/home/News/index.tsx +++ b/app/home/News/index.tsx @@ -22,6 +22,7 @@ export const News = observer(() => { useEffect(() => { (async () => { const res = await fetch("/api/news", { + cache: "no-store", method: "GET", }); const { result } = await res.json(); diff --git a/app/home/Publish/index.tsx b/app/home/Post/index.tsx similarity index 84% rename from app/home/Publish/index.tsx rename to app/home/Post/index.tsx index 7b07e6f..b288dad 100644 --- a/app/home/Publish/index.tsx +++ b/app/home/Post/index.tsx @@ -8,7 +8,6 @@ import { ModalBody, ModalFooter, Button, - Input, Textarea, } from "@nextui-org/react"; import { CameraIcon } from "../icons/CameraIcon"; @@ -17,13 +16,12 @@ import { useModal } from "@ebay/nice-modal-react"; import Message from "../Message"; import { useStores } from "@/hooks/useStores"; -export const Publish = observer(() => { +export const Post = observer(() => { const [isPublishLoading, setIsPublishLoading] = useState(false); const [content, setContent] = useState("温馨提示,再不睡头发就要掉光光喽。"); + const { user, news, location } = useStores(); const messageModal = useModal(Message); - const { user, news } = useStores(); const userInfo = user.userInfo; - return ( <> { defaultValue={content} onValueChange={setContent} /> - +
+ +