refactor: remove GitHub authentication API endpoints and add new components

- Deleted GitHub callback and authentication handler files.
- Added new component for displaying headlines.
- Implemented hot news feed component with external API fetching.
- Created dynamic news provider page with routing.
- Developed news organization about page with animations and data fetching.
- Introduced a code of conduct document.
This commit is contained in:
yuanhau 2025-05-11 14:37:46 +08:00
parent b35e96625e
commit 930713042e
20 changed files with 861 additions and 599 deletions

View file

@ -1 +0,0 @@
<template></template>

View file

@ -1,64 +0,0 @@
<script lang="ts" setup>
const ffeed = ref();
const ass = ["健康2.0", "中天", "TVBS", "香港01", "ETtoday"];
import Button from "~/components/ui/button/Button.vue";
try {
const { data } = await useFetch("/api/rss/google");
ffeed.value = data.value;
} catch (error) {
console.error("Error:", error);
}
</script>
<template>
<div
v-for="item in ffeed"
class="justify-center align-center text-center p-4 border border-white rounded-lg m-4"
>
<span class="text-xl text-bold text-gray-100"
>{{ item.title }}
<span
v-if="ass.some((app) => item.title.includes(app))"
class="text-red-500 text-sm"
>
&nbsp;- 疑似來自有中資背景公司
</span>
</span>
<h4 class="text-gray-500 text-sm">
{{ new Date(item.date).toLocaleString() }}
</h4>
<div class="flex justify-center gap-2 mt-1">
<NuxtLink :to="item.link">
<Button>文章</Button>
</NuxtLink>
<NuxtLink>
<Button>關於媒體</Button>
</NuxtLink>
</div>
<br />
類似新聞:
<div v-for="itit in item.content">
<ul v-for="ititit in itit">
<li v-if="ititit.content?.[0].content[0] !== item.title">
&nbsp; -
<a :href="ititit.content?.[0].attributes?.href">{{
ititit.content?.[0].content[0]
}}</a>
-
<a :href="'/find/newsOrg?name=' + ititit.content?.[2].content[0]">{{
ititit.content?.[2].content[0]
}}</a>
<span
v-if="
ass.some((app) => ititit.content?.[2].content[0].includes(app))
"
class="text-red-500 text-sm"
>
&nbsp;- 疑似來自有中資背景公司
</span>
</li>
</ul>
</div>
</div>
</template>

View file

@ -1,3 +1,73 @@
<script setup lang="ts">
definePageMeta({
layout: false,
});
import { gsap } from "gsap";
import { TextPlugin } from "gsap/TextPlugin";
gsap.registerPlugin(TextPlugin);
const { t } = useI18n();
const localePath = useLocalePath();
const router = useRouter();
const popMessage = ref(null);
const currentDate = ref(
new Date().toLocaleDateString("zh-TW", {
month: "2-digit",
day: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
}),
);
onMounted(() => {
setInterval(() => {
currentDate.value = new Date().toLocaleDateString("zh-TW", {
month: "2-digit",
day: "2-digit",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
});
}, 1000);
});
import { ComputerDesktopIcon } from "@heroicons/vue/24/outline";
</script>
<template>
<h1>The "For you page"</h1>
<div
class="absolute inset-x-0 flex flex-row px-2 py-1 bg-white dark:bg-gray-900 justify-between align-center text-center z-index-[999999]"
>
<div class="flex flex-row g-2 text-gray-400 dark:text-gray-500">
<button @click="" class="w-8 h-8 text-gray-400 dark:text-gray-500">
<ComputerDesktopIcon class="w-8 h-8 text-gray-400 dark:text-gray-500" />
</button>
<span class="ml-1 mr-2 text-[20px]">|</span>
</div>
<div class="text-gray-400">{{ currentDate }}</div>
</div>
<div class="w-full h-[2.5em]"></div>
<div
class="flex flex-col justify-center align-center text-center absolute w-full h-screen inset-x-0 inset-y-0"
>
<div class="z-index-[9]"></div>
</div>
<div
class="absolute w-[calc(100% - 5px)] inset-x-0 bottom-0 mx-[1.5px] p-3 z-index-[999999] justify-between align-center flex flex-row"
>
<div class="">
Lang: <span class="text-sm">{{ $i18n.locale }}</span>
</div>
<div class="">
<SignedOut>
<SignInButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</div>
</div>
</template>

View file

@ -1,7 +0,0 @@
<script lang="ts" setup>
const route = useRoute();
const provider = route.params.provider;
const slug = route.params.slug;
</script>
<template></template>

View file

