<commit_before>import React from 'react'
import {
  difference, filter, first, includes, isEmpty, map, some, sortBy, values
} from 'lodash'
import { find, get } from 'lodash/fp'
const { array, bool, func, object, string } = React.PropTypes
import cx from 'classnames'
import cheerio from 'cheerio'
import {
  humanDate, nonbreaking, present, textLength, truncate, appendInP
} from '../util/text'
import { sanitize } from 'hylo-utils/text'
import { linkifyHashtags } from '../util/linkify'
import { tagUrl } from '../routes'
import A from './A'
import Avatar from './Avatar'
import Dropdown from './Dropdown'
import { ClickCatchingSpan } from './ClickCatcher'
import { handleMouseOver } from './TagPopover'
import Comment from './Comment'
import CommentForm from './CommentForm'
import PeopleTyping from './PeopleTyping'
import LazyLoader from './LazyLoader'
import Icon from './Icon'
import LinkedPersonSentence from './LinkedPersonSentence'
import LinkPreview from './LinkPreview'
import { connect } from 'react-redux'
import { compose } from 'redux'
import {
  appendComment, followPost, navigate, removePost, startPostEdit, voteOnPost, pinPost
} from '../actions'
import { same } from '../models'
import { getComments, getCommunities, isPinned } from '../models/post'
import { getCurrentCommunity } from '../models/community'
import { canEditPost, canModerate } from '../models/currentUser'
import { isMobile } from '../client/util'
import config from '../config'
import decode from 'ent/decode'

const spacer =   •

export const presentDescription = (post, community, opts = {}) =>
  present(sanitize(post.description), {slug: get('slug', community), ...opts})

class Post extends React.Component {
  static propTypes = {
    post: object,
    communities: array,
    community: object,
    comments: array,
    dispatch: func,
    expanded: bool,
    onExpand: func,
    commentId: string
  }

  static childContextTypes = {post: object}

  getChildContext () {
    return {post: this.props.post}
  }

  render () {
    const { post, communities, comments, expanded, onExpand, community } = this.props
    const { tag, media, linkPreview } = post
    const image = find(m => m.type === 'image', media)
    const classes = cx('post', tag, {image, expanded})
    const title = linkifyHashtags(decode(sanitize(post.name || '')), get('slug', community))

    return

      <a name={`post-${post.id}`}>

      {image && <LazyLoader>

      </LazyLoader>}

      {linkPreview && <LinkPreview {...{linkPreview}}/>}

<VoteButton/><Voters/>

      <Attachments/>
      <CommentSection {...{post, expanded, onExpand, comments}}/>

  }
}

export default compose(
  connect((state, { post }) => {
    return {
      comments: getComments(post, state),
      communities: getCommunities(post, state),
      community: getCurrentCommunity(state)
    }
  })
)(Post)

export const UndecoratedPost = Post // for testing

export const Header = ({ communities }, { post, community }) => {
  const { tag } = post
  const person = tag === 'welcome' ? post.relatedUsers[0] : post.user
  const createdAt = new Date(post.created_at)

  return

    <Menu/>
    <Avatar person={person}/>
    {tag === 'welcome'
      ? <WelcomePostHeader communities={communities}/>
      :

          <A className='name' to={`/u/${person.id}`}>{person.name}

            <A to={`/p/${post.id}`} title={createdAt}>
              {nonbreaking(humanDate(createdAt))}

            {communities && <Communities communities={communities}/>}
            {post.public && {spacer}Public}

}

}
Header.contextTypes = {post: object, community: object}

const Communities = ({ communities }, { community }) => {
  if (community) communities = sortBy(communities, c => c.id !== community.id)
  const { length } = communities
  if (communities.length === 0) return null

  const communityLink = community => <A to={`/c/${community.slug}`}>{community.name}
  return
     in {communityLink(communities[0])}
    {length > 1 && <Dropdown className='post-communities-dropdown'
      toggleChildren={ + {length - 1} other{length > 2 ? 's' : ''}}>
      {communities.map(c =>
{communityLink(c)}
)}
    </Dropdown>}

}
Communities.contextTypes = {community: object}

const getTags = text =>
  cheerio.load(text)('.hashtag').toArray().map(tag =>
    tag.children[0].data.replace(/^#/, ''))

const extractTags = (shortDesc, fullDesc) => {
  const tags = getTags(fullDesc)
  return tags.length === 0 ? [] : difference(tags, getTags(shortDesc))
}

const HashtagLink = ({ tag, slug }, { dispatch }) => {
  const onMouseOver = handleMouseOver(dispatch)
  return
    {`#${tag}`}

}
HashtagLink.contextTypes = {dispatch: func}

export const Details = ({ expanded, onExpand }, { post, community, dispatch }) => {
  const { tag } = post
  const slug = get('slug', community)
  let description = presentDescription(post, community)
  let extractedTags = []
  const truncated = !expanded && textLength(description) > 200
  if (truncated) {
    const orig = description
    description = truncate(description, 200)
    extractedTags = extractTags(description, orig)
  }
  if (description) description = appendInP(description, ' ')

  return

    <ClickCatchingSpan dangerouslySetInnerHTML={{__html: description}}/>
    {truncated &&
      <wbr/>
       onExpand(null)} className='show-more'>Show more

    }
    {extractedTags.map(tag =>
      <wbr/>
      <HashtagLink tag={tag} slug={slug}/>

    )}
    {tag && <HashtagLink tag={tag} slug={slug}/>}

}
Details.contextTypes = {post: object, community: object, dispatch: func}

const Attachments = (props, { post }) => {
  const attachments = filter(post.media, m => m.type === 'gdoc')
  if (isEmpty(attachments)) return

  return

    {attachments.map((file, i) =>

        {truncate(file.name, 40).text}
      )}

}
Attachments.contextTypes = {post: object}

const WelcomePostHeader = ({ communities }, { post }) => {
  let person = post.relatedUsers[0]
  let community = communities[0]
  return

    <A to={`/u/${person.id}`}>{person.name} joined

    {community
      ?
          <A to={`/c/${community.slug}`}>{community.name}.

            Welcome them!

      :
          a community that is no longer active.
        }

}
WelcomePostHeader.contextTypes = {post: object}

export const Menu = (props, { dispatch, post, currentUser, community }) => {
  const canEdit = canEditPost(currentUser, post)
  const following = some(post.followers, same('id', currentUser))
  const pinned = isPinned(post, community)
  const edit = () => isMobile()
    ? dispatch(navigate(`/p/${post.id}/edit`))
    : dispatch(startPostEdit(post))
  const remove = () => window.confirm('Are you sure? This cannot be undone.') &&
    dispatch(removePost(post.id))
  const pin = () => dispatch(pinPost(get('slug', community), post.id))

  const toggleChildren = pinned
    ? Pinned
    :

  return <Dropdown className='post-menu' alignRight {...{toggleChildren}}>
    {canModerate(currentUser, community) &&

      {pinned ? 'Unpin post' : 'Pin post'}

}
    {canEdit &&
Edit
}
    {canEdit &&
Remove
}

       dispatch(followPost(post.id, currentUser))}>
        Turn {following ? 'off' : 'on'} notifications for this post

       window.alert('TODO')}>Report objectionable content

  </Dropdown>
}
Menu.contextTypes = {post: object, currentUser: object, dispatch: func, community: object}

export class CommentSection extends React.Component {
  static propTypes = {
    comments: array,
    onExpand: func,
    post: object,
    expanded: bool
  }

  static contextTypes = {
    community: object,
    currentUser: object,
    isProjectRequest: bool,
    dispatch: func,
    socket: object
  }

  constructor (props) {
    super(props)
    this.state = {
      peopleTyping: {}
    }
  }

  componentDidMount () {
    const { post: { id }, expanded, comments } = this.props
    const { dispatch } = this.context
    if (expanded) {
      this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/subscribe`)
      this.context.socket.on('commentAdded', function (comment){
        dispatch(appendComment(id, comment))
      })
      this.context.socket.on('userTyping', this.userTyping.bind(this))
    }
  }

  componentWillUnmount () {
    const { post: { id }, expanded } = this.props
    if (expanded) {
      this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/unsubscribe`)
      this.context.socket.off('commentAdded')
      this.context.socket.off('userTyping')
    }
  }

  userTyping (data) {
    let newState = this.state
    if (data.isTyping) {
      newState.peopleTyping[data.userId] = data.userName
    } else {
      delete newState.peopleTyping[data.userId]
    }
    this.setState(newState)
  }

  startedTyping () {
    const { post: { id } } = this.props
    this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/typing`, { isTyping: true })
  }

  stoppedTyping () {
    const { post: { id } } = this.props
    this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/typing`, { isTyping: false })
  }

  render () {
    let { post, comments, onExpand, expanded } = this.props
    const truncate = !expanded
    const { currentUser, community, isProjectRequest } = this.context
    const placeholder = isProjectRequest ? 'How can you help?' : null
    const peopleTyping = values(this.state.peopleTyping)

    if (!comments) comments = []
    comments = sortBy(comments, c => c.created_at)
    if (truncate) comments = comments.slice(-3)

    return

      {truncate && post.numComments > comments.length &&

         onExpand()}>Show all {post.numComments} comments

}
      {comments.map(c => <Comment comment={{...c, post_id: post.id}}
        truncate={truncate}
        expand={() => onExpand(c.id)}
        community={community}
        expanded={expanded}
        key={c.id}/>)}
      {peopleTyping.length > 0 && <PeopleTyping names={peopleTyping} showNames={false}/>}
      {currentUser && <CommentForm startedTyping={this.startedTyping.bind(this)} stoppedTyping={this.stoppedTyping.bind(this)} postId={post.id} {...{placeholder}}/>}

  }
}

export const VoteButton = (props, { post, currentUser, dispatch }) => {
  let vote = () => dispatch(voteOnPost(post, currentUser))
  let myVote = includes(map(post.voters, 'id'), (currentUser || {}).id)
  return
    {myVote ? <Icon name='Heart2' /> : <Icon name='Heart' />}
    {myVote ? 'Liked' : 'Like'}

}
VoteButton.contextTypes = {post: object, currentUser: object, dispatch: func}

