Projare - share your projects


this is a progressive web app that allows users to share their projects, what they used to create their projects, and the status of their projects.


https://firebasestorage.googleapis.com/v0/b/alonzoaustin-8314b.appspot.com/o/5_25_21%2Fprojare%2Fcreation.png?alt=media&token=1a2dc172-c932-4d1d-aff7-50b0ffe04f97https://firebasestorage.googleapis.com/v0/b/alonzoaustin-8314b.appspot.com/o/5_25_21%2Fprojare%2Fmain.png?alt=media&token=c8e3d2a4-58f3-4967-b94b-fb4e145b3168https://firebasestorage.googleapis.com/v0/b/alonzoaustin-8314b.appspot.com/o/5_25_21%2Fprojare%2Fproject.png?alt=media&token=0f78cb14-e1a2-4590-8b98-65daaa9ae287https://firebasestorage.googleapis.com/v0/b/alonzoaustin-8314b.appspot.com/o/5_25_21%2Fprojare%2Fyouraccount.png?alt=media&token=9c68a26f-ad22-4c37-89d2-0c9206dd1d62https://firebasestorage.googleapis.com/v0/b/alonzoaustin-8314b.appspot.com/o/5_25_21%2Fprojare%2Fmobilecreation.png?alt=media&token=79ad9feb-c955-402b-81f7-831c819c214ehttps://firebasestorage.googleapis.com/v0/b/alonzoaustin-8314b.appspot.com/o/5_25_21%2Fprojare%2Fmobilemain.png?alt=media&token=bed6f31f-fa6b-4168-893a-715366c706c4https://firebasestorage.googleapis.com/v0/b/alonzoaustin-8314b.appspot.com/o/5_25_21%2Fprojare%2Fmobileyouraccount.png?alt=media&token=a5d19596-c396-4b43-a33b-f7f2d994e809

Navigation

Codetake

Projare first started off as a react project called Codetake. When starting off, I thought that a single page app refers to an app without routing. With that in mind, I tried to build Codetake without the use of any routing.

routing example

Codetake worked this way because the Code behind the Navbar looked like this:

import React, {useState} from 'react'
import zeit from './bullet.ico'
import takes from './book.svg'
import user from './user.svg'
import favorites from './favorite.svg'
import updates from './notification.svg'
import settings from './settings.svg'
import add from './plus.svg'
import Enter from './enter'
import Newprojects from './newProject'
import Display from './projectdisplay'


function Components(){

    const [currentPage, setcurrentPage] = useState("takes")

    function Navprop(props){

    function changePage(event){
        sessionStorage.setItem("page", event.target.title)
        setcurrentPage(event.target.title)
        console.log("name: " + event.target.title)
        var pageTest = sessionStorage.getItem("page")
        console.log("pageTest: " + pageTest)
    }

    function changePage1(event){
        sessionStorage.setItem("page", event.target.innerText)
        setcurrentPage(event.target.innerText)
        console.log("name: " + event.target.innerText)
        var pageTest = sessionStorage.getItem("page")
        console.log("pageTest: " + pageTest)
    }

    return(
        <div title={props.description} onClick={changePage}>
            <img style={{
                width: '48px', 
                height: '48px',
                marginRight: '20px',
                }} title={props.description} className="navpic" src={props.pic} />
            <div style={{
                display: 'inline-block',
                fontSize: "16px",
                position: 'relative',
                bottom: '15px',
                backgroundColor: "#2f3e46",
                borderRadius: "6px",
                color: "white"
                }} onClick={changePage1} title={props.description}><strong title={props.description}>{props.description}</strong></div>
        </div>
    )
    }


        function Navbar(){
            var NavbarStyle = {
        backgroundColor: "#cad2c5",
        border: "none",
        borderRadius: "12px",
        boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.25)",
        position: "fixed",
        right: "0",
        top: "0",
        zIndex: "2",
        display: "inline-block",
            }

            var prevScrollPos = window.pageYOffset;
            const [currentScrollPos, setCurrentScrollPos] = useState("")
            const [hasMoved, setHasMoved] = useState(true)
            window.onscroll = function() {
      var currentScrollPos = window.pageYOffset;
      if (prevScrollPos > currentScrollPos) {
        setHasMoved(current => false);
      } else {
        setHasMoved(true)
      }
      prevScrollPos = currentScrollPos;
            } 

            hasMoved && (NavbarStyle.display = "none")
            !hasMoved && (NavbarStyle.display = "inline-block")



            const page = sessionStorage.getItem("page")

            return(
                <div className="navbar" style={NavbarStyle}>
                    <Navprop pic={takes} description="takes" />
                    <Navprop pic={add} description="add" />
                    <Navprop pic={user} description="my account" />
                    <Navprop pic={favorites} description="favorites" />
                    <Navprop pic={updates} description="updates" />
                    <Navprop pic={settings} description="settings" />
                </div>


            )
        }
    return(
        <div>
            <Navbar />
            {currentPage === "" && <Display />}
            {currentPage === "takes" && <Display />}
            {currentPage === "add" && <Newprojects />}
        </div>
    )
}