@ -1,123 +0,0 @@
<script setup lang="ts">
import { gsap } from "gsap";
import { ScrambleTextPlugin } from "gsap/dist/ScrambleTextPlugin";
gsap.registerPlugin(ScrambleTextPlugin);
const loading = ref(true);
const route = useRoute();
const slug = route.params.slug;
const { t, locale } = useI18n();
definePageMeta({
layout: "newsorg",
});
const {
data: fetchNewsOrgInfo,
pending,
error,
} = useFetch("/api/getData/fetchNewsOrgInfo", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: {
lang: locale,
requestPage: slug,
},
});
const {
data: fetchOtherData,
pending: fetchOtherDataPending,
error: fetchOtherDataError,
} = useFetch("/api/getData/fetchSidebarData", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: {
lang: locale,
},
});
watchEffect(() => {
loading.value = pending.value;
});
useSeoMeta({
title: fetchNewsOrgInfo.value?.title,
});
const orgNameAnimation = ref(null);
onMounted(() => {
gsap.to(orgNameAnimation.value, {
duration: 1,
scrambleText: fetchNewsOrgInfo.value?.title,
});
});
// Import Icons
import { GlobeAltIcon } from "@heroicons/vue/24/outline";
</script>
<template>
<div class="flex flex-row flex-wrap w-full mt-2">
<div class="text-center align-center justify-center w-[70%]">
<div
class="flex flex-row bg-[#AAACAA61] rounded-3xl p-3 gap-3 m-3 scale-5"
>
<NuxtImg
:src="fetchNewsOrgInfo?.logoUrl"
class="w-48 h-48 rounded-[10px]"
/>
<div class="flex flex-col gap-3 text-left">
<h1 class="text-4xl font-bold m-3 text-left" ref="orgNameAnimation">
{{ fetchNewsOrgInfo?.title }}
</h1>
<span class="text-ms m-1 mt-5 text-left text-wrap">{{
fetchNewsOrgInfo?.description
}}</span>
</div>
</div>
<div
class="gap-[3px] flex flex-row text-center align-center justify-center"
>
<a
:href="fetchNewsOrgInfo?.website"
target="_blank"
class="text-blue-200 hover:text-blue-300 transiton-all duration-100 flex flex-row"
><GlobeAltIcon class="w-6 h-6" />網站</a
>
</div>
</div>
<div
class="flex flex-col gap-3 text-left justify-right align-right bg-[#AAACAA61] w-[28%] rounded-3xl p-3 mt-3 h-screen"
>
<h3 class="text-2xl mt-2s">其他媒體</h3>
<hr />
<div v-for="data in fetchOtherData" :key="data.id" class="flex flex-col">
<NuxtImg :src="data.image"></NuxtImg>
<div class="flex flex-row">
<h1 class="text-xl text-bold">{{ data.title }}</h1>
<span class="text-ms ml-2 align-center justify-center text-center">
(
<span>{{ data.lean }}</span>
-
<span
>文章分數:
<span>{{ data.score }}</span>
</span>
)
</span>
</div>
</div>
<NuxtLink class="justify-center align-center text-center">
<button
class="bg-red-500 text-black p-2 rounded-full justify-center align-center"
>
<span>查看更多</span>
</button>
</NuxtLink>
</div>
</div>
</template>

View file

@ -55,33 +55,58 @@ onMounted(() => {
></span>
<div class="flex flex-row justify-center align-center gap-0s">
<NuxtLink :to="localePath('/app/')">
<button
class="m-4 mr-1 ml-1 bg-[#8C9393] text-white p-3 rounded-full bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
>
<span>{{ t("home.startusing") }}</span>
</button>
</NuxtLink>
<NuxtLink to="#learnmore">
<button
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
>
<span>{{ t("home.learnmore") }}</span>
</button>
</NuxtLink>
<button
class="m-4 mr-1 ml-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
>
<span>{{ t("home.startusing") }}</span>
</button>
</NuxtLink>
<NuxtLink to="#learnmore">
<button
class="m-4 ml-1 mr-1 bg-[#8C9393] text-white p-3 rounded-[10px] bg-gray-700 transition-all duration-150 hover:transform hover:scale-105 hover:shadow-lg"
>
<span>{{ t("home.learnmore") }}</span>
</button>
</NuxtLink>
</div>
</div>
<div class="h-screen"></div>
<div id="learnmore"></div>
<div class="flex flex-row flex-wrap justify-center gap-x-10 gap-y-3">
<div class="flex flex-col justify-center items-center align-middle bg-[#C9C9C9]/60 rounded-xl shadow-lg p-5 m-5 w-[300px] h-[200px]">
<div
class="flex flex-col justify-center items-center align-middle bg-[#C9C9C9]/60 rounded-xl shadow-lg p-5 m-5 w-[300px] h-[200px]"
>
<h1 class="text-8xl mt-0">🤔</h1>
<h2 class="text-xl font-bold">Why?</h2>
<span class="text-sm">台灣的新聞是要痲是來自中國控制的媒體或是來自只想獲得點閱的記者</span>
<span class="text-sm"
>台灣的新聞是要痲是來自中國控制的媒體或是來自只想獲得點閱的記者</span
>
</div>
<div class="flex flex-col justify-center items-center align-middle bg-[#C9C9C9]/60 rounded-xl shadow-lg p-5 m-5 w-[300px] h-[200px]">
<div
class="flex flex-col justify-center items-center align-middle bg-[#C9C9C9]/60 rounded-xl shadow-lg p-5 m-5 w-[300px] h-[200px]"
>
<h1 class="text-8xl mt-0">🧐</h1>
<h2 class="text-xl font-bold">How?</h2>
<span class="text-sm">We use web scraping to search for the latest news, and store it into a postgres database.</span>
<span class="text-sm"
>We use web scraping to search for the latest news, and store it into
a postgres database.</span
>
</div>
</div>
<br />
<h2 class="text-center align-center justify-center text-3xl">
Coming soon...
</h2>
<div class="flex flex-row flex-wrap justify-center gap-x-10 gap-y-3">
<div
class="flex flex-col justify-center items-center align-middle bg-[#C9C9C9]/60 rounded-xl shadow-lg p-5 m-5"
>
<h2 class="text-xl font-bold">Threads 文比較</h2>
</div>
<div
class="flex flex-col justify-center items-center align-middle bg-[#C9C9C9]/60 rounded-xl shadow-lg p-5 m-5"
>
<h2 class="text-xl font-bold">新聞 Facebook 觀點</h2>
</div>
</div>
</div>