export const Voters = (props, { post, currentUser }) => {
  const voters = post.voters || []

  let onlyAuthorIsVoting = voters.length === 1 && same('id', first(voters), post.user)
  return voters.length > 0 && !onlyAuthorIsVoting
    ? <LinkedPersonSentence people={voters} className='voters meta'>
        liked this.
      </LinkedPersonSentence>
    :
}
Voters.contextTypes = {post: object, currentUser: object}
<commit_msg>disable the typing feature<commit_after>import React from 'react'
import {
  difference, filter, first, includes, isEmpty, map, some, sortBy, values
} from 'lodash'
import { find, get } from 'lodash/fp'
const { array, bool, func, object, string } = React.PropTypes
import cx from 'classnames'
import cheerio from 'cheerio'
import {
  humanDate, nonbreaking, present, textLength, truncate, appendInP
} from '../util/text'
import { sanitize } from 'hylo-utils/text'
import { linkifyHashtags } from '../util/linkify'
import { tagUrl } from '../routes'
import A from './A'
import Avatar from './Avatar'
import Dropdown from './Dropdown'
import { ClickCatchingSpan } from './ClickCatcher'
import { handleMouseOver } from './TagPopover'
import Comment from './Comment'
import CommentForm from './CommentForm'
import PeopleTyping from './PeopleTyping'
import LazyLoader from './LazyLoader'
import Icon from './Icon'
import LinkedPersonSentence from './LinkedPersonSentence'
import LinkPreview from './LinkPreview'
import { connect } from 'react-redux'
import { compose } from 'redux'
import {
  appendComment, followPost, navigate, removePost, startPostEdit, voteOnPost, pinPost
} from '../actions'
import { same } from '../models'
import { getComments, getCommunities, isPinned } from '../models/post'
import { getCurrentCommunity } from '../models/community'
import { canEditPost, canModerate } from '../models/currentUser'
import { isMobile } from '../client/util'
import config from '../config'
import decode from 'ent/decode'

const spacer =   •

export const presentDescription = (post, community, opts = {}) =>
  present(sanitize(post.description), {slug: get('slug', community), ...opts})

class Post extends React.Component {
  static propTypes = {
    post: object,
    communities: array,
    community: object,
    comments: array,
    dispatch: func,
    expanded: bool,
    onExpand: func,
    commentId: string
  }

  static childContextTypes = {post: object}

  getChildContext () {
    return {post: this.props.post}
  }

  render () {
    const { post, communities, comments, expanded, onExpand, community } = this.props
    const { tag, media, linkPreview } = post
    const image = find(m => m.type === 'image', media)
    const classes = cx('post', tag, {image, expanded})
    const title = linkifyHashtags(decode(sanitize(post.name || '')), get('slug', community))

    return

      <a name={`post-${post.id}`}>

      {image && <LazyLoader>

      </LazyLoader>}

      {linkPreview && <LinkPreview {...{linkPreview}}/>}

<VoteButton/><Voters/>

      <Attachments/>
      <CommentSection {...{post, expanded, onExpand, comments}}/>

  }
}

export default compose(
  connect((state, { post }) => {
    return {
      comments: getComments(post, state),
      communities: getCommunities(post, state),
      community: getCurrentCommunity(state)
    }
  })
)(Post)

export const UndecoratedPost = Post // for testing

export const Header = ({ communities }, { post, community }) => {
  const { tag } = post
  const person = tag === 'welcome' ? post.relatedUsers[0] : post.user
  const createdAt = new Date(post.created_at)

  return

    <Menu/>
    <Avatar person={person}/>
    {tag === 'welcome'
      ? <WelcomePostHeader communities={communities}/>
      :

          <A className='name' to={`/u/${person.id}`}>{person.name}

            <A to={`/p/${post.id}`} title={createdAt}>
              {nonbreaking(humanDate(createdAt))}

            {communities && <Communities communities={communities}/>}
            {post.public && {spacer}Public}

}

}
Header.contextTypes = {post: object, community: object}

const Communities = ({ communities }, { community }) => {
  if (community) communities = sortBy(communities, c => c.id !== community.id)
  const { length } = communities
  if (communities.length === 0) return null

  const communityLink = community => <A to={`/c/${community.slug}`}>{community.name}
  return
     in {communityLink(communities[0])}
    {length > 1 && <Dropdown className='post-communities-dropdown'
      toggleChildren={ + {length - 1} other{length > 2 ? 's' : ''}}>
      {communities.map(c =>
{communityLink(c)}
)}
    </Dropdown>}

}
Communities.contextTypes = {community: object}

const getTags = text =>
  cheerio.load(text)('.hashtag').toArray().map(tag =>
    tag.children[0].data.replace(/^#/, ''))

const extractTags = (shortDesc, fullDesc) => {
  const tags = getTags(fullDesc)
  return tags.length === 0 ? [] : difference(tags, getTags(shortDesc))
}

const HashtagLink = ({ tag, slug }, { dispatch }) => {
  const onMouseOver = handleMouseOver(dispatch)
  return
    {`#${tag}`}

}
HashtagLink.contextTypes = {dispatch: func}

export const Details = ({ expanded, onExpand }, { post, community, dispatch }) => {
  const { tag } = post
  const slug = get('slug', community)
  let description = presentDescription(post, community)
  let extractedTags = []
  const truncated = !expanded && textLength(description) > 200
  if (truncated) {
    const orig = description
    description = truncate(description, 200)
    extractedTags = extractTags(description, orig)
  }
  if (description) description = appendInP(description, ' ')

  return

    <ClickCatchingSpan dangerouslySetInnerHTML={{__html: description}}/>
    {truncated &&
      <wbr/>
       onExpand(null)} className='show-more'>Show more

    }
    {extractedTags.map(tag =>
      <wbr/>
      <HashtagLink tag={tag} slug={slug}/>

    )}
    {tag && <HashtagLink tag={tag} slug={slug}/>}

}
Details.contextTypes = {post: object, community: object, dispatch: func}

const Attachments = (props, { post }) => {
  const attachments = filter(post.media, m => m.type === 'gdoc')
  if (isEmpty(attachments)) return

  return

    {attachments.map((file, i) =>

        {truncate(file.name, 40).text}
      )}

}
Attachments.contextTypes = {post: object}

const WelcomePostHeader = ({ communities }, { post }) => {
  let person = post.relatedUsers[0]
  let community = communities[0]
  return

    <A to={`/u/${person.id}`}>{person.name} joined

    {community
      ?
          <A to={`/c/${community.slug}`}>{community.name}.

            Welcome them!

      :
          a community that is no longer active.
        }

}
WelcomePostHeader.contextTypes = {post: object}

export const Menu = (props, { dispatch, post, currentUser, community }) => {
  const canEdit = canEditPost(currentUser, post)
  const following = some(post.followers, same('id', currentUser))
  const pinned = isPinned(post, community)
  const edit = () => isMobile()
    ? dispatch(navigate(`/p/${post.id}/edit`))
    : dispatch(startPostEdit(post))
  const remove = () => window.confirm('Are you sure? This cannot be undone.') &&
    dispatch(removePost(post.id))
  const pin = () => dispatch(pinPost(get('slug', community), post.id))

  const toggleChildren = pinned
    ? Pinned
    :

  return <Dropdown className='post-menu' alignRight {...{toggleChildren}}>
    {canModerate(currentUser, community) &&

      {pinned ? 'Unpin post' : 'Pin post'}

}
    {canEdit &&
Edit
}
    {canEdit &&
Remove
}

       dispatch(followPost(post.id, currentUser))}>
        Turn {following ? 'off' : 'on'} notifications for this post

       window.alert('TODO')}>Report objectionable content

  </Dropdown>
}
Menu.contextTypes = {post: object, currentUser: object, dispatch: func, community: object}

export class CommentSection extends React.Component {
  static propTypes = {
    comments: array,
    onExpand: func,
    post: object,
    expanded: bool
  }

  static contextTypes = {
    community: object,
    currentUser: object,
    isProjectRequest: bool,
    dispatch: func,
    socket: object
  }

  constructor (props) {
    super(props)
    this.state = {
      peopleTyping: {}
    }
  }

  componentDidMount () {
    const { post: { id }, expanded, comments } = this.props
    const { dispatch } = this.context
    if (expanded) {
      this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/subscribe`)
      this.context.socket.on('commentAdded', function (comment){
        dispatch(appendComment(id, comment))
      })
      //this.context.socket.on('userTyping', this.userTyping.bind(this))
    }
  }

  componentWillUnmount () {
    const { post: { id }, expanded } = this.props
    if (expanded) {
      this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/unsubscribe`)
      this.context.socket.off('commentAdded')
      //this.context.socket.off('userTyping')
    }
  }

  userTyping (data) {
    let newState = this.state
    if (data.isTyping) {
      newState.peopleTyping[data.userId] = data.userName
    } else {
      delete newState.peopleTyping[data.userId]
    }
    this.setState(newState)
  }

  startedTyping () {
    const { post: { id } } = this.props
    //this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/typing`, { isTyping: true })
  }

  stoppedTyping () {
    const { post: { id } } = this.props
    //this.context.socket.post(`${config.upstreamHost}/noo/post/${id}/typing`, { isTyping: false })
  }

  render () {
    let { post, comments, onExpand, expanded } = this.props
    const truncate = !expanded
    const { currentUser, community, isProjectRequest } = this.context
    const placeholder = isProjectRequest ? 'How can you help?' : null
    const peopleTyping = values(this.state.peopleTyping)

    if (!comments) comments = []
    comments = sortBy(comments, c => c.created_at)
    if (truncate) comments = comments.slice(-3)

    return

      {truncate && post.numComments > comments.length &&

         onExpand()}>Show all {post.numComments} comments

}
      {comments.map(c => <Comment comment={{...c, post_id: post.id}}
        truncate={truncate}
        expand={() => onExpand(c.id)}
        community={community}
        expanded={expanded}
        key={c.id}/>)}
      {peopleTyping.length > 0 && <PeopleTyping names={peopleTyping} showNames={false}/>}
      {currentUser && <CommentForm startedTyping={this.startedTyping.bind(this)} stoppedTyping={this.stoppedTyping.bind(this)} postId={post.id} {...{placeholder}}/>}

  }
}

export const VoteButton = (props, { post, currentUser, dispatch }) => {
  let vote = () => dispatch(voteOnPost(post, currentUser))
  let myVote = includes(map(post.voters, 'id'), (currentUser || {}).id)
  return
    {myVote ? <Icon name='Heart2' /> : <Icon name='Heart' />}
    {myVote ? 'Liked' : 'Like'}

}
VoteButton.contextTypes = {post: object, currentUser: object, dispatch: func}

