<repo_name>casteem/insteem<file_sep>/src/components/StoryList/components/StoryListItem/components/StoryListItemExtra.js
import moment from "moment/moment";
import React from "react";
import steem from "steem";
import styled from "styled-components";
import { Link } from "react-router-dom";
import { Icon, Label } from "semantic-ui-react";

const Container = styled.div`
  color: gray;
`;

const StoryListItemExtra = ({ story }) => {
  return (
    <Container>
      {moment.utc(story.created).fromNow()} in{" "}
      <Label color="blue" size="small" basic>
        {story.category}
      </Label>
      by <Link to={`/@${story.author}`}>{story.author}</Link>{" "}
      <Label circular>
        {steem.formatter.reputation(story.author_reputation)}
      </Label>
      <Icon name="chevron up" /> {story.net_votes}
    </Container>
  );
};

export default StoryListItemExtra;
<file_sep>/src/services/helpers/filter.js
import _ from "lodash";
import { calculateReputationScore } from "./format";
import { parseMetadata } from "services/helpers/format";
import { contains, reject } from "ramda";

export const containsTag = (json, tag = "") => {
  const activeTags = parseMetadata(json).tags;
  const bool = contains(tag, activeTags);
  return bool;
};

export const rejectByTag = (stories, tags = []) => {
  stories = reject(story => containsTag(story.json_metadata, tags))(stories);
  return stories;
};

export const filterReputation = (postList = [], maxRep = 75, minRep = 25) => {
  return _.reject(postList, post => {
    const score = calculateReputationScore(post.author_reputation);
    if (score <= maxRep && score >= minRep) return false;
    return true;
  });
};

// Use all filter options at once.
export const filterAll = (
  postList = [],
  maxRep,
  minRep,
  maxChars,
  minChars
) => {
  return _.reject(postList, post => {
    const score = calculateReputationScore(post.author_reputation);
    const chars = _.get(post, "body_length");
    if (
      score <= maxRep &&
      score >= minRep &&
      chars >= minChars &&
      chars <= maxChars
    )
      return false;
    return true;
  });
};
<file_sep>/src/scenes/CategoryScene/index.js
export * from "./CategoryScene";
export { default } from "./CategoryScene";
<file_sep>/src/scenes/Mentions/MentionsScene/index.js
export { default } from "./MentionsScene";
<file_sep>/src/components/StoryList/components/StoryListItem/components/StoryListItemMeta.js
import React from "react";
import styled from "styled-components";
import moment from "moment";
import { Link } from "react-router-dom";
import { Label } from "semantic-ui-react";
import steem from "steem";

const Container = styled.div`
  color: gray;
  font-size: 0.9em;
  padding-top: 5px;
`;

const StoryListItemMeta = ({ story }) => {
  return (
    <Container>
      {moment.utc(story.created).fromNow()} in{" "}
      <Label color="blue" size="small" basic>
        {story.category}
      </Label>
      by <Link to={`/@${story.author}`}>{story.author}</Link>{" "}
      <Label circular>
        {steem.formatter.reputation(story.author_reputation)}
      </Label>
    </Container>
  );
};

export default StoryListItemMeta;
<file_sep>/src/scenes/Auth/SigninScene/Signin.scene.js
import React from "react";
import steem from "steem";
import { connect } from "react-redux";
import { signin } from "services/state/auth/actions";
import { Grid, Form, Input, Button, Message } from "semantic-ui-react";

// SignIn Form.
class SignIn extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      username: "",
      password: "",
      wif: ""
    };
  }

  signin(e) {
    e.preventDefault();
    const wif = this.state.password;
    // const wif = steem.auth.toWif(this.state.username, this.state.password, [
    //   "posting"
    // ]);
    const isWif = steem.auth.isWif(wif);
    if (!isWif) {
      alert("Please use a private posting key!");
      return;
    }

    this.props.onSignin(this.state.username, this.state.password, wif);
  }

  render() {
    return (
      <Grid centered>
        <Grid.Column width="6">
          <Form
            style={{ padding: 20, textAlign: "center" }}
            onSubmit={e => this.signin(e)}
          >
            <Form.Field required>
              <Input
                onChange={event =>
                  this.setState({ username: event.target.value })
                }
                value={this.state.username}
                placeholder="Username"
                required
              />
            </Form.Field>
            <Form.Field>
              <Input
                onChange={event =>
                  this.setState({ password: event.target.value })
                }
                value={this.state.password}
                placeholder="Private Posting Key"
                type="password"
                required
              />
            </Form.Field>
            <Button type="submit">Signin</Button>
            {/*<Button onClick={e => this.signin(e)}>Signin</Button>*/}
          </Form>
        </Grid.Column>
        <Message>
          Insteem uses your private posting key to interact with steem. This key
          is stored unencrypted in your browser and can only be used for posting
          and voting. Make sure you are only using your posting key. Keep your
          active/owner keys safe.
        </Message>
      </Grid>
    );
  }
}

