diff --git a/packages/app/src/pages/posts/components/explore/index.jsx b/packages/app/src/pages/posts/components/explore/index.jsx
new file mode 100755
index 00000000..7729d466
--- /dev/null
+++ b/packages/app/src/pages/posts/components/explore/index.jsx
@@ -0,0 +1,126 @@
+import React from "react"
+import { Skeleton } from "antd"
+import { Icons } from "components/Icons"
+
+import { PostsList, Searcher } from "components"
+import Post from "models/post"
+
+import "./index.less"
+
+export default class ExplorePosts extends React.Component {
+ state = {
+ loading: true,
+ initialLoading: true,
+ hasMorePosts: true,
+ posts: [],
+ focusedSearcher: false,
+ filledSearcher: false,
+ }
+
+ wsEvents = {
+ "post.new": async (data) => {
+ this.setState({
+ posts: [data, ...this.state.posts],
+ })
+ },
+ "post.delete": async (id) => {
+ this.setState({
+ posts: this.state.posts.filter((post) => {
+ return post._id !== id
+ }),
+ })
+ }
+ }
+
+ loadPosts = async ({
+ trim,
+ replace = false
+ } = {}) => {
+ await this.setState({
+ loading: true,
+ })
+
+ // get posts from api
+ const result = await Post.getExplorePosts({
+ trim: trim ?? this.state.posts.length,
+ })
+
+ console.log("Loaded posts => \n", result)
+
+ if (result) {
+ if (result.length === 0) {
+ await this.setState({
+ hasMorePosts: false,
+ })
+
+ return false
+ }
+
+ await this.setState({
+ posts: replace ? result : [...this.state.posts, ...result],
+ })
+ }
+
+ await this.setState({
+ loading: false,
+ })
+
+ if (this.state.initialLoading) {
+ await this.setState({
+ initialLoading: false,
+ })
+ }
+ }
+
+ toggleFocusSearcher = (to) => {
+ to = to ?? !this.state.focusedSearcher
+
+ this.setState({
+ focusedSearcher: to
+ })
+ }
+
+ toggleState = (key, to) => {
+ to = to ?? !this.state[key]
+
+ this.setState({
+ [key]: to
+ })
+ }
+
+ componentDidMount = async () => {
+ await this.loadPosts()
+
+ Object.keys(this.wsEvents).forEach((event) => {
+ window.app.cores.api.namespaces["main"].listenEvent(event, this.wsEvents[event])
+ })
+ }
+
+ componentWillUnmount = async () => {
+ Object.keys(this.wsEvents).forEach((event) => {
+ window.app.cores.api.namespaces["main"].unlistenEvent(event, this.wsEvents[event])
+ })
+ }
+
+ render() {
+ return
+
+ this.toggleState("focusedSearcher", true)}
+ onUnfocus={() => this.toggleState("focusedSearcher", false)}
+ onFilled={() => this.toggleState("filledSearcher", true)}
+ onEmpty={() => this.toggleState("filledSearcher", false)}
+ />
+
+ {
+ this.state.focusedSearcher || this.state.filledSearcher ? null : this.state.initialLoading ?
:
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/components/explore/index.less b/packages/app/src/pages/posts/components/explore/index.less
new file mode 100755
index 00000000..07d5974d
--- /dev/null
+++ b/packages/app/src/pages/posts/components/explore/index.less
@@ -0,0 +1,8 @@
+.postsExplore {
+ width: 100%;
+
+ .postsExplore_header {
+ font-size: 1.3rem;
+ margin-bottom: 20px;
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/components/feed/index.jsx b/packages/app/src/pages/posts/components/feed/index.jsx
new file mode 100755
index 00000000..f5e74ff9
--- /dev/null
+++ b/packages/app/src/pages/posts/components/feed/index.jsx
@@ -0,0 +1,119 @@
+import React from "react"
+import { Skeleton } from "antd"
+
+import { PostsList } from "components"
+
+import FeedModel from "models/feed"
+
+import "./index.less"
+
+const emptyListRender = () => {
+ return
+}
+
+export default class Feed extends React.Component {
+ state = {
+ loading: true,
+ initialLoading: true,
+ hasMorePosts: true,
+ posts: [],
+ }
+
+ wsEvents = {
+ "post.new": (data) => {
+ this.setState({
+ posts: [data, ...this.state.posts],
+ })
+ },
+ "post.delete": (id) => {
+ this.setState({
+ posts: this.state.posts.filter((post) => {
+ return post._id !== id
+ }),
+ })
+ }
+ }
+
+ loadData = async ({
+ trim,
+ replace = false
+ } = {}) => {
+ await this.setState({
+ loading: true,
+ })
+
+ // get posts from api
+ const result = await FeedModel.getPostsFeed({
+ trim: trim ?? this.state.posts.length,
+ })
+
+ console.log("Loaded data => \n", result)
+
+ if (result) {
+ if (result.length === 0) {
+ await this.setState({
+ hasMorePosts: false,
+ loading: false,
+ initialLoading: false,
+ })
+
+ return false
+ }
+
+ await this.setState({
+ posts: replace ? result : [...this.state.posts, ...result],
+ })
+ }
+
+ await this.setState({
+ loading: false,
+ })
+
+ if (this.state.initialLoading) {
+ await this.setState({
+ initialLoading: false,
+ })
+ }
+ }
+
+ componentDidMount = async () => {
+ await this.loadData()
+
+ console.log(this.wsEvents)
+
+ Object.keys(this.wsEvents).forEach((event) => {
+ window.app.cores.api.namespaces["main"].listenEvent(event, this.wsEvents[event])
+ })
+ }
+
+ componentWillUnmount = async () => {
+ Object.keys(this.wsEvents).forEach((event) => {
+ window.app.cores.api.namespaces["main"].unlistenEvent(event, this.wsEvents[event])
+ })
+ }
+
+ render() {
+ return
+ {
+ this.state.initialLoading ?
:
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/components/feed/index.less b/packages/app/src/pages/posts/components/feed/index.less
new file mode 100755
index 00000000..6002adff
--- /dev/null
+++ b/packages/app/src/pages/posts/components/feed/index.less
@@ -0,0 +1,21 @@
+.feed {
+ display: flex;
+ flex-direction: column;
+
+ align-items: center;
+ justify-content: center;
+
+ width: 100%;
+
+ .emptyFeed {
+ display: flex;
+ flex-direction: column;
+
+ align-items: center;
+ justify-content: center;
+
+ width: 100%;
+
+ margin-top: 20px;
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/components/savedPosts/index.jsx b/packages/app/src/pages/posts/components/savedPosts/index.jsx
new file mode 100755
index 00000000..291075cf
--- /dev/null
+++ b/packages/app/src/pages/posts/components/savedPosts/index.jsx
@@ -0,0 +1,91 @@
+import React from "react"
+import { Skeleton } from "antd"
+import { Icons } from "components/Icons"
+
+import { PostsList } from "components"
+import Post from "models/post"
+
+import "./index.less"
+
+const emptyListRender = () => {
+ return
+
+ You dont have any saved posts.
+
+
+}
+
+export default class SavedPosts extends React.Component {
+ state = {
+ loading: true,
+ initialLoading: true,
+ hasMorePosts: true,
+ posts: [],
+ }
+
+ loadData = async ({
+ trim,
+ replace = false
+ } = {}) => {
+ await this.setState({
+ loading: true,
+ })
+
+ const result = await Post.getSavedPosts({
+ trim: trim ?? this.state.posts.length,
+ })
+
+ console.log("Loaded data => \n", result)
+
+ if (result) {
+ if (result.length === 0) {
+ await this.setState({
+ hasMorePosts: false,
+ loading: false,
+ initialLoading: false,
+ })
+
+ return false
+ }
+
+ await this.setState({
+ posts: replace ? result : [...this.state.posts, ...result],
+ })
+ }
+
+ await this.setState({
+ loading: false,
+ })
+
+ if (this.state.initialLoading) {
+ await this.setState({
+ initialLoading: false,
+ })
+ }
+ }
+
+ componentDidMount() {
+ this.loadData()
+ }
+
+ render() {
+ return
+
+
+
+ Saved Posts
+
+
+
+ {
+ this.state.initialLoading ?
:
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/components/savedPosts/index.less b/packages/app/src/pages/posts/components/savedPosts/index.less
new file mode 100755
index 00000000..0c36f204
--- /dev/null
+++ b/packages/app/src/pages/posts/components/savedPosts/index.less
@@ -0,0 +1,7 @@
+.savedPosts {
+ width: 100%;
+
+ .savedPosts_header {
+ font-size: 1.3rem;
+ }
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/components/trendings/index.jsx b/packages/app/src/pages/posts/components/trendings/index.jsx
new file mode 100755
index 00000000..ddc03329
--- /dev/null
+++ b/packages/app/src/pages/posts/components/trendings/index.jsx
@@ -0,0 +1,14 @@
+import React from "react"
+import { Result } from "antd"
+
+import "./index.less"
+
+export default (props) => {
+ return
+
+
+}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/components/trendings/index.less b/packages/app/src/pages/posts/components/trendings/index.less
new file mode 100755
index 00000000..db49f864
--- /dev/null
+++ b/packages/app/src/pages/posts/components/trendings/index.less
@@ -0,0 +1 @@
+.trendingsBrowser {}
\ No newline at end of file
diff --git a/packages/app/src/pages/posts/tabs.jsx b/packages/app/src/pages/posts/tabs.jsx
new file mode 100755
index 00000000..16b19096
--- /dev/null
+++ b/packages/app/src/pages/posts/tabs.jsx
@@ -0,0 +1,27 @@
+import FeedTab from "./components/feed"
+import ExploreTab from "./components/explore"
+import TrendingsTab from "./components/trendings"
+import SavedPostsTab from "./components/savedPosts"
+
+export default {
+ "feed": {
+ title: "Feed",
+ icon: "Rss",
+ component: FeedTab
+ },
+ "explore": {
+ title: "Explore",
+ icon: "Search",
+ component: ExploreTab
+ },
+ "trendings": {
+ title: "Trendings",
+ icon: "TrendingUp",
+ component: TrendingsTab
+ },
+ "savedPosts": {
+ title: "Saved posts",
+ icon: "Bookmark",
+ component: SavedPostsTab
+ }
+}
\ No newline at end of file