export const Voters = (props, { post, currentUser }) => {
  const voters = post.voters || []

  let onlyAuthorIsVoting = voters.length === 1 && same('id', first(voters), post.user)
  return voters.length > 0 && !onlyAuthorIsVoting
    ? <LinkedPersonSentence people={voters} className='voters meta'>
        liked this.
      </LinkedPersonSentence>
    :
}
Voters.contextTypes = {post: object, currentUser: object}
<|endoftext|>


<commit_before>/** ****************************************************************************************************
 * File: navbar.js
 * Project: hootenanny-ui
 * @author Matt Putipong on 2/6/18
 *******************************************************************************************************/

import About from './about';

/**
 * Creates the navigation bar
 *
 * @param container - body
 * @constructor
 */
export default class Navbar {
    constructor( isLoggedIn, login ) {
        this.container  = d3.select( 'body' );
        this.isLoggedIn = isLoggedIn;
        this.login      = login;
    }

    /**
     * Render navbar and all of its view
     */
    async render() {
        this.navbar = this.container
            .insert( 'nav', ':first-child' )
            .attr( 'id', 'navbar' )
            .classed( 'contain text-white fill-dark', true );

        this.createLeftContainer();
        this.createRightContainer();

        return this;
    }

    createLeftContainer() {
        let leftContainer = this.navbar
            .append( 'div' )
            .classed( 'nav-item', true );

        leftContainer
            .append( 'div' )
            .classed( 'logo-container', true )
            .append( 'img' )
            .attr( 'src', './img/hoot_logo_update.png' )
            .classed( 'pointer hoot-logo', true );

        if ( this.isLoggedIn ) {
            this.menuButton = leftContainer
                .insert( 'div', '.logo-container' )
                .classed( 'menu-button button text-light pointer icon-container', true )
                .on( 'click', () => this.toggleManagePanel() );

            this.menuButton
                .append( 'i' )
                .classed( 'medium material-icons', true )
                .text( 'menu' );
        }
    }

    createRightContainer() {
        let rightContainer = this.navbar
            .append( 'div' )
            .classed( 'nav-item', true);

        if ( this.isLoggedIn ) {
            let user = JSON.parse( localStorage.getItem( 'user' ) );

            rightContainer
                .append( 'div' )
                .classed( 'dropdown-toggle light strong small info pad2x flex align-center text-light pointer', true )
                .append( 'span' )
                .text( 'Logged in as ' + user.display_name );

            let dropdownToggle = rightContainer
                .append( 'div' )
                .classed( 'dropdown-toggle icon-container button flex align-center text-light pointer', true );

            dropdownToggle
                .append( 'i' )
                .classed( 'medium material-icons', true )
                .text( 'info_outline' );

            dropdownToggle
                .append( 'i' )
                .classed( 'medium material-icons', true )
                .text( 'arrow_drop_down' );

            let dropdownContent = rightContainer
                .append( 'ul' )
                .classed( 'dropdown-content fill-white', true );

            dropdownContent
                .append( 'li' )
                .classed( 'dropdown-item pad2x strong pointer', true )
                .on( 'click', () => this.openAboutModal() )
                .append( 'a' )
                .attr( 'href', '#!' )
                .text( 'About' );

            dropdownContent
                .append( 'li' )
                .classed( 'dropdown-item pad2x strong pointer', true )
                .on( 'click', () => {
                    Hoot.api.logout()
                        .then( () => {
                            localStorage.removeItem( 'user' );
                            window.location.replace( 'login.html' );
                        } );
                } )
                .append( 'a' )
                .attr( 'href', '#!' )
                .text( 'Logout' );

            this.initDropdown();

        } else {
            rightContainer
                .append( 'div' )
                .attr( 'id', 'logoutTabBtn' )
                .attr( 'href', '#logout' )
                .classed( '_icon light strong small info pad2x flex align-center text-light pointer', true )
                .text( 'Launch Login' )
                .on( 'click', () => this.login.launchOAuthLogin() );
        }
    }

    toggleManagePanel() {
        let managePanel = Hoot.ui.managePanel,
            vis         = managePanel.isOpen === true;

        this.menuButton.classed( 'active', !vis );
        managePanel.container.classed( 'hidden', vis );

        d3.selectAll( '.context-menu, .datasets-options-menu' ).remove();

        managePanel.isOpen = !managePanel.isOpen;
    }

    //////////////////// possibly use in the future
    initDropdown() {
        let that = this;

        // bind single click listener to open dropdown
        $( 'nav .dropdown-toggle' ).one( 'click', () => toggleDropdown() );

        // toggle the dropdown
        function toggleDropdown( cb ) {
            $( 'nav .dropdown-content' ).slideToggle( 50, () => {
                if ( cb ) {
                    cb();
                    return;
                }

                if ( !$( 'nav .dropdown-content' ).is( ':visible' ) ) return;

                bindBodyClick();
            } );
        }

        // bind single click listener to body after dropdown is visible to close dropdown
        function bindBodyClick() {
            $( 'body' ).one( 'click', () => toggleDropdown( () => that.initDropdown() ) );
        }
    }

    openAboutModal() {
        new About().render();
    }
}
<commit_msg>#1259 - remove ability to click logged-in text in navbar<commit_after>/** ****************************************************************************************************
 * File: navbar.js
 * Project: hootenanny-ui
 * @author Matt Putipong on 2/6/18
 *******************************************************************************************************/

import About from './about';

/**
 * Creates the navigation bar
 *
 * @param container - body
 * @constructor
 */
export default class Navbar {
    constructor( isLoggedIn, login ) {
        this.container  = d3.select( 'body' );
        this.isLoggedIn = isLoggedIn;
        this.login      = login;
    }

    /**
     * Render navbar and all of its view
     */
    async render() {
        this.navbar = this.container
            .insert( 'nav', ':first-child' )
            .attr( 'id', 'navbar' )
            .classed( 'contain text-white fill-dark', true );

        this.createLeftContainer();
        this.createRightContainer();

        return this;
    }

    createLeftContainer() {
        let leftContainer = this.navbar
            .append( 'div' )
            .classed( 'nav-item', true );

        leftContainer
            .append( 'div' )
            .classed( 'logo-container', true )
            .append( 'img' )
            .attr( 'src', './img/hoot_logo_update.png' )
            .classed( 'pointer hoot-logo', true );

        if ( this.isLoggedIn ) {
            this.menuButton = leftContainer
                .insert( 'div', '.logo-container' )
                .classed( 'menu-button button text-light pointer icon-container', true )
                .on( 'click', () => this.toggleManagePanel() );

            this.menuButton
                .append( 'i' )
                .classed( 'medium material-icons', true )
                .text( 'menu' );
        }
    }

    createRightContainer() {
        let rightContainer = this.navbar
            .append( 'div' )
            .classed( 'nav-item', true);

        if ( this.isLoggedIn ) {
            let user = JSON.parse( localStorage.getItem( 'user' ) );

            rightContainer
                .append( 'div' )
                .classed( 'light strong small info pad2x flex align-center text-light', true )
                .append( 'span' )
                .text( 'Logged in as ' + user.display_name );

            let dropdownToggle = rightContainer
                .append( 'div' )
                .classed( 'dropdown-toggle icon-container button flex align-center text-light pointer', true );

            dropdownToggle
                .append( 'i' )
                .classed( 'medium material-icons', true )
                .text( 'info_outline' );

            dropdownToggle
                .append( 'i' )
                .classed( 'medium material-icons', true )
                .text( 'arrow_drop_down' );

            let dropdownContent = rightContainer
                .append( 'ul' )
                .classed( 'dropdown-content fill-white', true );

            dropdownContent
                .append( 'li' )
                .classed( 'dropdown-item pad2x strong pointer', true )
                .on( 'click', () => this.openAboutModal() )
                .append( 'a' )
                .attr( 'href', '#!' )
                .text( 'About' );

            dropdownContent
                .append( 'li' )
                .classed( 'dropdown-item pad2x strong pointer', true )
                .on( 'click', () => {
                    Hoot.api.logout()
                        .then( () => {
                            localStorage.removeItem( 'user' );
                            window.location.replace( 'login.html' );
                        } );
                } )
                .append( 'a' )
                .attr( 'href', '#!' )
                .text( 'Logout' );

            this.initDropdown();

        } else {
            rightContainer
                .append( 'div' )
                .attr( 'id', 'logoutTabBtn' )
                .attr( 'href', '#logout' )
                .classed( '_icon light strong small info pad2x flex align-center text-light pointer', true )
                .text( 'Launch Login' )
                .on( 'click', () => this.login.launchOAuthLogin() );
        }
    }

    toggleManagePanel() {
        let managePanel = Hoot.ui.managePanel,
            vis         = managePanel.isOpen === true;

        this.menuButton.classed( 'active', !vis );
        managePanel.container.classed( 'hidden', vis );

        d3.selectAll( '.context-menu, .datasets-options-menu' ).remove();

        managePanel.isOpen = !managePanel.isOpen;
    }

    initDropdown() {
        let that = this;

        // bind single click listener to open dropdown
        $( 'nav .dropdown-toggle' ).one( 'click', () => toggleDropdown() );

        // toggle the dropdown
        function toggleDropdown( cb ) {
            $( 'nav .dropdown-content' ).slideToggle( 50, () => {
                if ( cb ) {
                    cb();
                    return;
                }

                if ( !$( 'nav .dropdown-content' ).is( ':visible' ) ) return;

                bindBodyClick();
            } );
        }

        // bind single click listener to body after dropdown is visible to close dropdown
        function bindBodyClick() {
            $( 'body' ).one( 'click', () => toggleDropdown( () => that.initDropdown() ) );
        }
    }

    openAboutModal() {
        new About().render();
    }
}
<|endoftext|>


