diff --git a/packages/app/src/components/PlaylistTimelineEntry/index.jsx b/packages/app/src/components/PlaylistTimelineEntry/index.jsx
new file mode 100644
index 00000000..9101b603
--- /dev/null
+++ b/packages/app/src/components/PlaylistTimelineEntry/index.jsx
@@ -0,0 +1,47 @@
+import React from "react"
+import { Button } from "antd"
+import { Icons } from "components/Icons"
+import UserPreview from "components/UserPreview"
+import Image from "components/Image"
+
+import "./index.less"
+
+export default (props) => {
+ const { data } = props
+
+ return
+
+
+
+
+
+
+
+
+ {data.title ?? "Untitled"}
+
+
+
+
+
+ {data.description ?? "No description"}
+
+
+
+
+
+
+
+
+
+}
\ No newline at end of file
diff --git a/packages/app/src/components/PlaylistTimelineEntry/index.less b/packages/app/src/components/PlaylistTimelineEntry/index.less
new file mode 100644
index 00000000..7e495d2e
--- /dev/null
+++ b/packages/app/src/components/PlaylistTimelineEntry/index.less
@@ -0,0 +1,116 @@
+.playlistTimelineEntry {
+ display: flex;
+ flex-direction: column;
+
+ width: 35vw;
+ min-width: 300px;
+ max-width: 600px;
+
+ //min-height: 165px;
+ height: 100%;
+ max-width: 800px;
+
+ background-color: var(--background-color-accent);
+
+ transition: all 150ms ease-in-out;
+
+ padding: 17px;
+
+ border-bottom: 2px solid var(--border-color);
+
+ padding-bottom: 10px;
+
+ overflow: visible;
+
+ .playlistTimelineEntry_content {
+ display: flex;
+
+ flex-direction: row;
+
+ justify-items: center;
+
+ width: 100%;
+
+ gap: 20px;
+
+ .playlistTimelineEntry_thumbnail {
+ img {
+ width: 150px;
+ height: 150px;
+
+ object-fit: cover;
+
+ border-radius: 12px;
+ }
+ }
+
+ .playlistTimelineEntry_info {
+ display: flex;
+ flex-direction: column;
+
+ justify-content: center;
+
+ gap: 10px;
+
+ width: 100%;
+
+ .playlistTimelineEntry_title {
+ h1 {
+ font-size: 1.5rem;
+ font-weight: 600;
+
+ font-family: "Space Grotesk", sans-serif;
+
+ margin-top: 0;
+ margin-bottom: 5px;
+
+ color: var(--text-color);
+ }
+ }
+
+ .playlistTimelineEntry_description {
+ p {
+ font-size: 1rem;
+ font-weight: 400;
+
+ margin-top: 0;
+ margin-bottom: 5px;
+
+ color: var(--text-color);
+ }
+ }
+ }
+
+ .playlistTimelineEntry_actions {
+ display: flex;
+ flex-direction: column;
+
+ justify-content: center;
+ align-items: center;
+
+ gap: 10px;
+
+ .playlistTimelineEntry_action {
+ display: flex;
+ flex-direction: row;
+
+ justify-content: center;
+
+ gap: 10px;
+ }
+ }
+ }
+
+ &:first-child {
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+ }
+
+ &:last-child {
+ border-bottom-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+
+ border-bottom: none;
+ padding-bottom: 0;
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/components/PostsList/index.jsx b/packages/app/src/components/PostsList/index.jsx
index f53dfe6d..61cc07a5 100755
--- a/packages/app/src/components/PostsList/index.jsx
+++ b/packages/app/src/components/PostsList/index.jsx
@@ -1,7 +1,11 @@
import React from "react"
import * as antd from "antd"
import { Icons } from "components/Icons"
-import { PostCard, LoadMore } from "components"
+
+import PostCard from "components/PostCard"
+import PlaylistTimelineEntry from "components/PlaylistTimelineEntry"
+import LoadMore from "components/LoadMore"
+
//import { ViewportList } from "react-viewport-list"
import AutoSizer from "react-virtualized-auto-sizer"
@@ -27,6 +31,11 @@ const NoResultComponent = () => {
/>
}
+const typeToComponent = {
+ "post": (args) => ,
+ "playlist": (args) => ,
+}
+
export class PostsListsComponent extends React.Component {
state = {
openPost: null,
@@ -389,17 +398,17 @@ export class PostsListsComponent extends React.Component {
}
{
- this.state.list.map((post) => {
- return {
+ return React.createElement(typeToComponent[data.type ?? "post"], {
+ key: data._id,
+ data: data,
+ events: {
onClickLike: this.onLikePost,
onClickSave: this.onSavePost,
onClickDelete: this.onDeletePost,
onClickEdit: this.onEditPost,
- }}
- />
+ }
+ })
})
}
diff --git a/packages/app/src/models/feed/index.js b/packages/app/src/models/feed/index.js
index aab7886c..dca86425 100755
--- a/packages/app/src/models/feed/index.js
+++ b/packages/app/src/models/feed/index.js
@@ -1,14 +1,19 @@
export default class FeedModel {
- static get bridge() {
- return window.app?.cores.api.withEndpoints()
+ static async getTimelineFeed({ trim, limit }) {
+ const { data } = await app.cores.api.customRequest({
+ method: "GET",
+ url: `/feed/timeline`,
+ params: {
+ trim: trim ?? 0,
+ limit: limit ?? window.app.cores.settings.get("feed_max_fetch"),
+ }
+ })
+
+ return data
}
static async getPostsFeed({ trim, limit }) {
- if (!FeedModel.bridge) {
- throw new Error("Bridge is not available")
- }
-
- const { data } = await app.cores.api.customRequest( {
+ const { data } = await app.cores.api.customRequest({
method: "GET",
url: `/feed/posts`,
params: {
@@ -21,11 +26,7 @@ export default class FeedModel {
}
static async getPlaylistsFeed({ trim, limit }) {
- if (!FeedModel.bridge) {
- throw new Error("Bridge is not available")
- }
-
- const { data } = await app.cores.api.customRequest( {
+ const { data } = await app.cores.api.customRequest({
method: "GET",
url: `/feed/playlists`,
params: {
@@ -38,11 +39,7 @@ export default class FeedModel {
}
static async search(keywords, params = {}) {
- if (!FeedModel.bridge) {
- throw new Error("Bridge is not available")
- }
-
- const { data } = await app.cores.api.customRequest( {
+ const { data } = await app.cores.api.customRequest({
method: "GET",
url: `/search`,
params: {
diff --git a/packages/server/src/controllers/FeedController/index.js b/packages/server/src/controllers/FeedController/index.js
index e4b0e2c8..cd1bd225 100755
--- a/packages/server/src/controllers/FeedController/index.js
+++ b/packages/server/src/controllers/FeedController/index.js
@@ -9,6 +9,58 @@ export default class FeedController extends Controller {
httpEndpoints = {
get: {
+ "/timeline": {
+ middlewares: ["withAuthentication"],
+ fn: async (req, res) => {
+ const for_user_id = req.user?._id.toString()
+
+ if (!for_user_id) {
+ return res.status(400).json({
+ error: "Invalid user id"
+ })
+ }
+
+ // fetch posts
+ let posts = await getPosts({
+ for_user_id,
+ limit: req.query?.limit,
+ skip: req.query?.trim,
+ })
+
+ // fetch playlists
+ let playlists = await getPlaylists({
+ for_user_id,
+ limit: req.query?.limit,
+ skip: req.query?.trim,
+ })
+
+
+ // add type to posts and playlists
+ posts = posts.map((data) => {
+ data.type = "post"
+
+ return data
+ })
+
+ playlists = playlists.map((data) => {
+ data.type = "playlist"
+
+ return data
+ })
+
+ let feed = [
+ ...posts,
+ ...playlists,
+ ]
+
+ // sort feed
+ feed.sort((a, b) => {
+ return new Date(b.created_at) - new Date(a.created_at)
+ })
+
+ return res.json(feed)
+ }
+ },
"/posts": {
middlewares: ["withAuthentication"],
fn: async (req, res) => {
diff --git a/packages/server/src/controllers/FeedController/services/getPosts.js b/packages/server/src/controllers/FeedController/services/getPosts.js
index 2b8d429f..39aae9e5 100755
--- a/packages/server/src/controllers/FeedController/services/getPosts.js
+++ b/packages/server/src/controllers/FeedController/services/getPosts.js
@@ -1,6 +1,6 @@
-import { Post, UserFollow } from "../../../models"
+import { Post, UserFollow } from "@models"
-import fullfillPostsData from "../../../utils/fullfillPostsData"
+import fullfillPostsData from "@utils/fullfillPostsData"
export default async (payload) => {
const {