Browse Source

feat: feed wip

pull/9323/head
DarkPhoenix2704 2 months ago
parent
commit
bd936cb1af
  1. 28
      packages/nc-gui/components/feed/Changelog/Item.vue
  2. 20
      packages/nc-gui/components/feed/Changelog/index.vue
  3. 6
      packages/nc-gui/components/feed/Navigation.vue
  4. 49
      packages/nc-gui/components/feed/Roadmap.vue
  5. 74
      packages/nc-gui/components/feed/View.vue
  6. 53
      packages/nc-gui/components/feed/Youtube/Player.vue
  7. 23
      packages/nc-gui/components/feed/Youtube/index.vue
  8. 92
      packages/nc-gui/composables/useProductFeed.ts

28
packages/nc-gui/components/feed/Changelog/Item.vue

@ -15,39 +15,13 @@ const renderMarkdown = async (markdown: string) => {
return await unified().use(remarkParse).use(remarkRehype).use(rehypeSanitize).use(rehypeStringify).process(markdown)
}
const markdown = `# Introducing New Field Type : Button 🎉
Introducing the new field type Button, designed to enhance interactivity and automation by utilising data from the current row. This Button field type allows you to wire up the execution to predefined action such as triggering a webhook/API or opening a custom URL or launching a pre-filled NocoDB form. With this, you can dynamically pass record data, enabling highly tailored and context-aware interactions.
[![DemoVideo](https://github.com/user-attachments/assets/0c8539d7-71f1-4b2f-931b-d924eeedde8e)](https://youtu.be/V20tQDkbkvU)
### Added
- Automate workflows by configuring buttons to trigger custom webhooks.
- Configure buttons to open custom URLs
- Easily set up buttons to open pre-filled NocoDB forms
- Tailor the buttons text, colour, and icon to fit your specific needs
- Leverage NocoDB's formula editor to create dynamic URLs and payloads
### 📢 Important notice
This release includes a migration to generate a file reference table, which will be used in future updates to clean up unreferenced attachments. The migration process will run in the background. Please **note that this release doesnt initiate any clean up of unreferenced files**.
Additionally, we are introducing the [NC_ATTACHMENT_RETENTION_DAYS](https://docs.nocodb.com/getting-started/self-hosted/environment-variables/#storage) configuration for future releases. If this value is set to 0, it will prevent the cleanup of any stale / unreferenced attachments. Otherwise, unreferenced attachments will be removed from the disk after the specified number of days.
**Team NocoDB**
`
const renderedText = computedAsync(async () => {
return await renderMarkdown(props.body)
})
</script>
<template>
<div class="block max-w-260 px-12 relative">
<div class="block px-12 relative">
<div class="relative pb-12">
<div class="aside">
<div class="aside-divider">

20
packages/nc-gui/components/feed/Changelog/index.vue

@ -1,25 +1,15 @@
<script setup lang="ts">
const releases = ref<
{
body: string
id: string
published_at: string
}[]
>([])
const fetchReleaseNotes = async () => {
const response = await fetch('https://api.github.com/repos/nocodb/nocodb/releases')
const data = await response.json()
return data
}
const { loadGithubFeed, githubFeed } = useProductFeed()
onMounted(async () => {
releases.value = await fetchReleaseNotes()
await loadGithubFeed()
})
</script>
<template>
<FeedChangelogItem v-for="feed in releases" :key="feed.id" :date="feed.published_at" :body="feed?.body" />
<div class="max-w-260 mx-auto">
<FeedChangelogItem v-for="feed in githubFeed" :key="feed.id" :date="feed.published_at" :body="feed?.body" />
</div>
</template>
<style scoped lang="scss"></style>

6
packages/nc-gui/components/feed/Navigation.vue

@ -22,6 +22,12 @@ const navigationElements = [
title: 'Changelog',
description: 'Recent changes',
},
{
key: 'youtube',
icon: iconMap.youtube,
title: 'Youtube',
description: 'Recent changes',
},
{
key: 'roadmap',
icon: iconMap.ncNavigation,

49
packages/nc-gui/components/feed/Roadmap.vue

@ -0,0 +1,49 @@
<script setup lang="ts">
const iFrame = ref<HTMLIFrameElement | null>(null)
const isLoaded = ref(false)
const handleIframeLoad = () => {
if (!iFrame.value) {
return
}
const iframeDocument = iFrame.value?.contentDocument || iFrame.value?.contentWindow?.document
const classList = ['.nc-table-topbar', '.nc-table-toolbar']
for (const className of classList) {
nextTick(() => {
const element = iframeDocument?.querySelector(className)
if (element) {
element.remove()
}
})
}
isLoaded.value = true
}
</script>
<template>
<div
:class="{
'hidden': !isLoaded,
'block h-full': isLoaded,
}"
>
<iframe
ref="iFrame"
src="http://localhost:3000/#/nc/kanban/dc9d297d-2d89-4a33-9804-87924148913a"
width="100%"
height="100%"
style="border: none"
@load="handleIframeLoad"
></iframe>
</div>
<div v-if="!isLoaded" class="flex items-center justify-center h-full">
<NcLoader />
</div>
</template>
<style scoped lang="scss"></style>

74
packages/nc-gui/components/feed/View.vue