<commit_before>import React from 'react';
import _ from 'lodash';
import ReplEntries from './ReplEntries';
import ReplPrompt from './ReplPrompt';
import ReplStatusBar from './ReplStatusBar';
import ReplStore from '../stores/ReplStore';
import ReplDOMEvents from '../common/ReplDOMEvents';
import ReplDOM from '../common/ReplDOM';
import ReplActiveInputActions from '../actions/ReplActiveInputActions';
import ReplPreferencesActions from '../actions/ReplPreferencesActions';
import ReplConsoleActions from '../actions/ReplConsoleActions';
import ReplSuggestionActions from '../actions/ReplSuggestionActions';
import ReplStatusBarActions from '../actions/ReplStatusBarActions';
import Reflux from 'reflux';
import {ipcRenderer} from 'electron';
import {writeFile, readFile} from 'fs';
import {EOL} from 'os';
import remote from 'remote';
import ReplStreamHook from '../common/ReplStreamHook';
import ReplConsoleHook from '../common/ReplConsoleHook';
import ReplConsole from './ReplConsole';
import ReplOutput from '../common/ReplOutput';
import ContextMenu from '../menus/context-menu.json';
import ReplConstants from '../constants/ReplConstants';
import ReplContext from '../common/ReplContext';
import ReplCommon from '../common/ReplCommon';
import ReplLanguages from '../languages/ReplLanguages';
import {format} from 'util';

export default class Repl extends React.Component {
  constructor(props) {
    super(props);
    _.each([
      'onStateChange', 'onPaste', 'onContextMenu',
      'onKeydown', 'onBreakPrompt', 'onClearCommands',
      'onCollapseAll', 'onExpandAll', 'onDrag', 'onToggleConsole', 'onFormatPromptCode',
      'onStdout', 'onStderr', 'onStdMessage', 'onConsole', 'onConsoleChange', 'getPromptKey',
      'onImport', 'onExport', 'onAddPath', 'loadPreferences', 'onSaveCommands', 'onLoadScript',
      'checkNewRelease', 'onNewRelease', 'resizeWindow', 'onSetREPLMode', 'loadStartupScript', 'onInit'
    ], (field) => {
      this[field] = this[field].bind(this);
    });

    this.loadPreferences();
    ReplCommon.addUserDataToPath(ReplContext.getContext());
    ReplCommon.addToPath(global.Mancy.preferences.npmPaths, ReplContext.getContext());

    this.state = _.cloneDeep(ReplStore.getStore());
  }

  componentDidMount() {
    // set REPL language
    ReplLanguages.setREPL(global.Mancy.preferences.lang);
    this.setupContextMenu();
    this.activePromptKey = Date.now();

    //register events
    this.unsubscribe = ReplStore.listen(this.onStateChange);

    window.addEventListener('paste', this.onPaste, false);
    window.addEventListener('contextmenu', this.onContextMenu, false);
    window.addEventListener('keydown', this.onKeydown, false);

    // hooks
    ReplStreamHook.on('stdout', this.onStdout);
    ReplStreamHook.on('stderr', this.onStderr);

    ReplConsoleHook.on('console', this.onConsole);

    ipcRenderer.on('application:import-file', this.onImport);
    ipcRenderer.on('application:export-file', this.onExport);
    ipcRenderer.on('application:add-path', this.onAddPath);
    ipcRenderer.on('application:save-as', this.onSaveCommands);
    ipcRenderer.on('application:load-file', this.onLoadScript);

    ipcRenderer.on('application:prompt-clear-all', this.onClearCommands);
    ipcRenderer.on('application:prompt-expand-all', this.onExpandAll);
    ipcRenderer.on('application:prompt-collapse-all', this.onCollapseAll);
    ipcRenderer.on('application:prompt-break', this.onBreakPrompt);
    ipcRenderer.on('application:prompt-format', this.onFormatPromptCode);

    ipcRenderer.on('application:prompt-mode', (sender, value) => this.onSetREPLMode(value));
    ipcRenderer.on('application:prompt-language', (sender, value) =>  {
      global.Mancy.session.lang = value;
      ReplLanguages.setREPL(value);
      ReplStatusBarActions.updateLanguage(value);
    });

    ipcRenderer.on('application:preferences', ReplPreferencesActions.openPreferences);

    ipcRenderer.on('application:preference-theme-dark', () => ReplPreferencesActions.setTheme('Dark Theme'));
    ipcRenderer.on('application:preference-theme-light', () => ReplPreferencesActions.setTheme('Light Theme'));

    ipcRenderer.on('application:view-theme-dark', () => document.body.className = 'dark-theme');
    ipcRenderer.on('application:view-theme-light', () => document.body.className = 'light-theme');

    ipcRenderer.on('application:new-release', this.onNewRelease);
    this.onInit();
  }

  onInit() {
    this.checkNewRelease();
    this.onSetREPLMode(global.Mancy.preferences.mode);
    ReplPreferencesActions.setTheme(global.Mancy.preferences.theme);

    this.resizeWindow();
    this.loadStartupScript();
    ipcRenderer.send('application:prompt-on-close', global.Mancy.preferences.promptOnClose);
  }

  onLoadScript(sender, script) {
    if(script) {
      let ext = script.replace(/(?:.*)\.(\w+)$/, '$1');
      let lang = ReplLanguages.getLangName(ext);
      if(lang) {
        ReplLanguages.setREPL(lang);
        ReplActiveInputActions.playCommands([`.load ${script}`]);
        if(lang !== global.Mancy.session.lang) {
          setTimeout(() => {
            ReplLanguages.setREPL(global.Mancy.session.lang);
            ReplStore.onReloadPrompt('');
          }, 200);
        }
      } else {
        let options = {
          buttons: ['Close'],
          title: 'Failed to load script',
          type: 'error',
          message: `Failed to load '${script}'. '${ext}' extension is not supported.`
        };
        ipcRenderer.send('application:message-box', options);
      }
    }
  }

  loadStartupScript() {
    this.onLoadScript(null, global.Mancy.preferences.loadScript);
  }

  resizeWindow() {
    let setSize = (w, h) => localStorage.setItem('window', JSON.stringify({ width: w, height: h }));
    let win = remote.getCurrentWindow();
    let lastWindow = JSON.parse(localStorage.getItem('window'));
    let [width, height] = win.getSize();
    if(!lastWindow) { setSize(width, height) }
    else {
      try {
        win.setSize(lastWindow.width, lastWindow.height);
      } catch(e) {}
    }
    win.on('resize', () => {
      let [width, height] = win.getSize();
      setSize(width, height);
    });
  }

  setupContextMenu() {
    let Menu = remote.require('menu');
    let contextMenu = _.cloneDeep(ContextMenu);
    let actionTemplates = [
      {
        label: 'Clear All',
        accelerator: 'Ctrl+L',
        click: this.onClearCommands
      },
      {
        label: 'Collapse All',
        accelerator: 'CmdOrCtrl+K',
        click: this.onCollapseAll
      },
      {
        label: 'Expand All',
        accelerator: 'CmdOrCtrl+E',
        click: this.onExpandAll
      },
      {
        label: 'Break Prompt',
        accelerator: 'Ctrl+D',
        click: this.onBreakPrompt
      },
      {
        label: 'Format',
        accelerator: 'Shift+Ctrl+F',
        click: this.onFormatPromptCode
      }
    ];

    _.each(actionTemplates, (template) => contextMenu.push(template));

    this.menu = Menu.buildFromTemplate(contextMenu);
  }

  componentWillUnmount() {
    this.unsubscribe();
    window.removeEventListener('paste', this.onPaste, false);
    window.removeEventListener('contextmenu', this.onContextMenu, false);
    window.removeEventListener('keydown', this.onKeydown, false);

    ReplStreamHook.removeListener('stdout', this.onStdout);
    ReplStreamHook.removeListener('stderr', this.onStderr);
    ReplStreamHook.disable();

    ReplConsoleHook.removeListener('console', this.onConsole);
    ReplConsoleHook.disable();
  }

  checkNewRelease() {
    setTimeout(() => ipcRenderer.send('application:check-new-release'), 2000);
  }

  loadPreferences() {
    let preferences = JSON.parse(localStorage.getItem('preferences'));
    ipcRenderer.send('application:sync-preference', preferences);
  }

  onNewRelease(release) {
    ReplStatusBarActions.newRelease(release);
  }

  onSaveCommands(sender, filename) {
    let {history} = ReplStore.getStore();
    let data = _.chain(history)
      .map((h) => h.plainCode)
      .filter((c) => !/^\s*\./.test(c))
      .value()
      .join(EOL);

    writeFile(filename, data, { encoding: ReplConstants.REPL_ENCODING }, (err) => {
      let options = { buttons: ['Close'] };
      if(err) {
        options = _.extend(options, {
          title: 'Save Error',
          type: 'error',
          message: err.name || ' Error',
          detail: err.toString()
        });
      } else {
        options = _.extend(options, {
          title: 'Commands saved',
          type: 'info',
          message: `Commands saved to ${filename}`
        });
      }
      ipcRenderer.send('application:message-box', options);
    });
  }

  onImport(sender, filename) {
    readFile(filename, (err, data) => {
      if(!err) {
        try {
          let history = JSON.parse(data);
          if(!Array.isArray(history) || !_.every(history, (h) => typeof h === 'string')) {
            throw Error(`Invalid session file ${filename}`);
          }
          ReplActiveInputActions.playCommands(history);
          return;
        } catch(e) {
          err = e;
        }
      }

      ipcRenderer.send('application:message-box', {
        title: 'Load session error',
        buttons: ['Close'],
        type: 'error',
        message: err.toString()
      });
    });
  }

  onExport(sender, filename) {
    let {history} = ReplStore.getStore();
    let data = JSON.stringify(_.map(history, (h) => h.plainCode));
    writeFile(filename, data, { encoding: ReplConstants.REPL_ENCODING }, (err) => {
      let options = { buttons: ['Close'] };
      if(err) {
        options = _.extend(options, {
          title: 'Export Error',
          type: 'error',
          message: err.name || ' Error',
          detail: err.toString()
        });
      } else {
        options = _.extend(options, {
          title: 'Session saved',
          type: 'info',
          message: `Session saved to ${filename}`
        });
      }
      ipcRenderer.send('application:message-box', options);
    });
  }

  onAddPath(sender, paths) {
    ReplCommon.addToPath(paths, ReplContext.getContext());
  }

  onContextMenu(e) {
    e.preventDefault();
    this.menu.popup(remote.getCurrentWindow());
  }

  onSetREPLMode(mode) {
    ReplStore.onSetREPLMode(mode);
    ReplStatusBarActions.updateMode(mode);
    global.Mancy.session.mode = mode;
  }

  onPaste(e) {
    e.preventDefault();
    var text = e.clipboardData.getData("text/plain");
    document.execCommand("insertHTML", false, text);
    ReplSuggestionActions.removeSuggestion();
  }

