diff --git a/03_source/mobile/src/pages/DemoInstagramClone/components/ExploreContainer.css b/03_source/mobile/src/pages/DemoInstagramClone/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/components/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoInstagramClone/components/ExploreContainer.tsx b/03_source/mobile/src/pages/DemoInstagramClone/components/ExploreContainer.tsx new file mode 100644 index 0000000..396477f --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/components/ExploreContainer.tsx @@ -0,0 +1,21 @@ +import './ExploreContainer.css'; + +const ExploreContainer = ({ name }): React.JSX.Element => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/components/Feed.module.scss b/03_source/mobile/src/pages/DemoInstagramClone/components/Feed.module.scss new file mode 100644 index 0000000..4854d16 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/components/Feed.module.scss @@ -0,0 +1,221 @@ +.postsContainer { + + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} + +.postContainer { + + display: flex; + flex-direction: column; + margin-top: 1rem; +} + +.postProfile { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.postProfile ion-router-link { + + display: flex !important; + flex-direction: row !important; +} + +.postProfileInfo ion-avatar { + + height: 2.2rem; + width: 2.2rem; +} + +.postProfileInfo p { + + margin: 0; + padding: 0; + margin-left: 0.5rem; + font-weight: 500; + font-size: 0.9rem; + color: black; +} + +.postProfileInfo { + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + align-content: center; +} + +.postImage { + + border-top: 1px solid rgb(216, 216, 216); + margin-top: 0.5rem; + height: 20rem; + width: 100%; +} + +.postImageLike { + + font-size: 10rem; + color: rgb(231, 231, 231); + position: absolute; + left: 32vmin; + margin-top: 20vmin; + display: none; +} + +.postActionsContainer { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postActions { + + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.postActions ion-icon, +.postBookmark ion-icon { + + font-size: 1.5rem; +} + +.postActions ion-icon:not(:first-child) { + + padding-left: 0.7rem; +} + +.postLikesContainer { + + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postLikesContainer p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postLikedName { + + font-weight: 600; +} + +.postCaption { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.3rem; +} + +.postCaption p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postName { + + color: black !important; + font-weight: 600 !important; +} + +.postName ion-router-link { + + color: black; +} + +.postComments { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.5rem; +} + +.postComments p { + + margin: 0; + padding: 0; + color:rgb(175, 175, 175); + font-size: 0.8rem; +} + +.postAddComment { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.postAddCommentProfile { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + margin-top: 0.5rem; +} + +.postAddCommentProfile ion-avatar { + + height: 1.9rem; + width: 1.9rem; +} + +.postAddCommentProfile p { + + padding-left: 0.75rem; + font-size: 0.8rem; + color: rgb(175, 175, 175); +} + +.postAddCommentActions { + + +} + +.postAddCommentActions ion-icon { + + padding-left: 0.5rem; +} + +.postTime { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0rem; +} + +.postTime p { + + margin: 0; + padding: 0; + color: rgb(175, 175, 175); + font-size: 0.6rem; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoInstagramClone/components/Feed.tsx b/03_source/mobile/src/pages/DemoInstagramClone/components/Feed.tsx new file mode 100644 index 0000000..dbcfeff --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/components/Feed.tsx @@ -0,0 +1,129 @@ +import { IonAvatar, IonIcon, IonRouterLink } from '@ionic/react'; +import { + addCircleOutline, + bookmarkOutline, + chatbubbleOutline, + ellipsisVertical, + heart, + heartOutline, + paperPlaneOutline, +} from 'ionicons/icons'; +import { likePost } from '../pages/PostStore'; +import { ProfilesStore } from '../pages/ProfilesStore'; +import { ProfileStore } from '../pages/ProfileStore'; +import styles from './Feed.module.scss'; + +const Feed = (props): React.JSX.Element => { + const { posts } = props; + const profile = ProfileStore.useState((s) => s.profile); + const profiles = ProfilesStore.useState((s) => s.profiles); + + const addLike = (event, postID, liked) => { + likePost(event, postID, liked); + }; + + return ( +
+ {posts.map((post, index) => { + const postProfile = profiles.filter((p) => p.id === post.profile_id)[0]; + + return ( +
+
+
+ + + post avatar + + + + +

{postProfile.username}

+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ addLike(e, post.id, post.liked)} + /> + + +
+ +
+ +
+
+ +
+

+ Liked by alanmontgomery and{' '} + 2 others +

+
+ +
+

+ + + {postProfile.username} + + {' '} + {post.caption} +

+
+ +
+

View all {post.comments.length} comments

+
+ +
+
+ + add comment avatar + +

Add a comment...

+
+ +
+ + +
+
+ +
+

{post.time}

+
+
+ ); + })} +
+ ); +}; + +export default Feed; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/components/Stories.module.scss b/03_source/mobile/src/pages/DemoInstagramClone/components/Stories.module.scss new file mode 100644 index 0000000..aeb99cb --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/components/Stories.module.scss @@ -0,0 +1,98 @@ +$border: linear-gradient(to bottom, #d82b7e, #f57939); + +.stories { + + height: fit-content; + margin-top: -0.7rem; +} + +.storiesContainer { + + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + display: flex; + flex-direction: row; + width: 100%; +} + +.storiesContainer::-webkit-scrollbar { + + display: none; +} + +.story, +.yourStory { + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + align-content: center; + margin: 0 auto; + width: 4rem !important; + margin-left: 1rem; +} + +.story:first-child, +.yourStory:first-child { + + margin-left: 0.75rem; +} + +.story p, +.yourStory p { + + text-align: center; + margin: 0; + padding: 0; + margin-top: 0.2rem; + color: rgb(95, 95, 95); + font-size: 0.7rem; + font-weight: 400; + width: 120%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + position: relative; + margin-top: 5rem; +} + +.story img, +.yourStory img { + + height: 3.5rem !important; + width: 3.5rem !important; + position: absolute; + border-radius: 500px; + background: $border; + padding: 0.1rem; +} + +.yourStory img { + + background:rgb(214, 214, 214); +} + +.storyAdd { + + position: absolute; + color: white; + background-color: var(--ion-color-primary); + width: 1rem; + height: 1rem; + text-align: center; + margin: 0 auto; + display: flex; + flex-direction: row; + align-items: center; + align-content: center; + justify-content: center; + border-radius: 500px; + border: 2px solid white; + + bottom: 20px; + right: 0; + padding: 0.5rem; + font-size: 0.9rem; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoInstagramClone/components/Stories.tsx b/03_source/mobile/src/pages/DemoInstagramClone/components/Stories.tsx new file mode 100644 index 0000000..c902b30 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/components/Stories.tsx @@ -0,0 +1,27 @@ +import { IonCol, IonRouterLink, IonRow } from '@ionic/react'; +import styles from './Stories.module.scss'; + +const Stories = (props): React.JSX.Element => { + const { profiles } = props; + + return ( + +
+ {profiles.map((story, index) => { + return ( + + story avatar + {index === 0 &&
+
} + + +

{index === 0 ? 'Your story' : story.username}

+
+
+ ); + })} +
+
+ ); +}; + +export default Stories; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/index.tsx b/03_source/mobile/src/pages/DemoInstagramClone/index.tsx index c438e5b..ab4b29b 100644 --- a/03_source/mobile/src/pages/DemoInstagramClone/index.tsx +++ b/03_source/mobile/src/pages/DemoInstagramClone/index.tsx @@ -1,29 +1,61 @@ import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; -import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { bagOutline, cloudOutline, home, playCircleOutline, searchOutline } from 'ionicons/icons'; import { Route, Redirect } from 'react-router'; -import Tab1 from './AppPages/Tab1'; -import Tab2 from './AppPages/Tab2'; +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; import './style.scss'; +import Home from './pages/Home'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './theme/variables.scss'; +import { ProfileStore } from './pages/ProfileStore'; +import Profile from './pages/Profile'; +import MyProfile from './pages/MyProfile'; function DemoInstagramClone() { + const profile = ProfileStore.useState((s) => s.profile); + return ( - + + {/* + + + + */} + + + + + + + + + + + + + + + + + - {/* */} + {/* Dashboard @@ -32,6 +64,25 @@ function DemoInstagramClone() { Search + */} + + + + + + + + + + + + + + + + + tab avatar + ); diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Home.module.scss b/03_source/mobile/src/pages/DemoInstagramClone/pages/Home.module.scss new file mode 100644 index 0000000..48a1bdb --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/Home.module.scss @@ -0,0 +1,198 @@ +.postsContainer { + + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} + +.postContainer { + + display: flex; + flex-direction: column; + margin-top: 1rem; +} + +.postProfile { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.postProfileInfo ion-avatar { + + height: 2.2rem; + width: 2.2rem; +} + +.postProfileInfo p { + + margin: 0; + padding: 0; + margin-left: 0.5rem; + font-weight: 500; + font-size: 0.9rem; +} + +.postProfileInfo { + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + align-content: center; +} + +.postImage { + + border-top: 1px solid rgb(216, 216, 216); + margin-top: 0.5rem; + height: 20rem; + width: 100%; +} + +.postActionsContainer { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postActions { + + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.postActions ion-icon, +.postBookmark ion-icon { + + font-size: 1.5rem; +} + +.postActions ion-icon:not(:first-child) { + + padding-left: 0.7rem; +} + +.postLikesContainer { + + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postLikesContainer p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postLikedName { + + font-weight: 600; +} + +.postCaption { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.3rem; +} + +.postCaption p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postName { + + font-weight: 600 !important; +} + +.postComments { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.5rem; +} + +.postComments p { + + margin: 0; + padding: 0; + color:rgb(175, 175, 175); + font-size: 0.8rem; +} + +.postAddComment { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.postAddCommentProfile { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + margin-top: 0.5rem; +} + +.postAddCommentProfile ion-avatar { + + height: 1.9rem; + width: 1.9rem; +} + +.postAddCommentProfile p { + + padding-left: 0.75rem; + font-size: 0.8rem; + color: rgb(175, 175, 175); +} + +.postAddCommentActions { + + +} + +.postAddCommentActions ion-icon { + + padding-left: 0.5rem; +} + +.postTime { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0rem; +} + +.postTime p { + + margin: 0; + padding: 0; + color: rgb(175, 175, 175); + font-size: 0.6rem; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Home.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/Home.tsx new file mode 100644 index 0000000..64f15f8 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/Home.tsx @@ -0,0 +1,66 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { + addCircleOutline, + chevronBackOutline, + heartOutline, + paperPlaneOutline, +} from 'ionicons/icons'; +import Feed from '../components/Feed'; +import Stories from '../components/Stories'; +import { PostStore } from './PostStore'; +import { ProfilesStore } from './ProfilesStore'; + +const Home = (): React.JSX.Element => { + const profiles = ProfilesStore.useState((s) => s.profiles); + const posts = PostStore.useState((s) => s.posts); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + + main logo + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/MyProfile.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/MyProfile.tsx new file mode 100644 index 0000000..f643088 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/MyProfile.tsx @@ -0,0 +1,160 @@ +import { + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import { + addCircleOutline, + bookmarksOutline, + chevronDown, + gridOutline, + menuOutline, +} from 'ionicons/icons'; +import { useState } from 'react'; +import styles from './Profile.module.scss'; + +import { ProfilesStore } from './ProfilesStore'; +import { ProfileStore } from './ProfileStore'; + +const MyProfile = (): React.JSX.Element => { + const currentProfile = ProfileStore.useState((s) => s.profile); + const profiles = ProfilesStore.useState((s) => s.profiles); + const [profile, setProfile] = useState(false); + + useIonViewWillEnter(() => { + const profileID = currentProfile.id; + const tempProfile = profiles.filter((p) => parseInt(p.id) === parseInt(profileID))[0]; + setProfile(tempProfile); + }); + + return ( + + + + +

+ {profile.username} + +

+
+ + + + + + + + + +
+
+ + + + + + profile avatar + + + + + + + {profile.posts && profile.posts.length} + + Posts + + + + {profile.followers} + Followers + + + + {profile.following} + Following + + + + + + + +

+ {profile.firstname} {profile.surname} +

+

{profile.title}

+

{profile.bio}

+ + {profile.link} + +
+
+ + + + + Edit Profile + + + + + + Promotions + + + + + + Insights + + + +
+ + + + + + + + + + + + + {profile.posts && + profile.posts.map((post, index) => { + return ( + + post + + ); + })} + +
+
+ ); +}; + +export default MyProfile; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/PostStore.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/PostStore.tsx new file mode 100644 index 0000000..441b84b --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/PostStore.tsx @@ -0,0 +1,139 @@ +import { Store } from 'pullstate'; + +export const PostStore = new Store({ + posts: [ + { + id: 1, + image: 'https://ionic.io/img/ioniconf/ioniconf-open-graph.png', + caption: 'Ioniconf 2021! Register Now!', + likes: 73, + liked: false, + profile_id: 6, + time: '1 hour ago', + comments: [ + { + profile_id: 3, + comment: 'Test', + }, + ], + }, + + { + id: 2, + image: + 'https://creativetacos.com/wp-content/uploads/2019/08/Free-Chipper-Personal-Finance-App-Kit.jpg', + caption: 'Ionic React Hub! UI Components, Templates, Clones and more!', + likes: 73, + liked: true, + profile_id: 1, + time: '1 hour ago', + comments: [ + { + profile_id: 3, + comment: 'Test', + }, + { + profile_id: 3, + comment: 'Test', + }, + { + profile_id: 3, + comment: 'Test', + }, + ], + }, + + { + id: 3, + image: 'https://cdn.buttercms.com/AIcP6e8FRx6fgsKa7bvy', + caption: 'Join the first ever Ionic Event!', + likes: 73, + liked: false, + profile_id: 4, + time: '2 hours ago', + comments: [ + { + profile_id: 1, + comment: 'Test', + }, + { + profile_id: 2, + comment: 'Test', + }, + ], + }, + + { + id: 4, + image: 'https://ionicframework.com/img/meta/ionic-framework-og.png', + caption: 'Build cross platform mobile apps with the Ionic Framework!', + likes: 73, + liked: false, + profile_id: 2, + time: '3 hours ago', + comments: [ + { + profile_id: 1, + comment: 'Test', + }, + { + profile_id: 2, + comment: 'Test', + }, + ], + }, + ], +}); + +export const likePost = (event, postID, liked) => { + event.target.classList.add('animate__heartBeat'); + + if (!liked) { + document.getElementById(`postLike_${postID}`).style.display = 'inline'; + } + + setTimeout(() => { + event.target.classList.remove('animate__heartBeat'); + document.getElementById(`postLike_${postID}`).style.display = 'none'; + }, 850); + + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) ? (s.posts[index].liked = liked ? false : true) : false + ); + }); + + if (liked) { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].likes = s.posts[index].likes++) + : false + ); + }); + } else { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].likes = s.posts[index].likes--) + : false + ); + }); + } +}; + +export const addPost = (newPost) => { + PostStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; + +export const addCommentToPost = (newComment, postID) => { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].comments = [...s.posts[index].comments, newComment]) + : false + ); + }); +}; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Profile.module.scss b/03_source/mobile/src/pages/DemoInstagramClone/pages/Profile.module.scss new file mode 100644 index 0000000..c749657 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/Profile.module.scss @@ -0,0 +1,100 @@ +.username { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + font-weight: 600; +} + +.username ion-icon { + + font-size: 0.8rem; + margin-left: 0.3rem; +} + +.label { + + color: black; + font-size: 0.7rem; + font-weight: 500; + font-family: Arial, Helvetica, sans-serif !important; + text-transform: lowercase; +} + +.label::first-letter { + + text-transform: uppercase; +} + +.value { + + font-size: 1rem; +} + +.profileAvatar { + + border-radius: 500px; + width: 5.5rem; + height: auto; +} + +.profileInfo { + + display: flex; + flex-direction: column; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.profileInfo p, +.profileInfo a { + + // padding: 0.1rem 0 0.1rem 0; + margin: 0; + font-size: 0.9rem; +} + +.profileUsername { + + font-weight: 600; +} + +.profileTitle { + + color: rgb(136, 136, 136); +} + +.profileLink { + + color: rgb(22, 60, 131); + text-decoration: none; +} + +.profileActions ion-button { + + height: 2.3rem; + --border-radius: 5px; + font-weight: 600; +} + +.lightButton { + + --color: rgb(65, 65, 65); + --color-activated: rgb(65, 65, 65); + --background-hover: white; + --background-focused: white; + --background-activated: white; + --border-color: rgb(231, 231, 231); + --border-width: 2px; + font-size: 0.8rem; +} + +.postCol { + + --ion-grid-column-padding: 0rem; + padding: 0.1rem; + padding-bottom: 0.01rem !important; + padding-top: 0.01rem !important; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Profile.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/Profile.tsx new file mode 100644 index 0000000..aa07b55 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/Profile.tsx @@ -0,0 +1,183 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import { + addCircleOutline, + arrowBackOutline, + bookmarksOutline, + chevronDown, + ellipsisVertical, + gridOutline, + menuOutline, + personOutline, +} from 'ionicons/icons'; +import { useState } from 'react'; +import { useParams } from 'react-router'; +import styles from './Profile.module.scss'; + +import { ProfilesStore } from './ProfilesStore'; +import { ProfileStore } from './ProfileStore'; + +const Profile = (): React.JSX.Element => { + const params = useParams(); + const profiles = ProfilesStore.useState((s) => s.profiles); + const currentProfile = ProfileStore.useState((s) => s.profile); + const [profile, setProfile] = useState(false); + + useIonViewWillEnter(() => { + const profileID = params.id; + const tempProfile = profiles.filter((p) => parseInt(p.id) === parseInt(profileID))[0]; + setProfile(tempProfile); + }); + + return ( + + + + + {profile.id === currentProfile.id ? ( +

+ {profile.username} + +

+ ) : ( + <> + +

+ {profile.username} +

+ + )} +
+ + + {profile.id === currentProfile.id ? ( + <> + + + + + + + + ) : ( + + + + )} + +
+
+ + + + + + profile avatar + + + + + + + {profile.posts && profile.posts.length} + + Posts + + + + {profile.followers} + Followers + + + + {profile.following} + Following + + + + + + + +