const mapStateToProps = (state, ownProps) => {
  return {
    isSignedIn: state.auth.isSignedIn
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onSignin: (username, password, wif) => {
      dispatch(signin(username, password, wif));
    }
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(SignIn);
<file_sep>/src/scenes/StoryScene/components/StoryMetaBox.js
import React from "react";
import { Link } from "react-router-dom";
import { Segment, Label, Icon } from "semantic-ui-react";
import styled from "styled-components";
import moment from "moment";
import { parseMetadata } from "services/helpers/format";

const Author = styled.div`
  margin-bottom: 1rem;
`;
const Date = styled.div`
  margin-bottom: 1rem;
`;
// Wrap semantic Label Group in styled component.
const Tags = styled(Label.Group)`
  margin-bottom: 1rem;
`;

const renderTags = json => {
  return parseMetadata(json).tags.map(tag => (
    <Label key={tag} basic>
      {tag}
    </Label>
  ));
};

const StoryMetaBox = props => {
  const { story } = props;
  return (
    <Segment>
      <Author>
        <Link to={`/@${story.author}`}>{story.author}</Link>
      </Author>
      <Date>
        {moment(story.created)
          .utc()
          .format("LLL")}
      </Date>
      <Tags>{renderTags(story.json_metadata)}</Tags>
      <Icon name="chevron circle up" color="green" size="large" />{" "}
      {story.net_votes}
    </Segment>
  );
};

export default StoryMetaBox;
<file_sep>/src/scenes/Layout/Layout.js
import React from "react";
import styled from "styled-components";

const Container = styled.div`
  min-height: 100%;
  background-color: #f9f9f9;
`;

const Layout = props => {
  return <Container>{props.children}</Container>;
};

export default Layout;
<file_sep>/src/dsteem.client.js
import { Client } from "dsteem";

const client = new Client("https://api.steemit.com");

export default client;
<file_sep>/src/scenes/CategoryScene/CategoryScene.js
import React from "react";
import StoryList from "components/StoryList/StoryList";
import { rejectByTag } from "services/helpers/filter";
import { graphql } from "react-apollo";
import gql from "graphql-tag";
import { Loader, Grid } from "semantic-ui-react";

import Meta from "components/Meta";
import HowToBox from "components/Layout/HowToBox";
import FeaturedBox from "scenes/Index/components/FeaturedBox";
import AboutBox from "components/Layout/AboutBox";

const CategoryScene = props => {
  let { data: { loading, getDiscussions: stories }, match } = props;
  if (loading) return <Loader active />;
  // Hide `nsfw` stories.
  stories = rejectByTag(stories, "nsfw");
  return (
    <Grid stackable>
      <Meta
        data={{
          title:
            match.params.category[0].toUpperCase() +
            match.params.category.slice(1)
        }}
      />
      <Grid.Column width={10}>
        {<StoryList stories={stories || []} />}
      </Grid.Column>
      <Grid.Column width={6}>
        <HowToBox />
        <br />
        <FeaturedBox />
        <br />
        <AboutBox />
      </Grid.Column>
    </Grid>
  );
};

const QUERY = gql`
  query discussions($category: String = "news") {
    getDiscussions(by: "hot", query: { tag: $category }) {
      id
      title
      body
      author
      author_reputation
      net_votes
      category
      permlink
      json_metadata
      created
    }
  }
`;

export default graphql(QUERY, {
  options: ({ match }) => ({
    variables: { category: match.params.category },
    pollInterval: 1000 * 60 * 5 // 5 min
  })
})(CategoryScene);
<file_sep>/src/scenes/Index/index.js
export {default} from './IndexScene'
<file_sep>/src/components/Profile/components/ProfileDetails.js
import React from "react";
import _ from "lodash";
import { userData } from "services/helpers/format";
import { Segment, List } from "semantic-ui-react";
// import ExternalLink from "components/Elements/ExternalLink";

const renderVotingPower = vp => {
  return vp / 100;
};

const ProfileDetails = ({ user }) => {
  return (
    <Segment>
      <List horizontal>
        <List.Item>{_.get(userData(user), "name")}</List.Item>
        <List.Item>{_.get(userData(user), "location")}</List.Item>
        <List.Item>{_.get(userData(user), "about")}</List.Item>
        <List.Item>
          {/*<ExternalLink url={_.get(userData(user), "website")} value="test" />*/}
          Voting Power: {renderVotingPower(user.voting_power)}
        </List.Item>
      </List>
    </Segment>
  );
};

export default ProfileDetails;
<file_sep>/src/scenes/Profile/ProfileScene.js
import React from "react";
import { isNil } from "ramda";
import { userImage } from "services/helpers/format";
import { graphql } from "react-apollo";
import gql from "graphql-tag";
import steem from "steem";

import Loader from "../../components/Loader";
import ProfileDetails from "components/Profile/components/ProfileDetails";
import StoryList from "components/StoryList/StoryList";
import { Image, Label } from "semantic-ui-react";
// import ExternalLink from "components/Elements/ExternalLink";

class ProfileScene extends React.Component {
  render() {
    const {
      data: {
        loading,
        account: user,
        getDiscussionsByAuthorBeforeDate: stories
      }
    } = this.props;
    if (loading) return <Loader />;
    if (isNil(user)) return null;
    return (
      <div>
        <h1>
          <Image src={userImage(user)} avatar />
          {user.name}{" "}
          <Label circular size="big">
            {steem.formatter.reputation(user.reputation)}
          </Label>
          {/*<FavoriteButton username={user.name} />{" "}*/}
          {/*<ExternalLink url={`https://steemit.com/@${user.name}`}>*/}
          {/*<Icon name="share" />*/}
          {/*</ExternalLink>{" "}*/}
        </h1>

        <ProfileDetails user={user} />

        <StoryList stories={stories} />
      </div>
    );
  }
}

const QUERY = gql`
  query user($username: String!) {
    account(username: $username) {
      id
      name
      json_metadata
      created
      reputation
      voting_power
    }

    getDiscussionsByAuthorBeforeDate(author: $username) {
      id
      title
      body
      author
      author_reputation
      net_votes
      category
      permlink
      json_metadata
      created
    }
  }
`;

export default graphql(QUERY, {
  options: ({ match }) => ({
    variables: { username: match.params.username },
    // Polling: 1min
    pollInterval: 60 * 1000
  })
})(ProfileScene);
<file_sep>/src/components/StoryList/components/StoryListItem/components/StoryListItemBody.js
import React from "react";
import PropTypes from "prop-types";
import ellipsis from "text-ellipsis";
import striptags from "striptags";
import Remarkable from "remarkable";

const remarkable = new Remarkable({ html: true });

function decodeEntities(body) {
  return body.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
}

const BodyShort = props => {
  let body = striptags(
    remarkable.render(striptags(decodeEntities(props.body)))
  );
  body = body.replace(/(?:https?|ftp):\/\/[\S]+/g, "");

  // If body consists of whitespace characters only skip it.
  if (!body.replace(/\s/g, "").length) {
    return null;
  }

  return (
    <div
      dangerouslySetInnerHTML={{
        __html: ellipsis(body, props.length, { ellipsis: "…" })
      }}
    />
  );
};

BodyShort.propTypes = {
  body: PropTypes.string,
  length: PropTypes.number
};

BodyShort.defaultProps = {
  body: "",
  length: 200
};

export default BodyShort;
<file_sep>/src/components/User/ReputationLabel.js
import React from "react";
import steem from "steem";
import { Label as UnstyledLabel } from "unstyled";

const Label = UnstyledLabel.extend`
  background-color: #e8e8e8;
  font-size: 0.85rem;
  min-width: 2em;
  min-height: 2em;
  padding: 0.5em !important;
  line-height: 1em;
  text-align: center;
  border-radius: 500rem;
`;

const ReputationLabel = ({ reputation }) => {
  return (
    <Label title="Reputation">{steem.formatter.reputation(reputation)}</Label>
  );
};

export default ReputationLabel;
<file_sep>/src/scenes/Mentions/MentionsScene/MentionsScene.js
import React from "react";
import { graphql } from "react-apollo";
import gql from "graphql-tag";
import Loader from "components/Loader";
import MentionItem from "./components/MentionItem";
import MentionPagination from "./components/MentionPagination";

const MentionsScene = props => {
  const { data: { loading, mentions }, match } = props;
  if (loading) return <Loader />;
  return (
    <div>
      <h3>Total Mentions: {mentions.hits}</h3>
      <MentionPagination mentions={mentions} username={match.params.username} />
      {mentions.results.map(mention => (
        <MentionItem
          key={mention.permlink + mention.created}
          mention={mention}
        />
      ))}
    </div>
  );
};

const Query = gql`
  query getMentions($username: String!, $page: Int = 1) {
    mentions(username: $username, page: $page) {
      hits
      results {
        author
        created
        permlink
        summary
        title
        type
      }
    }
  }
`;

export default graphql(Query, {
  options: ({ match }) => ({
    variables: {
      username: match.params.username,
      page: match.params.page
    }
  })
})(MentionsScene);
<file_sep>/src/components/StoryList/StoryList.js
import React from "react";
import { map } from "ramda";
import { Item } from "semantic-ui-react";

import StoryListItem from "./components/StoryListItem";

const StoryList = props => {
  const { stories } = props;

  return (
    <Item.Group relaxed divided>
      {map(story => <StoryListItem key={story.id} story={story} />)(stories)}
    </Item.Group>
  );
};

export default StoryList;
<file_sep>/src/scenes/Static/AboutScene.js
import React from "react";
import Remarkable from "remarkable";
import styled from "styled-components";

const md = new Remarkable({
  html: true, // remarkable renders first then sanitize runs...
  breaks: false,
  linkify: false, // linkify is done locally
  typographer: false, // https://github.com/jonschlinkert/remarkable/issues/142#issuecomment-221546793
  quotes: "“”‘’"
});

const markdown = `
# About Insteem
Insteem will become a decentralised, rewarding news platform for
independent journalism and create an international network of
journalists, away from MSM.


Our goal is to spread high quality, uncensored, undeletable news from all over the world,
 by independent journalists and writers.
 
 ## How will it work?
* Journalists/Writers post their stories via Insteem
* Insteem will check new stories for plagiarism and show that next to a story (it won’t prevent publishing though, unless it's considered spam)
* Insteem will automatically vote on stories depending on an internal rating system (score) of journalists (multiple factors like e.g. reputation, length of story, unique content, spam level etc.)
* The voting will happen (near) instant, to bring news from quality writers to the top immediately
* This will be combined with manual upvoting through our Editors
* There will be different kind of templates for articles to post, like daily (short) news,
extensive stories or opinionated articles    
 
`;

const Text = styled.div`
  font-size: 16px;
  color: rgba(0, 0, 0, 0.7);
`;

const AboutScene = props => {
  return (
    <Text>
      <div dangerouslySetInnerHTML={{ __html: md.render(markdown) }} />
    </Text>
  );
};

export default AboutScene;
<file_sep>/src/components/Menu/CategoryMenu.js
import React from "react";
import { Link } from "react-router-dom";
import { Container, Menu, Dropdown } from "semantic-ui-react";

const CategoryMenu = () => {
  return (
    <Menu widths={11} stackable style={styles.menu}>
      <Container>
        <Menu.Item as={Link} to={"/categories/world"}>
          World
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/politics"}>
          Politics
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/business"}>
          Business
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/technology"}>
          Tech
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/science"}>
          Science
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/health"}>
          Health
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/sports"}>
          Sports
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/art"}>
          Arts
        </Menu.Item>

        <Menu.Item as={Link} to={"/categories/travel"}>
          Travel
        </Menu.Item>
        <Menu.Item as={Link} to={"/categories/cryptocurrency"}>
          Crypto
        </Menu.Item>
        <Dropdown item text="more">
          <Dropdown.Menu>
            <Dropdown.Item as={Link} to={"/categories/style"}>
              Style
            </Dropdown.Item>
            <Dropdown.Item as={Link} to={"/categories/food"}>
              Food
            </Dropdown.Item>
            <Dropdown.Item as={Link} to={"/categories/opinion"}>
              Opinion
            </Dropdown.Item>
            <Dropdown.Item as={Link} to={"/categories/photography"}>
              Photography
            </Dropdown.Item>
            <Dropdown.Item as={Link} to={"/categories/philosopy"}>
              Philosophy
            </Dropdown.Item>
          </Dropdown.Menu>
        </Dropdown>
      </Container>
    </Menu>
  );
};

export default CategoryMenu;

const styles = {
  menu: {
    marginBottom: 20
  }
};
<file_sep>/src/services/helpers/format.js
import steem from "steem";
import _ from "lodash";
import { isNil } from "ramda";

/**
 * Take the author_score from steem and convert it in a human readable score.
 * @param reputation
 * @return {number}
 */
export const calculateReputationScore = reputation => {
  return steem.formatter.reputation(reputation);
};

/**
 *  Parse the `json_metadata` property from steem.
 * @param json - string to parse into JSON format.
 */
export const parseMetadata = json => {
  // Check if JSON string is not empty otherwise `parse` will fail.
  if (json.length > 0) {
    return JSON.parse(json);
  }
  return "";
};

// Get profile picture for user.
export const userImage = user => {
  if (isNil(user)) return;
  const image = _.get(JSON.parse(user.json_metadata), "profile.profile_image");
  if (_.isEmpty(image)) return null;
  return image;
};

// Get JSON formatted profile data for user.
export const userData = user => {
  return _.get(parseMetadata(user.json_metadata), "profile");
};
<file_sep>/src/scenes/Journalists/JournalistsScene/index.js
export { default } from "./JournalistsScene";
<file_sep>/README.md
# Insteem - The News on Steem

Insteem rewards independant journalists on steem.

## Insteem Web/App

This is the web frontend for the Insteem project.
For the mobile app see: [insteem-app](https://github.com/Insteem/insteem-app)

## Mission

Insteem will become the platform and community for independent journalismn on steem.

## Ideas

Ideas:

* Dedicated Web and Mobile Apps (Insteem and Insteem Mobile, something similar to utopian-ui)
* Curation Trails
* BidBot for news stories
* Resteem service for high quality news
* Feature posts of great veteran or upcoming journalists
* SP Delegation for Voting
* Writing/Audio/Video competitions
* Post promotion
* Coaching for upcoming or promising journalists(edited)
<file_sep>/src/scenes/StoryScene/index.js
export * from "./StoryScene";
export { default } from "./StoryScene";
<file_sep>/src/scenes/Journalists/JournalistsScene/JournalistsScene.js
import React from "react";
import { Grid, Header, Label, Card, Button, Icon } from "semantic-ui-react";
import { map } from "ramda";
import { Link } from "react-router-dom";

const journalists = [
  {
    name: "adamcarter",
    desc: "Writes about technical stuff at Disobedient Media.",
    tags: ["technology"],
    location: "USA"
  },
  {
    name: "ameliabartlett",
    desc:
      "Writer and Photographer from East Tennessee" +
      " covering all aspects of life and supports women via" +
      "  @ladiesofsteemit.",
    tags: ["photography", "womanpower", " ladiesofsteemit"],
    location: "Knoxville, TN, US"
  },
  {
    name: "cyberwarrior",
    desc:
      "Blogger and crypto expert. Covers a wide" +
      " spectrum from politics, technology up to food.",
    tags: ["politics", "technology", "life"]
  },
  {
    name: "didic",
    desc:
      "Recovering journalist from Israel, currently writing" +
      " about" +
      " geeky books, nerdy entertainment, and social justice.",
    tags: ["books", "tv", "film", "culture"],
    location: "Israel"
  },
  {
    name: "eleanorgoldfield",
    desc:
      "Socio-political engaged activist," +
      " journalist and poet from Washington. Hosts the daily show `Act out.`",
    tags: ["politics", "science"],
    location: "Washington, D.C., US"
  },
  {
    name: "elizbethleavos",
    desc:
      "Independent journalists" +
      " from Disobedient Media. Covers" +
      " politics and freedom of speech.",
    tags: ["politics", "wikileaks"],
    location: "USA"
  },
  {
    name: "fedescholari",
    desc:
      "Italian (?) writer covering business, crypto" +
      " and money related topics ",
    tags: ["cryptocurrency", "money", "china"]
  },
  {
    name: "greenmask9",
    desc:
      "	New author from the Czech Republic. Covers health," +
      " psychology, sports and politics.",
    tags: ["health", "politics", "sports", " psychology"],
    location: "Czech" + " Republic"
  },
  {
    name: "johnvibes",
    desc:
      "Author and researcher who writes for numerous" +
      " alternative" +
      " media websites, including The Free Thought Project and The Mind" +
      " Unleashed, covering topics relating to government and police" +
      " corruption, the drug war and consciousness. In addition to his" +
      " activist work, he also hosts various live events, including the Free" +
      " Your Mind Conference.",
    tags: ["politics", "society", "corruption"],
    location: "USA"
  },
  {
    name: "joshsigurdson",
    desc:
      'Journalist at World Alternative Media. Does video" +\n' +
      '    " interviews on DTube, talks about money, crypto and business.',
    tags: ["business", "politics", "cryptocurrency"],
    location: "Winnipeg, Canada"
  },
  {
    name: "leecamp",
    desc:
      'Journalist from Washington. Host of "Redacted' +
      ' Tonight", a comedy show on Youtube (so far).',
    tags: ["comedy", "politics", "life"],
    location: "Washington,D.C., US"
  },
  {
    name: "lilyraabe",
    desc:
      "Working artist, business consultant, and entrepeneur" +
      " currently developing business strategy and content for 6 nonprofit" +
      " clients, and 5 independent artists. Her goal is to help artists thrive" +
      " on and off the Blockchain. Arts Editor of Insteem",
    tags: ["arts", "society", "politics"],
    location: "Seattle, US"
  },
  {
    name: "news2share",
    desc:
      "An online media team from Washington. Range from" +
      "  local, national to international politics",
    tags: ["politics", "technology"],
    location: "Washington,D.C., US"
  },
  {
    name: "ocupation",
    desc:
      "Writes about everything that comes to his mind," +
      " gaming, conspiracy, photography, health and so on.",
    tags: ["politics", "geoengineering", "conspiracy"]
  },
  {
    name: "patricklancaster",
    desc:
      "Video and photo journalists. Reports exclusively about the war in" +
      " Ukrain",
    tags: ["politics", "war", "ukraine"],
    location: "Ukraine"
  },
  {
    name: "travelwithus",
    desc:
      "Young family traveling the world and reporting their adventures" +
      " while conquering new countries.|",
    tags: ["travel", "life", "photography"],
    location: "Everywhere"
  },
  {
    name: "yahialababidi",
    desc:
      "Author of 7 books & published in international" +
      " magazines and newspapers. As an Egyptian-American, he touches on" +
      " immigration, Islamophobia & other cultural issues, in addition to covering" +
      " literature & philosophical/spiritual matters.",
    tags: ["society", "politics", "culture", "philosophy"]
  }
];

const blockedJournalists = [
  {
    name: "sarahlouise",
    desc:
      "Posts daily news about anything. The level of english in the" +
      " articles doesn't fit with the one in the comments and e.g. the" +
      " `introduceyourself` post. No plagiarism detected so far.",
    tags: []
  }
];

const renderTags = map(label => (
  <Label key={label} color="green">
    {label}
  </Label>
));

const renderJournalists = map(user => (
  <Card key={user.name}>
    <Card.Content>
      <Card.Header as={Link} to={`/@${user.name}`}>
        {user.name}
      </Card.Header>
      <Card.Meta>{user.location}</Card.Meta>
      <Card.Description>{user.desc}</Card.Description>
    </Card.Content>
    <Card.Content extra>
      <Button
        icon
        floated="right"
        size="tiny"
        color="blue"
        as="a"
        href={`https://steemit.com/@${user.name}`}
        target="_blank"
      >
        <Icon name="external share" />
      </Button>
      <Label.Group size="small">{renderTags(user.tags)}</Label.Group>
    </Card.Content>
  </Card>
));

const JournalistsScene = () => {
  return (
    <Grid centered columns={1}>
      <Grid.Column>
        <Header>Journalists and Writers (Approved)</Header>

        <Card.Group itemsPerRow={3}>
          {renderJournalists(journalists)}
        </Card.Group>

        <Header>Unapproved Journalists </Header>
        <Card.Group>{renderJournalists(blockedJournalists)}</Card.Group>
      </Grid.Column>
    </Grid>
  );
};

export default JournalistsScene;
<file_sep>/src/services/state/reducers.js
/*
 This file controls the shape of the default state.

 It imports all the various reducers and combines them using the
 `combineReducers()` API from redux.
 */

import { combineReducers } from "redux";

import auth from "./auth/reducer";
// import post from "./posts/reducer";
// import tags from "./tags/reducer";
// import rehydrated from "./rehydrated/reducer";

const reducer = combineReducers({
  auth
  // post,
  // tags,
  // rehydrated
});

export default reducer;
<file_sep>/src/scenes/StoryScene/StoryScene.js
import React from "react";
import client from "dsteem.client";
import { lifecycle } from "recompose";
import { head, propOr } from "ramda";
import { Grid, Header, Image } from "semantic-ui-react";
import Markdown from "react-markdown";
import { parseMetadata } from "services/helpers/format";
import styled from "styled-components";

import Meta from "components/Meta";
import StoryBody from "./components/StoryBody";
import StoryMeta from "./components/StoryMeta";
import StoryMetaBox from "./components/StoryMetaBox";

const Container = styled.div`
  overflow: hidden;
  margin-bottom: 10rem;
`;

const CoverImage = styled.div`
  margin-bottom: 1rem;
`;

const Content = styled.div`
  font-size: 1.3rem;
  font-weight: 300;
  color: hsla(0, 0%, 0%, 0.6);
`;

const StoryScene = props => {
  const { story } = props;
  if (!story) return <div />;

  // Get images from story object
  const images = propOr([], "image")(parseMetadata(story.json_metadata));
  const image = head(images);
  return (
    <Container>
      <Meta data={story} />
      <Grid>
        <Grid.Column width={11}>
          <Header>{story.title}</Header>

          <StoryMeta story={story} />
          {/*<CoverImage>{image ? <Image src={image} /> : <div />}</CoverImage>*/}

          <Content>
            <StoryBody body={story.body} />
            {/*<Markdown*/}
            {/*source={story.body}*/}
            {/*skipHtml={false}*/}
            {/*escapeHtml={false}*/}
            {/*disallowedTypes={["image"]}*/}
            {/*/>*/}
          </Content>
        </Grid.Column>
        <Grid.Column width={5}>
          <StoryMetaBox story={story} />
        </Grid.Column>
      </Grid>
    </Container>
  );
};

export default lifecycle({
  componentWillMount() {
    const { author, permlink } = this.props.match.params;
    console.log(author, permlink);
    client.database
      .getDiscussions("blog", {
        tag: author,
        limit: 1,
        start_author: author,
        start_permlink: permlink
      })
      .then(stories => {
        this.setState({ story: head(stories) });
      });
  }
})(StoryScene);
<file_sep>/src/components/User/UserLink.js
import React from "react";
import { Link as ReactLink } from "react-router-dom";
import styled from "styled-components";

const Link = styled(ReactLink)`
  display: block;
  margin-bottom: 0.5rem;
`;

const UserImage = styled.div`
  display: inline-block;
  height: 2rem;
  width: 2rem;
  border-radius: 50%;
  background-image: url(${props => props.image});
  background-size: cover;
  margin-right: 0.75rem;
  vertical-align: middle;
`;

const UserLink = ({ user }) => {
  return (
    <Link to={`/@${user.name}`}>
      <UserImage image={user.profile.profile_image} />
      {user.name}
      {/*<ReputationLabel reputation={user.reputation} />*/}
    </Link>
  );
};

export default UserLink;
<file_sep>/src/scenes/Static/RulesScene.js
import React from "react";
import Remarkable from "remarkable";
import styled from "styled-components";

const md = new Remarkable({
  html: true, // remarkable renders first then sanitize runs...
  breaks: false,
  linkify: false, // linkify is done locally
  typographer: false, // https://github.com/jonschlinkert/remarkable/issues/142#issuecomment-221546793
  quotes: "“”‘’"
});

const markdown = `
# Rules 
* To be considered for an upvote/resteem of @insteem and members, 
add the tag 'insteem' to your story.
* Optional: Add 'Powered by [Insteem](https://www.insteem.com)', 
the News on Steem at the end of your story, 
it will improve your chances for upvotes/resteem
* If your story doesn't fit our standards, we'll ask you to remove it
* Only articles with full source of the information will be considered
* Only articles which are your own IP will be considered

`;

const Text = styled.div`
  font-size: 16px;
  color: rgba(0, 0, 0, 0.7);
`;

const RulesScene = props => {
  return (
    <Text>
      <div dangerouslySetInnerHTML={{ __html: md.render(markdown) }} />
    </Text>
  );
};

export default RulesScene;
<file_sep>/src/components/Layout/AboutBox.js
import React from "react";
import { Segment, Header } from "semantic-ui-react";
import { Link } from "react-router-dom";

const AboutBox = () => {
  return (
    <div>
      <Header attached="top">
        <Link to="/about">About </Link>
      </Header>
      <Segment attached="bottom">
        Insteem will become a decentralised, rewarding news platform for
        independent journalism and create an international network of
        journalists, away from MSM.
        <br />
        <Link to="/rules">Rules</Link>
      </Segment>
    </div>
  );
};

export default AboutBox;
<file_sep>/src/components/Menu/MainMenu.js
import React from "react";
import { connect } from "react-redux";
import { signout } from "services/state/auth/actions";
import { Link } from "react-router-dom";
import { Container, Menu, Image } from "semantic-ui-react";
import styled from "styled-components";

import AccountMenu from "components/Menu/AccountMenu";
import SigninModal from "scenes/Auth/SigninModal";

const Title = styled.span`
  padding-left: 100px;
  color: darkgray;
  text-align: right;
`;

const MainMenu = props => {
  const { isSignedIn } = props;
  return (
    <Menu borderless stackable fixed="top" style={styles.menu} size="large">
      <Container>
        <Menu.Item as={Link} to={"/"}>
          <Image
            inline
            style={{ height: 30, width: 30 }}
            shape="rounded"
            src="/insteem.png"
          />
          &nbsp;{" "}
          <span style={styles.brand}>
            INSTEEM <span style={styles.brand.beta}>beta</span>
          </span>
          <Title>Decentralized News by Independent Journalists</Title>
        </Menu.Item>
        {/*<Menu.Item as={Link} to="/comments">*/}
        {/*Comments*/}
        {/*</Menu.Item>*/}
        {/*<Menu.Item as={Link} to="/messages">*/}
        {/*Messages*/}
        {/*</Menu.Item>*/}
        {/*<Menu.Item as={Link} to="/mentions">*/}
        {/*Mentions*/}
        {/*</Menu.Item>*/}
        <Menu.Menu position="right" />
        <Menu.Menu position="right">
          {isSignedIn ? (
            <AccountMenu />
          ) : (
            <Menu.Item link>
              <SigninModal />
            </Menu.Item>
          )}
        </Menu.Menu>
      </Container>
    </Menu>
  );
};

const mapStateToProps = state => {
  return {
    isSignedIn: state.auth.isSignedIn
  };
};

const mapDispatchToProps = dispatch => {
  return {
    onSignout: () => {
      dispatch(signout());
    }
  };
};

// export default connect(null, mapDispatchToProps)(MainMenu);
export default connect(mapStateToProps, mapDispatchToProps)(MainMenu);

const styles = {
  menu: {
    marginBottom: 20
  },
  brand: {
    fontWeight: "bold",
    fontSize: 26,
    beta: { fontSize: 11 }
  }
};
<file_sep>/src/scenes/Auth/SigninScene/index.js
export { default } from "./Signin.scene";
<file_sep>/src/services/helpers/image.js
const IMG_PROXY = "https://steemitimages.com/0x0/";
const IMG_PROXY_PREVIEW = "https://steemitimages.com/600x800/";

export const getProxyImageURL = (url, type) => {
  if (type === "preview") {
    return `${IMG_PROXY_PREVIEW}${url}`;
  }
  return `${IMG_PROXY}${url}`;
};

export default null;
<file_sep>/src/components/Meta.js
import React from "react";
import { Helmet } from "react-helmet";
import logo from "components/Layout/insteem.png";

const Meta = props => {
  const { data = {} } = props;

  const { title } = data || "Insteem";
  const {
    body: description = "Decentralized News by Independent Journalists"
  } = data;
  const path =
    "/" + data.category + "/@" + data.author + "/" + data.permlink || "";

  return (
    <Helmet titleTemplate="%s | Insteem" defaultTitle="Insteem">
      <title>{title}</title>
      <meta name="description" content={description} />
      <meta name="keywords" content={title} />
      <meta name="author" content={data.author} />
      <meta name="og:title" content={title} />
      <meta name="og:description" content={data.description} />
      <meta name="og:image" content={logo} />
      {data.image ? <meta name="og:image" content={data.image.url} /> : null}
      <meta name="twitter:card" content="summary" />
      <meta name="twitter:site" content="@GatsbyCentral" />
      <meta name="twitter:image" content={logo} />
      <link rel="canonical" href={`https://www.steemit.com${path}`} />
      <link rel="image_src" href={logo} />
    </Helmet>
  );
};

export default Meta;
<file_sep>/src/components/Layout/HowToBox.js
import React from "react";
import styled from "styled-components";
import { Header, Segment, Label } from "semantic-ui-react";

const LI = styled.li`
  margin: 0.4rem;
`;

const HowToBox = props => {
  return (
    <div>
      <Header attached="top">How to</Header>
      <Segment attached="bottom">
        <ul>
          <LI>
            Write your story on the steem frontend of your choice. Posting will
            be coming soon.
          </LI>
          <LI>
            Use{" "}
            <Label color="blue" basic>
              news
            </Label>{" "}
            as the first tag
          </LI>
          <LI>
            Use{" "}
            <Label color="blue" basic>
              insteem
            </Label>{" "}
            to qualify for an upvote of @insteem.{" "}
          </LI>
          <LI>Use at least one of the tags from the main menu</LI>
        </ul>
      </Segment>
    </div>
  );
};

export default HowToBox;