  onKeydown(e) {
    if(ReplDOMEvents.isEnter(e)) {
      ReplDOM.scrollToEnd();
      return;
    }
    if(e.ctrlKey && ReplDOMEvents.isSpace(e)) {
      ReplActiveInputActions.performAutoComplete();
      return;
    }
    if(e.ctrlKey && e.shiftKey && ReplDOMEvents.isNumber(e)) {
      let num = e.which - ReplDOMEvents.zero;
      ReplStore.onReloadPromptByIndex(num + 1, true);
      return;
    }
  }

  onFormatPromptCode() {
    ReplActiveInputActions.formatCode();
  }

  onCollapseAll() {
    ReplStore.collapseAll();
  }

  onExpandAll() {
    ReplStore.expandAll();
  }

  onBreakPrompt() {
    ReplActiveInputActions.breakPrompt();
  }

  onClearCommands() {
    ReplStore.clearStore();
    ReplConsoleActions.clear();
  }

  onStateChange() {
    this.setState(ReplStore.getStore());
  }

  onDrag(e) {
    let replConsole = document.getElementsByClassName('repl-console')[0];
    let replContainerRight = document.getElementsByClassName('repl-container-right')[0];

    let {clientX} = e;
    let {width} = document.defaultView.getComputedStyle(replConsole);
    let initWidth = parseInt(width, 10);

    let startDrag = (e) => {
      let adj = e.clientX - clientX;
      replContainerRight.style.flex = '0 0  ' + (initWidth - adj) + 'px';
    }

    let stopDrag = (e) => {
      document.documentElement.removeEventListener('mousemove', startDrag, false);
      document.documentElement.removeEventListener('mouseup', stopDrag, false);
    }

    document.documentElement.addEventListener('mousemove', startDrag, false);
    document.documentElement.addEventListener('mouseup', stopDrag, false);
  }

  onToggleConsole() {
    this.reloadPrompt = false;
    ReplStore.toggleConsole();
  }

  onStdMessage(data, type) {
    let {formattedOutput} = ReplOutput.some(data).highlight(data);
    ReplConsoleActions.addEntry({
      type: type,
      data: [formattedOutput]
    });
    this.onConsoleChange();
  }

  onStdout(msg) {
    this.onStdMessage(msg.data ? msg.data.toString() : msg, 'log');
  }

  onStderr(msg) {
    this.onStdMessage(msg.data ? msg.data.toString() : msg, 'error');
  }

  onConsole({type, data}) {
    if(data.length === 0) { return; }
    let results;
    if(data.length > 1 && typeof data[0] === 'string' && data[0].match(/%[%sdj]/)) {
      results = [format.apply(null, data)];
    }
    else {
      results = _.reduce(data, function(result, datum) {
        let {formattedOutput} = ReplOutput.some(datum).highlight(datum);
        result.push(formattedOutput);
        return result;
      }, []);
    }

    ReplConsoleActions.addEntry({
      type: type,
      data: results
    });
    this.onConsoleChange(type);
  }

  onConsoleChange(type) {
    let currentWindow = remote.getCurrentWindow();
    if(!currentWindow.isFocused() && process.platform === 'darwin') {
      ipcRenderer.send('application:dock-message-notification', currentWindow.id);
    }
    if(this.state.showConsole) { return; }
    ReplStore.showBell();
  }

  getPromptKey() {
    if(!this.state.reloadPrompt) {
      return this.activePromptKey;
    }
    this.activePromptKey = Date.now();
    return this.activePromptKey;
  }

  render() {
    // force to recreate ReplPrompt
    return (

          <ReplEntries entries={this.state.entries} />
          <ReplPrompt key={this.getPromptKey()}
            history={this.state.history}
            historyIndex={this.state.historyIndex}
            historyStaged={this.state.historyStaged}
            command={this.state.command}
            cursor= {this.state.cursor} />

        {
          this.state.showConsole
            ?

                  <ReplConsole />

            : null
        }

        <ReplStatusBar history={this.state.entries}
          showConsole={this.state.showConsole}
          showBell={this.state.showBell}
          onToggleConsole={this.onToggleConsole}/>

    );
  }
}
<commit_msg>:bug: paste as plaint text #80<commit_after>import React from 'react';
import _ from 'lodash';
import ReplEntries from './ReplEntries';
import ReplPrompt from './ReplPrompt';
import ReplStatusBar from './ReplStatusBar';
import ReplStore from '../stores/ReplStore';
import ReplDOMEvents from '../common/ReplDOMEvents';
import ReplDOM from '../common/ReplDOM';
import ReplActiveInputActions from '../actions/ReplActiveInputActions';
import ReplPreferencesActions from '../actions/ReplPreferencesActions';
import ReplConsoleActions from '../actions/ReplConsoleActions';
import ReplSuggestionActions from '../actions/ReplSuggestionActions';
import ReplStatusBarActions from '../actions/ReplStatusBarActions';
import Reflux from 'reflux';
import {ipcRenderer} from 'electron';
import {writeFile, readFile} from 'fs';
import {EOL} from 'os';
import remote from 'remote';
import ReplStreamHook from '../common/ReplStreamHook';
import ReplConsoleHook from '../common/ReplConsoleHook';
import ReplConsole from './ReplConsole';
import ReplOutput from '../common/ReplOutput';
import ContextMenu from '../menus/context-menu.json';
import ReplConstants from '../constants/ReplConstants';
import ReplContext from '../common/ReplContext';
import ReplCommon from '../common/ReplCommon';
import ReplLanguages from '../languages/ReplLanguages';
import {format} from 'util';

export default class Repl extends React.Component {
  constructor(props) {
    super(props);
    _.each([
      'onStateChange', 'onPaste', 'onContextMenu',
      'onKeydown', 'onBreakPrompt', 'onClearCommands',
      'onCollapseAll', 'onExpandAll', 'onDrag', 'onToggleConsole', 'onFormatPromptCode',
      'onStdout', 'onStderr', 'onStdMessage', 'onConsole', 'onConsoleChange', 'getPromptKey',
      'onImport', 'onExport', 'onAddPath', 'loadPreferences', 'onSaveCommands', 'onLoadScript',
      'checkNewRelease', 'onNewRelease', 'resizeWindow', 'onSetREPLMode', 'loadStartupScript', 'onInit'
    ], (field) => {
      this[field] = this[field].bind(this);
    });

    this.loadPreferences();
    ReplCommon.addUserDataToPath(ReplContext.getContext());
    ReplCommon.addToPath(global.Mancy.preferences.npmPaths, ReplContext.getContext());

    this.state = _.cloneDeep(ReplStore.getStore());
  }

  componentDidMount() {
    // set REPL language
    ReplLanguages.setREPL(global.Mancy.preferences.lang);
    this.setupContextMenu();
    this.activePromptKey = Date.now();

    //register events
    this.unsubscribe = ReplStore.listen(this.onStateChange);

    window.addEventListener('paste', this.onPaste, false);
    window.addEventListener('contextmenu', this.onContextMenu, false);
    window.addEventListener('keydown', this.onKeydown, false);

    // hooks
    ReplStreamHook.on('stdout', this.onStdout);
    ReplStreamHook.on('stderr', this.onStderr);

    ReplConsoleHook.on('console', this.onConsole);

    ipcRenderer.on('application:import-file', this.onImport);
    ipcRenderer.on('application:export-file', this.onExport);
    ipcRenderer.on('application:add-path', this.onAddPath);
    ipcRenderer.on('application:save-as', this.onSaveCommands);
    ipcRenderer.on('application:load-file', this.onLoadScript);

    ipcRenderer.on('application:prompt-clear-all', this.onClearCommands);
    ipcRenderer.on('application:prompt-expand-all', this.onExpandAll);
    ipcRenderer.on('application:prompt-collapse-all', this.onCollapseAll);
    ipcRenderer.on('application:prompt-break', this.onBreakPrompt);
    ipcRenderer.on('application:prompt-format', this.onFormatPromptCode);

    ipcRenderer.on('application:prompt-mode', (sender, value) => this.onSetREPLMode(value));
    ipcRenderer.on('application:prompt-language', (sender, value) =>  {
      global.Mancy.session.lang = value;
      ReplLanguages.setREPL(value);
      ReplStatusBarActions.updateLanguage(value);
    });

    ipcRenderer.on('application:preferences', ReplPreferencesActions.openPreferences);

    ipcRenderer.on('application:preference-theme-dark', () => ReplPreferencesActions.setTheme('Dark Theme'));
    ipcRenderer.on('application:preference-theme-light', () => ReplPreferencesActions.setTheme('Light Theme'));

    ipcRenderer.on('application:view-theme-dark', () => document.body.className = 'dark-theme');
    ipcRenderer.on('application:view-theme-light', () => document.body.className = 'light-theme');

    ipcRenderer.on('application:new-release', this.onNewRelease);
    this.onInit();
  }

  onInit() {
    this.checkNewRelease();
    this.onSetREPLMode(global.Mancy.preferences.mode);
    ReplPreferencesActions.setTheme(global.Mancy.preferences.theme);

    this.resizeWindow();
    this.loadStartupScript();
    ipcRenderer.send('application:prompt-on-close', global.Mancy.preferences.promptOnClose);
  }

  onLoadScript(sender, script) {
    if(script) {
      let ext = script.replace(/(?:.*)\.(\w+)$/, '$1');
      let lang = ReplLanguages.getLangName(ext);
      if(lang) {
        ReplLanguages.setREPL(lang);
        ReplActiveInputActions.playCommands([`.load ${script}`]);
        if(lang !== global.Mancy.session.lang) {
          setTimeout(() => {
            ReplLanguages.setREPL(global.Mancy.session.lang);
            ReplStore.onReloadPrompt('');
          }, 200);
        }
      } else {
        let options = {
          buttons: ['Close'],
          title: 'Failed to load script',
          type: 'error',
          message: `Failed to load '${script}'. '${ext}' extension is not supported.`
        };
        ipcRenderer.send('application:message-box', options);
      }
    }
  }

  loadStartupScript() {
    this.onLoadScript(null, global.Mancy.preferences.loadScript);
  }