@ -1,16 +1,74 @@
<script setup lang="ts">
const activeKey = ref('recents')
import FeedRecents from './Changelog/index.vue'
import FeedYoutube from './Youtube/index.vue'
import FeedRoadmap from './Roadmap.vue'
const { activeTab } = useProductFeed()
const tabs = [
{
key: 'recents',
icon: 'clock',
title: 'Recents',
container: FeedRecents,
},
{
key: 'changelog',
icon: 'list',
title: 'Changelog',
container: FeedRecents,
},
{
key: 'roadmap',
icon: 'ncMapPin',
title: 'Roadmap',
container: FeedRoadmap,
},
{
key: 'youtube',
icon: 'ncYoutube',
title: 'Youtube',
container: FeedYoutube,
},
]
</script>
<template>
<FeedHeader />
<div class="flex gap-6 justify-center h-full">
<FeedNavigation v-model:active-key="activeKey" class="flex-grow-1" />
<div style="height: calc(100% - var(--topbar-height) - 32px)" class="overflow-y-auto w-full mt-8">
<div>
<FeedChangelog v-if="activeKey === 'changelog'" />
</div>
</div>
<div class="flex flex-col h-full">
<NcTabs v-model:activeKey="activeTab" centered>
<a-tab-pane v-for="tab in tabs" :key="tab.key" class="bg-gray-50 !h-full">
<template #tab>
<div class="flex gap-2 items-center">
<GeneralIcon
:class="{
'text-brand-500': activeTab === tab.key,
'text-gray-600': activeTab !== tab.key,
}"
:icon="tab.icon as any"
/>
<span
:class="{
'text-brand-500 font-medium': activeTab === tab.key,
'text-gray-700': activeTab !== tab.key,
}"
class="text-sm"
>{{ tab.title }}
</span>
</div>
</template>
<div
:style="{
height:
activeTab === 'recents'
? 'calc(100dvh - var(--toolbar-height) - var(--topbar-height))'
: 'calc(100dvh - var(--toolbar-height))',
}"
class="overflow-y-auto nc-scrollbar-md mx-auto w-full"
>
<component :is="tab.container" />
</div>
</a-tab-pane>
</NcTabs>
</div>
</template>

53
packages/nc-gui/components/feed/Youtube/Player.vue

@ -0,0 +1,53 @@
<script setup lang="ts">
import Plyr from 'plyr'
import 'plyr/dist/plyr.css'
defineProps<{
body: string
name: string
published_at: string
embed_url: string
html_url: string
}>()
const videoPlayer = ref<HTMLElement>()
const player = ref()
onMounted(() => {
if (!videoPlayer.value) return
player.value = new Plyr(videoPlayer.value, {
previewThumbnails: {},
})
})
onBeforeUnmount(() => {
if (player.value) {
player.value.destroy()
}
})
</script>
<template>
<div class="flex flex-col gap-5">
<div class="aspect-video !rounded-lg mx-auto !h-[428px]">
<div id="player" ref="videoPlayer" class="plyr__video-embed">
<iframe
:src="`${embed_url}?origin=https://plyr.io&amp;iv_load_policy=3&amp;modestbranding=1&amp;playsinline=1&amp;showinfo=0&amp;rel=0&amp;enablejsapi=1`"
allowfullscreen
allowtransparency
allow="autoplay"
></iframe>
</div>
</div>
<div>
{{ name }}
</div>
</div>
</template>
<style lang="scss">
.plyr--video {
@apply !rounded-lg;
}
</style>

23
packages/nc-gui/components/feed/Youtube/index.vue

@ -0,0 +1,23 @@
<script setup lang="ts">
const { youtubeFeed, loadYoutubeFeed } = useProductFeed()
onMounted(() => {
loadYoutubeFeed()
})
</script>
<template>
<div class="flex gap-2 flex-col">
<FeedYoutubePlayer
v-for="feed in youtubeFeed"
:key="feed.id"
:html_url="feed.html_url"
:name="feed.name"
:body="feed.body"
:published_at="feed.published_at"
:embed_url="feed.embed_url"
/>
</div>
</template>
<style scoped lang="scss"></style>

92
packages/nc-gui/composables/useProductFeed.ts

@ -0,0 +1,92 @@
import axios from 'axios'
const axiosInstance = axios.create({
// baseURL: 'https://nocodb.com/api',
baseURL: 'http://localhost:8000/api/v1',
headers: {
'Content-Type': 'application/json',
},
})
export const useProductFeed = createSharedComposable(() => {
const activeTab = ref('recents')
const youtubeFeed = ref<
{
id: string
body: string
name: string
published_at: string
thumbnails: {
default: {
height: number
url: string
width: number
}
high: {
height: number
url: string
width: number
}
medium: {
height: number
url: string
width: number
}
}
embed_url: string
html_url: string
}[]
>([])
const githubFeed = ref<
{
body: string
html_url: string
id: string
name: string
published_at: string
}[]
>([])
const socialFeed = ref([])
const ytNextPageToken = ref('')
const loadYoutubeFeed = async (loadMore?: boolean) => {
const { data } = await axiosInstance.get('/social/youtube', {
params: loadMore
? {
pageToken: ytNextPageToken.value,
per_page: 10,
}
: {
per_page: 10,
},
})
ytNextPageToken.value = data.nextPageToken
youtubeFeed.value = data.videos
}
const loadGithubFeed = async (loadMore?: boolean) => {
const { data } = await axiosInstance.get('/social/github', {
params: loadMore
? {
page: githubFeed.value.length / 10 + 1,
per_page: 10,
}
: {
per_page: 10,
},
})
githubFeed.value = data
}
return {
activeTab,
youtubeFeed,
githubFeed,
socialFeed,
loadYoutubeFeed,
loadGithubFeed,
}
})
Loading…
Cancel
Save