export default Components

If a user were to add a project, the user would not be able to see it alongside the other projects until the user refreshed the page.

adding project

Kodentake

After realizing that a single page app had multiple links, I decided to shift from regular reactjs to a framework I dabbled with for some time-Nextjs-because of their simplistic routing methods.

Kodentake was an upgraded version of Codetake that maintained similar file names but with routing, making navigation easier for users. Though it was better than Codetake, the file names and the link names did not exactly make sense. For example, the starting point that displayed all of the projects was called "projectdisplay." This name did not reflect the page at all as the name "projectdisplay" would describe a singular project rather than multiple projects.

Nextjs had a module called useRouter that allowed users to pull the dynamic id to be used to collect data; however, I did not actually understand what routes were. To make use of dynamic urls, I literally typed out "url" in Visual Studio Code in hope that there would be some obsure module that would allow me to make use of the dynamic urls. Thankfully, there was. It was called getURL. In order for me to use it, I usually typed the following:

const [urlName, seturlName] = useState("")
const [altId, setaltId] = useState("")

useEffect(() => {
	seturlName(getURL())
}

const userId = urlName.slice(19, urlName.length)

//the number often represented the start of the dynamic url that represented an id or data

userId.includes("%20") ? (setaltId(userId.split("%20").join(" "))) : console.log("all good")

Version 0.84 (Projare)

In addition to cleaner code, Projare saw file names and link names that actually made sense. getUrl would no longer be used as I finally understood what useRouter actually did. It also solved a problem that prevented new projects to be shown when added to database. This was because instead of using <Link></Link> to go to a different page as data is being updated/created, I was able to use router.push() at the end of functions to move to the new page after data has been updated/created.

Version 0.89 (Projare)

After building more projects and completing a coding challenge that made use of NextJS's native api methods, I decided to implement what I learned from them into the next version of Projare. With that in mind, I decided to refresh the UI of Projare. The new UI brought many changes such as:

  • the project cards looking similar to to the cards in Studythat and Codebroilers.
  • I also drew inspiration from office.com left vertical nav control module by making the navbar in Projare vertical and placing it on the far right near the center of the page.

Instead of making a direct connection to the database, I wanted to try something different by using a rest api. This made my code cleaner (sadly at a cost of speed).

Passing Data

Codetake

Because Codetake was built upon Reactjs instead of Nextjs, I decided to try out a serverless database called FaunaDB. At first, I ran into a bug where everytime I tried to push received data to a const using useState(), react would continue to push the data until I closed the browser. To solve this problem, I used localStorage and sessionStorage to store and retreive data I received from FaunaDB. My method of doing so looked like the following:


const testArray = []

serverClient.query(
        q.Map(
            q.Paginate(q.Match(q.Index("projects"))),
            q.Lambda("X", q.Get(q.Var("X")))
          )
    ).then((ret, index) => {ret.data.map(one => {console.log(one.data); testArray.push(one.data); localStorage.setItem('projects', JSON.stringify(testArray));})})

Kodentake + Projare

After reflecting that while my method does technically work, it was not really making proper use FaunaDB. Therefore, I removed localStorage and sessionStorage and went back to using useState. To solve the problem with the looping, I set a condition to only push data into the array when the array is empty. The code looked something like this:


    const [projectArray, setProjectArray] =  useState([])
    const [worksIdArray, setworksIdArray] = useState([])
    const [networkStatus, setnetworkStatus] = useState(false)
    //const [receivedKeys, setreceivedKeys] = useState([])
    const [yourWorks, setyourWorks] = useState("")

    projectArray.length == 0 && serverClient.query(
        q.Map(
            q.Paginate(q.Match(q.Index("projects"))),
            q.Lambda("X", q.Get(q.Var("X")))
          )
    ).then(ret => {
        setProjectArray(ret.data.map(project => project.data)), 
        console.log(ret.data.map(project => project.data),
        setworksIdArray(ret.data.map(work => work.ref.id)),
        localForage.setItem("projectList", ret.data.map(project => project.data)).then(ret => console.log("has been set")).catch(err => console.log(err)),
        setnetworkStatus(true)
        )})

//localForage is a JavaScript library that provides offline support through indexedDB and localStorage in case the user's browser does not support indexedDB.