  resizeWindow() {
    let setSize = (w, h) => localStorage.setItem('window', JSON.stringify({ width: w, height: h }));
    let win = remote.getCurrentWindow();
    let lastWindow = JSON.parse(localStorage.getItem('window'));
    let [width, height] = win.getSize();
    if(!lastWindow) { setSize(width, height) }
    else {
      try {
        win.setSize(lastWindow.width, lastWindow.height);
      } catch(e) {}
    }
    win.on('resize', () => {
      let [width, height] = win.getSize();
      setSize(width, height);
    });
  }

  setupContextMenu() {
    let Menu = remote.require('menu');
    let contextMenu = _.cloneDeep(ContextMenu);
    let actionTemplates = [
      {
        label: 'Clear All',
        accelerator: 'Ctrl+L',
        click: this.onClearCommands
      },
      {
        label: 'Collapse All',
        accelerator: 'CmdOrCtrl+K',
        click: this.onCollapseAll
      },
      {
        label: 'Expand All',
        accelerator: 'CmdOrCtrl+E',
        click: this.onExpandAll
      },
      {
        label: 'Break Prompt',
        accelerator: 'Ctrl+D',
        click: this.onBreakPrompt
      },
      {
        label: 'Format',
        accelerator: 'Shift+Ctrl+F',
        click: this.onFormatPromptCode
      }
    ];

    _.each(actionTemplates, (template) => contextMenu.push(template));

    this.menu = Menu.buildFromTemplate(contextMenu);
  }

  componentWillUnmount() {
    this.unsubscribe();
    window.removeEventListener('paste', this.onPaste, false);
    window.removeEventListener('contextmenu', this.onContextMenu, false);
    window.removeEventListener('keydown', this.onKeydown, false);

    ReplStreamHook.removeListener('stdout', this.onStdout);
    ReplStreamHook.removeListener('stderr', this.onStderr);
    ReplStreamHook.disable();

    ReplConsoleHook.removeListener('console', this.onConsole);
    ReplConsoleHook.disable();
  }

  checkNewRelease() {
    setTimeout(() => ipcRenderer.send('application:check-new-release'), 2000);
  }

  loadPreferences() {
    let preferences = JSON.parse(localStorage.getItem('preferences'));
    ipcRenderer.send('application:sync-preference', preferences);
  }

  onNewRelease(release) {
    ReplStatusBarActions.newRelease(release);
  }

  onSaveCommands(sender, filename) {
    let {history} = ReplStore.getStore();
    let data = _.chain(history)
      .map((h) => h.plainCode)
      .filter((c) => !/^\s*\./.test(c))
      .value()
      .join(EOL);

    writeFile(filename, data, { encoding: ReplConstants.REPL_ENCODING }, (err) => {
      let options = { buttons: ['Close'] };
      if(err) {
        options = _.extend(options, {
          title: 'Save Error',
          type: 'error',
          message: err.name || ' Error',
          detail: err.toString()
        });
      } else {
        options = _.extend(options, {
          title: 'Commands saved',
          type: 'info',
          message: `Commands saved to ${filename}`
        });
      }
      ipcRenderer.send('application:message-box', options);
    });
  }

  onImport(sender, filename) {
    readFile(filename, (err, data) => {
      if(!err) {
        try {
          let history = JSON.parse(data);
          if(!Array.isArray(history) || !_.every(history, (h) => typeof h === 'string')) {
            throw Error(`Invalid session file ${filename}`);
          }
          ReplActiveInputActions.playCommands(history);
          return;
        } catch(e) {
          err = e;
        }
      }

      ipcRenderer.send('application:message-box', {
        title: 'Load session error',
        buttons: ['Close'],
        type: 'error',
        message: err.toString()
      });
    });
  }

  onExport(sender, filename) {
    let {history} = ReplStore.getStore();
    let data = JSON.stringify(_.map(history, (h) => h.plainCode));
    writeFile(filename, data, { encoding: ReplConstants.REPL_ENCODING }, (err) => {
      let options = { buttons: ['Close'] };
      if(err) {
        options = _.extend(options, {
          title: 'Export Error',
          type: 'error',
          message: err.name || ' Error',
          detail: err.toString()
        });
      } else {
        options = _.extend(options, {
          title: 'Session saved',
          type: 'info',
          message: `Session saved to ${filename}`
        });
      }
      ipcRenderer.send('application:message-box', options);
    });
  }

  onAddPath(sender, paths) {
    ReplCommon.addToPath(paths, ReplContext.getContext());
  }

  onContextMenu(e) {
    e.preventDefault();
    this.menu.popup(remote.getCurrentWindow());
  }

  onSetREPLMode(mode) {
    ReplStore.onSetREPLMode(mode);
    ReplStatusBarActions.updateMode(mode);
    global.Mancy.session.mode = mode;
  }

  onPaste(e) {
    e.preventDefault();
    var text = e.clipboardData.getData("text/plain");
    document.execCommand("insertText", false, text);
    ReplSuggestionActions.removeSuggestion();
  }

  onKeydown(e) {
    if(ReplDOMEvents.isEnter(e)) {
      ReplDOM.scrollToEnd();
      return;
    }
    if(e.ctrlKey && ReplDOMEvents.isSpace(e)) {
      ReplActiveInputActions.performAutoComplete();
      return;
    }
    if(e.ctrlKey && e.shiftKey && ReplDOMEvents.isNumber(e)) {
      let num = e.which - ReplDOMEvents.zero;
      ReplStore.onReloadPromptByIndex(num + 1, true);
      return;
    }
  }

  onFormatPromptCode() {
    ReplActiveInputActions.formatCode();
  }

  onCollapseAll() {
    ReplStore.collapseAll();
  }

  onExpandAll() {
    ReplStore.expandAll();
  }

  onBreakPrompt() {
    ReplActiveInputActions.breakPrompt();
  }

  onClearCommands() {
    ReplStore.clearStore();
    ReplConsoleActions.clear();
  }

  onStateChange() {
    this.setState(ReplStore.getStore());
  }

  onDrag(e) {
    let replConsole = document.getElementsByClassName('repl-console')[0];
    let replContainerRight = document.getElementsByClassName('repl-container-right')[0];

    let {clientX} = e;
    let {width} = document.defaultView.getComputedStyle(replConsole);
    let initWidth = parseInt(width, 10);

    let startDrag = (e) => {
      let adj = e.clientX - clientX;
      replContainerRight.style.flex = '0 0  ' + (initWidth - adj) + 'px';
    }

    let stopDrag = (e) => {
      document.documentElement.removeEventListener('mousemove', startDrag, false);
      document.documentElement.removeEventListener('mouseup', stopDrag, false);
    }

    document.documentElement.addEventListener('mousemove', startDrag, false);
    document.documentElement.addEventListener('mouseup', stopDrag, false);
  }

  onToggleConsole() {
    this.reloadPrompt = false;
    ReplStore.toggleConsole();
  }

  onStdMessage(data, type) {
    let {formattedOutput} = ReplOutput.some(data).highlight(data);
    ReplConsoleActions.addEntry({
      type: type,
      data: [formattedOutput]
    });
    this.onConsoleChange();
  }

  onStdout(msg) {
    this.onStdMessage(msg.data ? msg.data.toString() : msg, 'log');
  }

  onStderr(msg) {
    this.onStdMessage(msg.data ? msg.data.toString() : msg, 'error');
  }

  onConsole({type, data}) {
    if(data.length === 0) { return; }
    let results;
    if(data.length > 1 && typeof data[0] === 'string' && data[0].match(/%[%sdj]/)) {
      results = [format.apply(null, data)];
    }
    else {
      results = _.reduce(data, function(result, datum) {
        let {formattedOutput} = ReplOutput.some(datum).highlight(datum);
        result.push(formattedOutput);
        return result;
      }, []);
    }

    ReplConsoleActions.addEntry({
      type: type,
      data: results
    });
    this.onConsoleChange(type);
  }

  onConsoleChange(type) {
    let currentWindow = remote.getCurrentWindow();
    if(!currentWindow.isFocused() && process.platform === 'darwin') {
      ipcRenderer.send('application:dock-message-notification', currentWindow.id);
    }
    if(this.state.showConsole) { return; }
    ReplStore.showBell();
  }

  getPromptKey() {
    if(!this.state.reloadPrompt) {
      return this.activePromptKey;
    }
    this.activePromptKey = Date.now();
    return this.activePromptKey;
  }

  render() {
    // force to recreate ReplPrompt
    return (

          <ReplEntries entries={this.state.entries} />
          <ReplPrompt key={this.getPromptKey()}
            history={this.state.history}
            historyIndex={this.state.historyIndex}
            historyStaged={this.state.historyStaged}
            command={this.state.command}
            cursor= {this.state.cursor} />

        {
          this.state.showConsole
            ?

                  <ReplConsole />

            : null
        }

        <ReplStatusBar history={this.state.entries}
          showConsole={this.state.showConsole}
          showBell={this.state.showBell}
          onToggleConsole={this.onToggleConsole}/>

    );
  }
}
<|endoftext|>


<commit_before>import React from "react";
import Footer from "./footer";
import Banner from "./banner";

export default class Home extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            body: null
        }
    }
    componentDidMount = () => {
        let unauthBody = (

We Produce Your Future

Placeholder Video

          <video controls><source src="/static/videos/placeholder.mp4"/></video>

Reviews

There are no reviews

      );
      this.setState({
          body: sessionStorage.getItem("loggedIn") ? (

Edit Music

Title

            <input className="music-up" type="text" id="title" placeholder="Enter song title" required/>

Artist Name

            <input className="music-up" type="text" id="artist-name" placeholder="Enter artist name" required/>

Genre

            <input className="music-up" type="text" id="genre" placeholder="Genre" required/>

Price

            <input className="music-up" type="text" id="price" placeholder="Price" required/>

Video ID

            <input className="music-up" type="text" id="video-id" placeholder="Video ID, e.g., d1eaQrxA6ZE" required/>

            <button className="update" onClick={this.handleMusicUpdate}>Update Music</button>

Edit Events

Location

            <input className="event-up" type="text" id="location" placeholder="Enter location" required/>

Description

            <textarea className="event-up" id="description" required></textarea>

            <button className="update" onClick={this.handleEventsUpdate}>Update Events</button>

      ) : unauthBody
      });
    }
    handleMusicUpdate = () => {
        let title = document.getElementById("title").value;
        let artist = document.getElementById("artist-name").value;
        let genre = document.getElementById("genre").value;
        let price = document.getElementById("price").value;
        let videoId = document.getElementById("video-id").value;
        if (!title || !artist || !genre || !price || !videoId) {
          return;
        }
        let musicData = {
          title: title,
          artist: artist,
          genre: genre,
          price: price,
          id: videoId
        };
        fetch("/update-music", {
          method: "post",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify(musicData)
        }).then((res) => {
          return res.json();
        }).then((json) => {
          if (json.data === "success") {
              alert("Successfully Updated Music");
              Array.prototype.forEach.call(document.getElementsByClassName("music-up"), (element) => {
                  element.value = "";
              });
          }
        })
    };
    handleEventsUpdate = () => {
      let location = document.getElementById("location").value;
      let description = document.getElementById("description").value;
      if (!location || !description) {
        return;
      }
      let eventsData = {
        location: location,
        description: description
      }
      fetch("/events-update", {
        method: "post",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(eventsData)
      }).then((res) => {
        return res.json();
      }).then((json) => {
        if (json.data === "success") {
            alert("Successfully Updated Events");
            Array.prototype.forEach.call(document.getElementsByClassName("event-up"), (element) => {
                element.value = "";
            })
          }
      })
    }
    render() {
        return (

                <Banner/>

                  {this.state.body}

        )
    }
}<commit_msg>add width and height to video<commit_after>import React from "react";
import Footer from "./footer";
import Banner from "./banner";