+ {profile.firstname} {profile.surname} +

+

{profile.title}

+

{profile.bio}

+ + {profile.link} + +
+
+ + + + + Follow + + + + + + Message + + + + + + + + + +
+ + + + + + + + + + + + + {profile.posts && + profile.posts.map((post, index) => { + return ( + + post + + ); + })} + +
+
+ ); +}; + +export default Profile; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/ProfileStore.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/ProfileStore.tsx new file mode 100644 index 0000000..42f4d58 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/ProfileStore.tsx @@ -0,0 +1,20 @@ +import { Store } from 'pullstate'; + +export const ProfileStore = new Store({ + profile: { + id: 1, + firstname: 'Alan', + surname: 'Montgomery', + avatar: '/assets/alan.jpg', + followers: 0, + following: 0, + }, + posts: [], + feed: [], +}); + +export const addProfilePost = (newPost) => { + ProfileStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx new file mode 100644 index 0000000..63813c5 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx @@ -0,0 +1,172 @@ +import { Store } from 'pullstate'; + +export const ProfilesStore = new Store({ + profiles: [ + { + id: 1, + firstname: 'Alan', + surname: 'Montgomery', + username: 'alanmontgomery', + title: 'Mobile Team Lead', + bio: 'Full Stack 🤓 Mobile Team Lead/Senior React Dev', + link: 'alanmontgomery.co.uk', + avatar: '/assets/alan.jpg', + followers: '1,470', + following: '230', + posts: [ + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ], + }, + { + id: 2, + firstname: 'Max', + surname: 'Lynch', + username: 'maxlynch', + title: 'CEO Ionic', + bio: 'Co-founder/CEO @ionicframework. Created @capacitorjs. Gamer. @ManUtd fan.', + link: 'maxlynch.com', + avatar: 'https://pbs.twimg.com/profile_images/1318970727173885953/bln98FNj_400x400.jpg', + followers: '21.1K', + following: '1,200', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 3, + firstname: 'Ben', + surname: 'Sperry', + username: 'bensperry', + title: 'CDO Ionic', + bio: 'Co-founder / CDO @ionicframework. Creator of @ionicons. Product designer. Pixel junkie. Forest explorer.', + link: 'bensperry.com', + avatar: 'https://pbs.twimg.com/profile_images/1328390491126308864/jHHgl5Dm_400x400.jpg', + followers: '800', + following: '700', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 4, + firstname: 'Matt', + surname: 'Netkow', + username: 'mattnetkow', + title: 'Head of Product Marketing', + bio: 'I help web developers build cross-platform Web Native apps. @IonicFramework: Head of Product Marketing', + link: 'webnative.tech', + avatar: 'https://pbs.twimg.com/profile_images/1323383930150621187/GKc0nVzi_400x400.jpg', + followers: '1,200', + following: '900', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 5, + firstname: 'Liam', + surname: 'DeBeasi', + username: 'liamdebeasi', + title: 'Software Engineer', + bio: 'Software Engineer at @ionicframework', + link: 'liamdebeasi.com', + avatar: 'https://pbs.twimg.com/profile_images/1105953692669366273/ZNK4lRAJ_400x400.jpg', + followers: '871', + following: '510', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 6, + firstname: 'Mike', + surname: 'Hartington', + username: 'mikehartington', + title: 'Senior Dev Rel', + bio: 'Google Developer Expert. Mediocre at best. he/him. npx mhartington', + link: 'mhartington.io', + avatar: 'https://pbs.twimg.com/profile_images/1084993841898446849/DJ8XtR6L_400x400.jpg', + followers: '12.3K', + following: '2,200', + posts: [ + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ], + }, + { + id: 7, + firstname: 'Adam', + surname: 'Bradley', + username: 'adambradley', + title: 'Director of Technology', + bio: 'Proud dad, husband, veteran & dogs best friend. Typos are my own', + link: 'ionicframework.com', + avatar: 'https://pbs.twimg.com/profile_images/909075942320025600/hfYqicUk_400x400.jpg', + followers: '613', + following: '571', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 8, + firstname: 'Brody', + surname: 'Kidd', + username: 'brodykidd', + title: 'Enterprise Account Manager', + bio: 'Enterprise Account Manager | @ionicframework | @getcapacitor | @stenciljs', + link: 'ionicframework.com', + avatar: 'https://pbs.twimg.com/profile_images/477539679567228928/JObyaUW__400x400.jpeg', + followers: '677', + following: '219', + posts: [{}, {}, {}, {}, {}, {}, {}], + }, + ], +}); + +export const addProfilePost = (newPost) => { + ProfilesStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab1.css b/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab2.css b/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab2.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab2.tsx new file mode 100644 index 0000000..a46ce0c --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2 = (): React.JSX.Element => { + return ( + + + + Tab 2 + + + + + + Tab 2 + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab3.css b/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab3.tsx b/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab3.tsx new file mode 100644 index 0000000..094a323 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3 = (): React.JSX.Element => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile/src/pages/DemoInstagramClone/theme/variables.scss b/03_source/mobile/src/pages/DemoInstagramClone/theme/variables.scss new file mode 100644 index 0000000..64b2b41 --- /dev/null +++ b/03_source/mobile/src/pages/DemoInstagramClone/theme/variables.scss @@ -0,0 +1,126 @@ +.demo-instagram-clone { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + * { + font-family: Arial, Helvetica, sans-serif !important; + scroll-behavior: smooth; + } + + ::-webkit-scrollbar, + ::-webkit-scrollbar-thumb { + width: 0px; + } + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + :root { + --ion-toolbar-background: white; + --ion-tab-bar-color: black; + --ion-tab-bar-color-selected: black; + --ion-tab-bar-border-color: rgb(235, 235, 235); + } + + ion-tab-button ion-icon { + font-size: 1.6rem; + } + + ion-tab-button img { + border-radius: 500px; + height: 1.8rem; + border: 1px solid black; + } + + ion-tab-bar { + height: 3rem; + } + + ion-toolbar { + --border-style: none; + --padding-start: 1rem; + --padding-end: 1rem; + --padding-top: 0.5rem; + } + + ion-toolbar ion-icon { + font-weight: 900 !important; + font-size: 1.6rem; + } + + ion-toolbar ion-button:not(:last-child) { + padding-right: 0.3rem; + } +}