Giter Club home page Giter Club logo

lilnotion's Introduction

lilNotion

lilNotion is a fullstack, single-page web app that clones some of Notion's features. It's a document editor that allows users to write notes, projects, and wikis in a flexible format.

๐Ÿ–ฅ Live Demo: lilnotion.herokuapp.com

lilNotion home

๐Ÿง‘โ€๐Ÿ’ป Tech Stack

  • Frontend: React/Redux/Webpack
  • Backend: Ruby on Rails/PostgreSQL
  • Storage: AWS S3
  • Hosting: Heroku
  • Drag and Drop: react-beautiful-dnd
  • UI components: radix-ui

๐ŸŽจ Features

๐Ÿ” User Authentication

Users can create an account to store their private pages. Visitors who don't want to make an account can log in as a demo user. RESTful authentication is implemented encrypting passwords with BCrypt and generating/caching session tokens with cryptographically strong random numbers.

user authentication

๐Ÿ“ Pages and Blocks

Every page and block has a unique identifier (UUID v4). Users can write seamlessly with different data types.

pages

๐Ÿ“š Dynamic Blocks

A block can be converted into another type of block using the โ‹ฎโ‹ฎ icon or the โŒ˜ + / command. Changing the type of a block will affect the underlying content but will created a more stylized appearance.

blocks

Block.jsx

function Block({ blockType }) {
  let blockBody
  switch (blockType) {
    case 'h1':
      blockBody = <Heading1Container block={block} deleteBlock={deleteBlock} />
      break
    case 'h2':
      blockBody = <Heading2Container block={block} deleteBlock={deleteBlock} />
      break
    case 'h3':
      blockBody = <Heading3Container block={block} deleteBlock={deleteBlock} />
      break
    case 'paragraph':
      blockBody = <ParagraphContainer block={block} deleteBlock={deleteBlock} />
      break
    case 'quote':
      blockBody = <QuoteContainer block={block} deleteBlock={deleteBlock} />
      break
    case 'callout':
      blockBody = <CalloutContainer block={block} deleteBlock={deleteBlock} />
      break
    ...
    default:
      blockBody = <ParagraphContainer block={block} deleteBlock={deleteBlock} />
      break
  }

  return (
    <Draggable>
      <PlusHandle />
      <DragHandle />
      {this.state.actionMenuOpen ? <BlockActionMenu /> : null}
      {this.state.selectMenuOpen ? <BlockSelectMenu /> : null}
      {this.state.slashMenuOpen ? <BlockSlashMenu /> : null}
      {blockBody}
    </Draggable>
  )
}

๐Ÿค Drag and Drop

Users can drag and drop blocks to rearrange information.

Page.jsx

function Page({ pages, blocks, updatePage }) {
  const { id } = useParams()
  const [pageId, setPageId] = useState(id)
  const [page, setPage] = useState(pages[id])

  function onDragEnd(result) {
    const { source, destination } = result
    if (!destination || source.index === destination.index) return
    const blockIds = page.blockIds
    const newBlockIds = [...blockIds]
    const removed = newBlockIds.splice(source.index, 1)
    newBlockIds.splice(destination.index, 0, ...removed)
    const newPage = Object.assign(page, { blockIds: newBlockIds })
    setPage(newPage)
    updatePage(newPage)
  }

  const orderedBlocks = []
  const blockIds = page.blockIds
  for (let i = 0; i < blockIds.length; i++) {
    orderedBlocks.push(blocks[blockIds[i]])
  }

  return (
    <div className="page">
      <div className="page-scroller">
        <DragDropContext onDragEnd={onDragEnd}>
          <Droppable droppableId={pageId}>
            {(provided) => (
              <div ref={provided.innerRef} {...provided.droppableProps} className="droppable-area">
                {orderedBlocks.map((block, index) => (
                  <BlockContainer
                    key={block.id}
                    block={block}
                    index={index}
                    blockIds={page.blockIds}
                  />
                ))}
                {provided.placeholder}
              </div>
            )}
          </Droppable>
        </DragDropContext>
      </div>
    </div>
  )
}

๐Ÿ“ธ Image Support

Image support with Amazon S3 storage allows users to attach images to blocks and page covers.

Image.jsx

function Image({ block, updateBlock }) {
  const [photoUrl, setPhotoUrl] = useState(null)

  function handleUpload(e) {
    const file = e.target.files[0]
    if (file) {
      const fileReader = new FileReader()
      fileReader.readAsDataURL(file)

      fileReader.onloadend = () => {
        const formData = new FormData()
        formData.append('block[imageUrl]', file)

        $.ajax({
          url: `/api/blocks/${block.id}`,
          method: 'PATCH',
          data: formData,
          contentType: false,
          processData: false,
        }).then(
          (res) => updateBlock(res),
          (err) => console.log('error: ', err)
        )
      }
    }
  }

  const isImageUploaded = block.imageUrl && block.imageUrl.length > 0
  const imageBody = isImageUploaded ? (
    <img className="block-image" src={block.imageUrl} alt="" />
  ) : (
    <label className="image-upload-label">
      <BiImage className="image-upload-icon" />
      <div className="image-upload-text">Add an image</div>
      <input
        type="file"
        className="image-upload-input"
        accept="image/*"
        onChange={handleUpload}
        hidden
      />
    </label>
  )

  return (
    <div className="block-body image">
      <div className="image-block-wrapper">{imageBody}</div>
    </div>
  )
}

๐Ÿ‘‹ How To Use

To clone and run this application, you'll need Git, Node.js, and Ruby on Rails installed on your computer. From your command line:

# Clone this repository
$ git clone https://github.com/brandonfang/lilnotion

# Go into the repository
$ cd lilnotion

# Install dependencies
$ npm install
$ gem install

# Run the app
$ npm start
$ rails server

๐Ÿ“… To-do

  • Rich text - text formatting, colored text, colored backgrounds
  • Nested pages

lilnotion's People

Contributors

brandonfang avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.