export default class Home extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            body: null
        }
    }
    componentDidMount = () => {
        let unauthBody = (

We Produce Your Future

Placeholder Video

          <video id="home-video" preload="none" controls><source src="/static/videos/placeholder.mp4"/></video>

Reviews

There are no reviews

      );
      this.setState({
          body: sessionStorage.getItem("loggedIn") ? (

Edit Music

Title

            <input className="music-up" type="text" id="title" placeholder="Enter song title" required/>

Artist Name

            <input className="music-up" type="text" id="artist-name" placeholder="Enter artist name" required/>

Genre

            <input className="music-up" type="text" id="genre" placeholder="Genre" required/>

Price

            <input className="music-up" type="text" id="price" placeholder="Price" required/>

Video ID

            <input className="music-up" type="text" id="video-id" placeholder="Video ID, e.g., d1eaQrxA6ZE" required/>

            <button className="update" onClick={this.handleMusicUpdate}>Update Music</button>

Edit Events

Location

            <input className="event-up" type="text" id="location" placeholder="Enter location" required/>

Description

            <textarea className="event-up" id="description" required></textarea>

            <button className="update" onClick={this.handleEventsUpdate}>Update Events</button>

      ) : unauthBody
      });
    }
    handleMusicUpdate = () => {
        let title = document.getElementById("title").value;
        let artist = document.getElementById("artist-name").value;
        let genre = document.getElementById("genre").value;
        let price = document.getElementById("price").value;
        let videoId = document.getElementById("video-id").value;
        if (!title || !artist || !genre || !price || !videoId) {
          return;
        }
        let musicData = {
          title: title,
          artist: artist,
          genre: genre,
          price: price,
          id: videoId
        };
        fetch("/update-music", {
          method: "post",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify(musicData)
        }).then((res) => {
          return res.json();
        }).then((json) => {
          if (json.data === "success") {
              alert("Successfully Updated Music");
              Array.prototype.forEach.call(document.getElementsByClassName("music-up"), (element) => {
                  element.value = "";
              });
          }
        })
    };
    handleEventsUpdate = () => {
      let location = document.getElementById("location").value;
      let description = document.getElementById("description").value;
      if (!location || !description) {
        return;
      }
      let eventsData = {
        location: location,
        description: description
      }
      fetch("/events-update", {
        method: "post",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(eventsData)
      }).then((res) => {
        return res.json();
      }).then((json) => {
        if (json.data === "success") {
            alert("Successfully Updated Events");
            Array.prototype.forEach.call(document.getElementsByClassName("event-up"), (element) => {
                element.value = "";
            })
          }
      })
    }
    render() {
        return (

                <Banner/>

                  {this.state.body}

        )
    }
}<|endoftext|>


<commit_before>'use strict';

import React, {
  AsyncStorage,
  Component,
  Dimensions,
  Image,
  StyleSheet,
  Text,
  TouchableOpacity,
  View
} from 'react-native';

import Firebase from 'firebase';
import Icon from 'react-native-vector-icons/MaterialIcons';

import GridView from './grid-view';
import Profile from "../scenes/profile";
import SearchBar from './search-bar';

let database = new Firebase("poopapp1.firebaseio.com");

class Post extends Component {
  constructor(props) {
    super(props);

    this.state = {};
  }

  componentDidMount() {
    var postSnapshot = this.props.id;
    var self = this;

    database.once("value", function(snapshot){
      var userid = postSnapshot.val().userID;
      var userSnapshot = snapshot.child("users/" + userid);
      var proPic = userSnapshot.val().profilePic;

      self.setState({
        postID: postSnapshot.key().toString(),
        userID: userid,
        user: postSnapshot.val().user,
        userPhoto: proPic,
        photo: postSnapshot.val().photoID,
        rating: postSnapshot.val().rating,
        description: postSnapshot.val().description,
      });
    });
  }

  profile() {
    this.props.navigator.push({component: Profile, state: this.state.userID});
  }

  picture() {
    // TODO
    // this.props.navigator.push({component: Post, state: post.postID});
  }

  favorite() {
    // post.isFavorite = !post.isFavorite;
    // TODO: update database

    // this is probably bad because it rerenders the entire scene. only really needs to update the Icon's color prop
    this.forceUpdate();
  }

  messages() {
    alert("Go to messages page.");
  }

  getFavoriteColor() {
    return "orange";
  }

  render() {
    return(
      <View style = {styles.item}>
        <View>
          <TouchableOpacity
            style = {styles.userView}
            onPress = {this.profile.bind(this)}>
            <Image
              resizeMode = "cover"
              style = {styles.userPhoto}
              source = {{uri: this.state.userPhoto}}
            />
            <Text style = {styles.userName}>
              {this.state.user}
            </Text>
          </TouchableOpacity>
        </View>
        <View style = {styles.descriptionView}>
          <Text style = {styles.description}>
            {this.state.description}
          </Text>
        </View>
        <TouchableOpacity
          style = {styles.photo}
          onPress = {this.picture.bind(this)}>
          <Image
            resizeMode = "cover"
            style = {{flex: 1}}
            source = {{uri: this.state.photo}}
          />
        </TouchableOpacity>
        <View style = {styles.buttonView}>
          <TouchableOpacity
            style = {styles.button}
            onPress = {this.favorite.bind(this)}>
            <Icon
              name = "star"
              size = {16}
              color = {this.getFavoriteColor()}
            />
          </TouchableOpacity>
          <TouchableOpacity
            style = {styles.button}
            onPress = {this.messages.bind(this)}>
            <Icon
              name = "feedback"
              size = {16}
              color = "green"
            />
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  item: {
    backgroundColor: 'white',
    borderWidth: 1,
    borderColor: 'gray',
    margin: 8,
  },
  userView: {
    flexDirection: 'row',
    margin: 8,
  },
  userPhoto: {
    width: 30,
    height: 30,
    borderRadius: 90,
    padding: 4,
  },
  userName: {
    padding: 4,
  },
  photo: {
    width: Dimensions.get("window").width - 16,
    height: (Dimensions.get("window").width - 16) * 9 / 16,
  },
  descriptionView: {
    padding: 12,
  },
  description: {
    color: 'black'
  },
  buttonView: {
    justifyContent: 'center',
    flexDirection: 'row',
  },
  button: {
    marginLeft: 8,
    marginRight: 8,
    marginTop: 4,
    marginBottom: 4,
  }
});

module.exports = Post;
<commit_msg>Made like/unlike function<commit_after>'use strict';

import React, {
  AsyncStorage,
  Component,
  Dimensions,
  Image,
  StyleSheet,
  Text,
  TouchableOpacity,
  View
} from 'react-native';

import Firebase from 'firebase';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import IonIcon from 'react-native-vector-icons/Ionicons';

import GridView from './grid-view';
import Profile from "../scenes/profile";
import SearchBar from './search-bar';

let database = new Firebase("poopapp1.firebaseio.com");

class Post extends Component {
  constructor(props) {
    super(props);

    this.state = {
      liked: false
    };
  }

  componentDidMount() {
    var postSnapshot = this.props.id;
    var self = this;

    database.on("value", function(snapshot){
      var userid = postSnapshot.val().userID;
      var userSnapshot = snapshot.child("users/" + userid);
      var proPic = userSnapshot.val().profilePic;
      var likeData = snapshot.child("posts/" + postSnapshot.key().toString() + "/ratedList");

      var didLike = false;
      if (typeof likeData != 'undefined'){
        likeData.forEach(function(userRated) {
          if (userRated.val().userId == userid){
            didLike = true;
          }
        });
      }

      self.setState({
        postID: postSnapshot.key().toString(),
        userID: userid,
        user: postSnapshot.val().user,
        userPhoto: proPic,
        photo: postSnapshot.val().photoID,
        rating: postSnapshot.val().rating,
        description: postSnapshot.val().description,
        liked: didLike,
      });
    });
  }

  profile() {
    this.props.navigator.push({component: Profile, state: this.state.userID});
  }

  like(){
    var postRated = database.child("posts/" + this.state.postID + "/ratedList");
    var ratedVal = database.child("posts/" + this.state.postID + "/rating");

    if (!this.state.liked){
      postRated.push({userId: this.state.userID});
      //postRef.update({rating: (this.state.rating + 1)}, function(){});
      ratedVal.transaction(function(currentRating){
        return currentRating+1;
      });
    }
    else{
      var postSnapshot = this.props.id;
      var self = this;

      database.once("value", function(snapshot){
        var userid = postSnapshot.val().userID;
        var userSnapshot = snapshot.child("users/" + userid);
        var likeData = snapshot.child("posts/" + postSnapshot.key().toString() + "/ratedList");

        if (typeof likeData != 'undefined'){
          likeData.forEach(function(userRated) {
            if (userRated.val().userId == userid){
              var toDelete = database.child("posts/" + self.state.postID + "/ratedList/" + userRated.key().toString() + "/userId");
              toDelete.set(null);
            }
          });
        }
      });
      ratedVal.transaction(function(currentRating){
        return currentRating-1;
      });
    }
  }

  getLikeColor(){
    if (this.state.liked){
      return "green";
    }
    return "grey";
  }

  picture(){
    //TODO
    //this.props.navigator.push({component: Post, state: post.postID});
  }

  favorite() {
    // post.isFavorite = !post.isFavorite;
    // TODO: update database

    // this is probably bad because it rerenders the entire scene. only really needs to update the Icon's color prop
    this.forceUpdate();
  }

  messages() {
    alert("Go to messages page.");
  }

  getFavoriteColor() {
    return "orange";
  }

  render() {
    return(
      <View style = {styles.item}>
        <View>
          <TouchableOpacity
            style = {styles.userView}
            onPress = {this.profile.bind(this)}>
            <Image
              resizeMode = "cover"
              style = {styles.userPhoto}
              source = {{uri: this.state.userPhoto}}
            />
            <Text style = {styles.userName}>
              {this.state.user}
            </Text>
          </TouchableOpacity>
        </View>
        <View style = {styles.descriptionView}>
          <Text style = {styles.description}>
            {this.state.description}
          </Text>
        </View>
        <TouchableOpacity
          style = {styles.photo}
          onPress = {this.picture.bind(this)}>
          <Image
            resizeMode = "cover"
            style = {{flex: 1}}
            source = {{uri: this.state.photo}}
          />
        </TouchableOpacity>
        <View style = {styles.buttonView}>
          <TouchableOpacity
            style = {styles.button}
            onPress = {this.like.bind(this)}>
            <IonIcon
              name = "ios-pizza"
              size = {16}
              color = {this.getLikeColor()}
            />
          </TouchableOpacity>
          <TouchableOpacity
            style = {styles.button}
            onPress = {this.favorite.bind(this)}>
            <MaterialIcon
              name = "star"
              size = {16}
              color = {this.getFavoriteColor()}
            />
          </TouchableOpacity>
          <TouchableOpacity
            style = {styles.button}
            onPress = {this.messages.bind(this)}>
            <MaterialIcon
              name = "feedback"
              size = {16}
              color = "green"
            />
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  item: {
    backgroundColor: 'white',
    borderWidth: 1,
    borderColor: 'gray',
    margin: 8,
  },
  userView: {
    flexDirection: 'row',
    margin: 8,
  },
  userPhoto: {
    width: 30,
    height: 30,
    borderRadius: 90,
    padding: 4,
  },
  userName: {
    padding: 4,
  },
  photo: {
    width: Dimensions.get("window").width - 16,
    height: (Dimensions.get("window").width - 16) * 9 / 16,
  },
  descriptionView: {
    padding: 12,
  },
  description: {
    color: 'black'
  },
  buttonView: {
    justifyContent: 'center',
    flexDirection: 'row',
  },
  button: {
    marginLeft: 8,
    marginRight: 8,
    marginTop: 4,
    marginBottom: 4,
  }
});

module.exports = Post;
<|endoftext|>


<commit_before>/**
 * Created by Ady on 2/20/2015.
 */

var mongoose = require('mongoose');
var Q = require('q');
var _ = require('underscore');
//require('./mongoData.js')(process.env['MONGOLAB_URI']);

var NodeCache = require("node-cache");
var myCache = new NodeCache({stdTTL: 300}); //5m default cache time

module.exports = function () {

    var Guild = mongoose.model('Guilds', {
        name: String,
        lastKnownIntel: String,
        isDeleted: {type: Boolean, default: false},
        deletedBy: String,
        deletionDate: Date,
        players: [
            {
                name: String,
                lvl: Number,
                def: Number,
                eqDef: Number,
                heroDef: Number,
                date: Date,
                insertedByGuild: String,
                insertedByUser: String,
                insertedByUserId: Number,
                isDeleted: {type: Boolean, default: false}
            }
        ]
    });

    var createNewGuild = function (guildName) {
        var g = new Guild({
            name: guildName,
            players: [],
            isDeleted: false
        });
        g.isNew = true;
        return g;
    }

    return {

        getGuildData: function (guildName, callback) {
            var that = this;
            var cacheKey = 'guild_' + guildName.replace(/\s/g, '_');
            var cacheItem = myCache.get(cacheKey);
            if (cacheItem) {
                callback(cacheItem[cacheKey]);
            } else {
                Guild.find({$or: [{name: guildName, isDeleted: { $exists: false }},{name: guildName,isDeleted: false}]}, function (err, guilds) {
                    var item;
                    if (guilds.length == 0) {
                        item = createNewGuild(guildName);
                    } else {
                        item = guilds[0];
                    }
                    item._save = item.save;
                    item._cacheKey = cacheKey;
                    item.save = function (cb) {
                        if (this.isDeleted){
                            myCache.del(this._cacheKey);
                        }else {
                            myCache.set(this._cacheKey, this, 600);
                        }
                        this._save(cb);
                    };
                    myCache.set(cacheKey, item, 600);
                    callback(item);
                });
            }
        },
        getAllGuilds: function () {
            var defered = Q.defer();
            Guild.find({}, function (err, guilds) {
                defered.resolve(guilds);
            });
            return defered.promise;
        },
        removeGuild: function (guildName, deletingUser) {
            var defered = Q.defer();
            this.getGuildData(guildName, function (guild) {
                if (guild.isNew) {
                    defered.resolve('Guild not found in DB');
                    return;
                } else {
                    guild.isDeleted=true;
                    guild.deletedBy=deletingUser;
                    guild.deletionDate= Date.now();
                    guild.save(function () {
                        defered.resolve('Guild [' + guildName + '] removed ');
                    });
                }
            });
            return defered.promise;
        }
    };
}();<commit_msg>cache upgrade<commit_after>/**
 * Created by Ady on 2/20/2015.
 */

var mongoose = require('mongoose');
var Q = require('q');
var _ = require('underscore');
//require('./mongoData.js')(process.env['MONGOLAB_URI']);

var NodeCache = require("node-cache");
var myCache = new NodeCache({stdTTL: 300}); //5m default cache time

module.exports = function () {

    var Guild = mongoose.model('Guilds', {
        name: String,
        lastKnownIntel: String,
        isDeleted: {type: Boolean, default: false},
        deletedBy: String,
        deletionDate: Date,
        players: [
            {
                name: String,
                lvl: Number,
                def: Number,
                eqDef: Number,
                heroDef: Number,
                date: Date,
                insertedByGuild: String,
                insertedByUser: String,
                insertedByUserId: Number,
                isDeleted: {type: Boolean, default: false}
            }
        ]
    });

    var createNewGuild = function (guildName) {
        var g = new Guild({
            name: guildName,
            players: [],
            isDeleted: false
        });
        g.isNew = true;
        return g;
    }

    return {

        getGuildData: function (guildName, callback) {
            var that = this;
            var cacheKey = 'guild_' + guildName.replace(/\s/g, '_');
            var cacheItem = myCache.get(cacheKey);
            if (cacheItem) {
                callback(cacheItem);
            } else {
                Guild.find({$or: [{name: guildName, isDeleted: { $exists: false }},{name: guildName,isDeleted: false}]}, function (err, guilds) {
                    var item;
                    if (guilds.length == 0) {
                        item = createNewGuild(guildName);
                    } else {
                        item = guilds[0];
                    }
                    item._save = item.save;
                    item._cacheKey = cacheKey;
                    item.save = function (cb) {
                        if (this.isDeleted){
                            myCache.del(this._cacheKey);
                        }else {
                            myCache.set(this._cacheKey, this, 600);
                        }
                        this._save(cb);
                    };
                    myCache.set(cacheKey, item, 600);
                    callback(item);
                });
            }
        },
        getAllGuilds: function () {
            var defered = Q.defer();
            Guild.find({}, function (err, guilds) {
                defered.resolve(guilds);
            });
            return defered.promise;
        },
        removeGuild: function (guildName, deletingUser) {
            var defered = Q.defer();
            this.getGuildData(guildName, function (guild) {
                if (guild.isNew) {
                    defered.resolve('Guild not found in DB');
                    return;
                } else {
                    guild.isDeleted=true;
                    guild.deletedBy=deletingUser;
                    guild.deletionDate= Date.now();
                    guild.save(function () {
                        defered.resolve('Guild [' + guildName + '] removed ');
                    });
                }
            });
            return defered.promise;
        }
    };
}();<|endoftext|>


<commit_before>"use strict";

import Guard from './Guard';

class EventContext {
    constructor(modelId, eventType, event) {
        Guard.isString(modelId, "The modelId should be a string");
        Guard.isString(eventType, "The eventType should be a string");
        Guard.isDefined(event, "The event should be defined");
        this._modelId = modelId;
        this._eventType = eventType;
        this._event = event;
        this._isCanceled = false;
        this._isCommitted = false;
    }
    get isCanceled() {
        return this._isCanceled;
    }
    get isCommitted() {
        return this._isCommitted;
    }
    get modelId() {
        return this._modelId;
    }
    get event() {
        return this._event;
    }
    get eventType() {
        return this._eventType;
    }
    cancel() {
        if(!this._isCanceled) {
            this._isCanceled = true;
        } else {
            throw 'event is already canceled';
        }
    }
    commit() {
        if(!this._isCommitted) {
            this._isCommitted = true;
        } else {
            throw 'event is already committed';
        }
    }
}

export default EventContext;
<commit_msg>Throw an error, not just a plain string<commit_after>"use strict";

import Guard from './Guard';

class EventContext {
    constructor(modelId, eventType, event) {
        Guard.isString(modelId, "The modelId should be a string");
        Guard.isString(eventType, "The eventType should be a string");
        Guard.isDefined(event, "The event should be defined");
        this._modelId = modelId;
        this._eventType = eventType;
        this._event = event;
        this._isCanceled = false;
        this._isCommitted = false;
    }
    get isCanceled() {
        return this._isCanceled;
    }
    get isCommitted() {
        return this._isCommitted;
    }
    get modelId() {
        return this._modelId;
    }
    get event() {
        return this._event;
    }
    get eventType() {
        return this._eventType;
    }
    cancel() {
        if(!this._isCanceled) {
            this._isCanceled = true;
        } else {
            throw new Error('event is already cancelled');
        }
    }
    commit() {
        if(!this._isCommitted) {
            this._isCommitted = true;
        } else {
            throw 'event is already committed';
        }
    }
}

export default EventContext;
<|endoftext|>