styling and file support overhaul #884

Merged
jessopb merged 1 commit from newstyling into master 2019-01-28 21:13:55 +01:00
35 changed files with 1794 additions and 1201 deletions

View file

@ -0,0 +1,7 @@
.asset-blocked__image {
width: 100%;
}
.asset-blocked__text {
width: 100%;
}

View file

@ -1,29 +1,37 @@
.asset-main {
height: 75vh;
display: flex;
flex-direction: column;
align-items: center;
}
.asset-document {
$asset-info-width: 1000px;
max-width: $asset-info-width;
width: 100%;
padding: $thin-padding;
height: fit-content;
@media (max-width: $break-point-tablet) {
margin: $primary-padding $secondary-padding;
}
@media (max-width: $break-point-mobile) {
margin: $primary-padding 0;
}
}
.asset-display {
display: flex;
min-height: 50vh
height: fit-content;
width: fit-content;
}
.asset-title {
padding-bottom: $thin-padding;
text-align: center;
@media (min-width: $break-point-mobile) {
padding-top: $secondary-padding;
}
}
.asset-image, .asset-video {
max-height: 100%;
max-width: 100%;
margin-left: auto;
margin-right: auto;
max-height: 95vh;
max-width: 95vw;
object-fit: contain;
object-position: center;
}
@ -110,7 +118,7 @@
$asset-info-width: 1000px;
max-width: $asset-info-width;
margin: $primary-padding;
max-width: 100%;
width: 100%;
@media (max-width: $break-point-tablet) {
margin: $primary-padding $secondary-padding;
@ -122,7 +130,7 @@
}
.asset-footer {
border-top: 1px solid $grey-border;
border-top: $subtle-border;
padding-top: $primary-padding;
margin-top: $primary-padding;
color: $grey;

View file

@ -1,28 +1,48 @@
.asset-preview {
position: relative;
background: $card-color;
padding: $thin-padding;
color: $text-color;
width: 240px;
border: $subtle-border;
height: 280px;
&:hover {
border: 1px solid $highlight-border-color;
color: #000000;
}
}
.asset-preview__label {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 7.3em;
}
.asset-preview__label-text {
height: 4.5em;
overflow: hidden;
text-overflow: ellipsis;
box-sizing: border-box;
}
.asset-preview__blocked {
box-sizing: border-box;
background: black;
color: white;
height: 80%;
padding: 5px;
//remove margin-bottom after mystery 5px on wrapper is gone.
margin-bottom: 5px;
height: 64%;
padding: $thin-padding;
margin-bottom: $thin-padding;
}
.asset-preview__image {
width : 100%;
width : 240px;
height : 180px;
overflow: hidden;
object-fit: cover;
padding: 0;
margin : 0;
}
.asset-preview__video {
cursor: pointer;
background-color: #ffffff;
width: 100%;
position: relative;
box-sizing: border-box;
}
h3.asset-preview__title {
@ -30,34 +50,6 @@ h3.asset-preview__title {
text-overflow: ellipsis;
word-wrap: break-word;
overflow: hidden;
line-height: 1em;
max-height: 2em;
}
.asset-preview__play-wrapper {
border: 0px;
padding: 0px;
margin: auto;
position: relative;
}
.asset-preview__play-overlay {
padding: 0;
border: 0;
position: absolute;
opacity: 0.80;
height: 25%;
top: 37.5%;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
margin: 0 auto;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E %3Cg stroke='black' stroke-width='2' fill='black' fill-rule='evenodd' stroke-linejoin='round'%3E %3Ccircle cx='30' cy='30' r='28'/%3E%3C/g%3E %3Cg stroke='white' stroke-width='1' fill='white' fill-rule='evenodd' stroke-linejoin='round'%3E %3Cpolygon points='25 19 42 30 25 41'/%3E %3C/g%3E %3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
}
.asset-preview__play-wrapper:hover .asset-preview__play-overlay {
opacity: 0.2;
max-height: 4em;
font-size: $text-large;
}

View file

@ -1,23 +1,24 @@
.channel-claims-display {
width: 100%;
display: grid;
grid-gap: 16px;
}
@media (min-width: 1040px) {
.channel-claims-display {
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: $thin-padding;
align-content: space-around;
@media (min-width: $break-point-x-large) {
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}
@media (min-width: $break-point-large) and (max-width: $break-point-x-large){
grid-template-columns: 1fr 1fr 1fr 1fr;
}
}
@media (min-width: 768px) and (max-width: 1039px) {
.channel-claims-display {
@media (min-width: $break-point-tablet) and (max-width: $break-point-large) {
grid-template-columns: 1fr 1fr 1fr;
}
@media (min-width: $break-point-mobile) and (max-width: $break-point-tablet) {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: 767px) {
.channel-claims-display {
@media (max-width: $break-point-mobile) {
grid-template-columns: 1fr;
}
}

View file

@ -4,7 +4,7 @@
justify-content: space-between;
align-items: center;
cursor: pointer;
border: 1px solid $grey-border;
border: 1px solid $subtle-border-color;
border-radius: 6px;
.click-to-copy {
border: none;
@ -17,7 +17,7 @@
line-height: 20px;
letter-spacing: 0;
font-family: monospace;
border-right: 1px solid $grey-border;
border-right: 1px solid $subtle-border-color;
}
.icon-wrap {
width: 30px;

View file

@ -5,15 +5,18 @@
display: flex;
flex-direction: column;
position: relative;
width: 100%;
box-sizing: border-box;
padding: 0px;
}
.dropzone {
border: 2px dashed #9b9b9b;
border: 2px dashed $drop-zone-border-color;
// fill the parent flex container
flex: 1 0 auto;
// be a flex container for children
display: flex;
padding: 1em;
padding: $thin-padding;
-webkit-flex-direction: column;
flex-direction: column;
justify-content: center;
@ -22,7 +25,7 @@
}
.dropzone:hover, .dropzone--active {
border: 2px dashed #4156C5;
border: 2px dashed $drop-zone-border-hover;
cursor: pointer;
}

View file

@ -1,55 +1,55 @@
.horizontal-split {
max-width: $width-content-constrained;
width: 100%;
margin-left: auto;
margin-right: auto;
display : flex;
flex-direction : row;
justify-content: space-between;
justify-content: center;
box-sizing: border-box;
&.horizontal-split--mobile-collapse {
@media (max-width: $break-point-mobile) {
@media (max-width: $break-point-tablet) {
flex-direction: column;
.horizontal-split__column {
width: 100%;
}
.horizontal-split__column--left {
padding-top: $thin-padding;
}
.horizontal-split__column--right {
padding-left: 0;
padding-top: $secondary-padding;
padding-top: $thin-padding;
}
}
}
};
.horizontal-split__column {
width: 50%;
flex: 1 0 auto;
display : flex;
flex: 1 1 auto;
box-sizing: border-box;
width: 100%;
}
.horizontal-split__column--left {
padding-right: $primary-padding;
padding: $tertiary-padding;
@media (max-width: $break-point-mobile) {
padding-right: $thin-padding;
@media (max-width: $break-point-tablet) {
padding-left: 0px;
padding-right: 0px;
}
}
.horizontal-split__column--right {
padding-left: $primary-padding;
@media (max-width: $break-point-mobile) {
padding-left: $thin-padding;
padding: $tertiary-padding;
@media (max-width: $break-point-tablet) {
padding-left: 0px;
padding-right: 0px;
}
}
@media (max-width: $break-point-tablet) {
.horizontal-split__column {
display : flex;
flex-direction : column;
justify-content: space-between;
};

115
client/scss/_markdown.scss Normal file
View file

@ -0,0 +1,115 @@
.markdown-preview {
// Headers
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: 600;
margin-bottom: var(--spacing-vertical-medium);
padding-top: var(--spacing-vertical-medium);
}
// Paragraphs
p {
font-size: 1.15rem;
margin-bottom: var(--spacing-vertical-medium);
white-space: pre-line;
svg {
width: 1rem;
height: 1rem;
margin-left: 0.2rem;
position: relative;
top: 1px;
}
}
// Strikethrough text
del {
}
// Tables
table {
margin-bottom: 1.2rem;
padding: var(--spacing-vertical-medium);
background-color: $base-color;
tr {
td,
th,
td:first-of-type,
th:first-of-type,
td:last-of-type,
th:last-of-type {
padding: var(--spacing-vertical-medium);
}
}
}
// Image
img {
margin-bottom: var(--spacing-vertical-medium);
padding-top: var(--spacing-vertical-medium);
}
// Horizontal Rule
hr {
width: 100%;
height: 1px;
background-color: $base-color;
margin-bottom: 2rem;
position: relative;
top: 1rem;
html[data-theme='dark'] & {
background-color: rgba($base-color, 0.2);
}
}
// Code
pre {
white-space: normal;
}
code {
margin-bottom: var(--spacing-vertical-medium);
padding: var(--spacing-vertical-medium);
background-color: $subtle-border-color;
color: $text-color;
display: block;
font-family: Consolas, 'Lucida Console', 'Source Sans', monospace;
}
a {
color: $primary-color;
display: inline-block;
}
// Lists
ul,
ol {
margin-bottom: var(--spacing-vertical-medium);
> li {
list-style-position: outside;
}
}
ul {
list-style: initial;
}
li {
margin-left: var(--spacing-vertical-large);
p {
display: inline-block;
}
}
}

View file

@ -1,7 +1,10 @@
.nav-bar {
margin-top: $thin-padding;
margin-left: $primary-padding;
margin-right: $primary-padding;
box-sizing: border-box;
padding: $thin-padding $primary-padding;
background: $base-color;
flex: 0 1 auto;
width: 100%;
border-bottom: $subtle-border;
@media (max-width: $break-point-mobile) {
margin-left: 15px;

View file

@ -2,26 +2,24 @@
flex: 1 0 auto;
display: flex;
flex-direction: column;
align-items: center;
max-width: 100%;
.content {
flex: 1 0 auto;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
margin: $secondary-padding;
width: 100%;
align-items: center;
box-sizing: border-box;
background: $base-color;
@media (min-width: $break-point-tablet) {
padding: $primary-padding;
}
@media (max-width: $break-point-tablet) {
padding: $tertiary-padding;
}
}
}
@media (max-width: $break-point-tablet) {
.page-layout .content { margin: $tertiary-padding; }
}
@media (max-width: $break-point-mobile) {
max-width: calc(100% - 30px);
}
//below should take some styles from _text.scss and probably elsewhere and become "markdown" or "rich" styles
.page-layout {
p {
margin-bottom: $tertiary-padding;
}
}

View file

@ -1,4 +1,6 @@
select {
margin: 0;
display: inline-block;
background: $base-color;
border: 0;
}

View file

@ -7,7 +7,11 @@ h1, h2, h3, h4, p {
body {
color: $text-color;
font-family: 'Circular', serif;
font-size: 16px;
font-size: 14px;
}
body a {
color: $primary-color;
}
h1 {

View file

@ -1,40 +1,60 @@
$base-color: white;
$primary-color: #005da0;
//backgrounds
$base-color: white; //default white
$card-color: white; //default white
$chrome-color: lightgray; //default white (navbar)
$background-color: $base-color;
//text colors
$primary-color: #005da0; //link default light blue #005da0
$secondary-color: $primary-color;
$text-color: #333;
$success-color: green;
$failure-color: red;
$grey: #9095A5;
$help-color: $grey;
$grey-border: #DDDFE4;
$shadow-color: rgba(169, 173, 186, 0.2);
//borders and highlights
$grey: #9095A5;
$help-color: $grey;
$subtle-border-color: #DDD;
$highlight-border-color: #333;
$shadow-color: rgba(169, 173, 186, 0.2);
$subtle-border: 1px dashed $subtle-border-color;
$grey-border: $subtle-border-color; //factor this out for all customers
$drop-zone-border-color: #9b9b9b; //default #9b9b9b
$drop-zone-border-hover: #4156C5; //default #4156C5
//padding
$primary-padding: 3em;
$secondary-padding: 2em;
$tertiary-padding: 1em;
$thin-padding: 0.3em;
$full-width-thin-padding: calc(100% - 0.6em);
$input-padding: 0.3em;
$width-content-constrained: 1000px;
$background-color: $base-color;
$text-color: #333;
$button-border-width: 1px;
$button-border-strength: solid;
$button-full-width: calc(100% - 2px);
$input-padding: 0.3em;
$input-full-width: calc(100% - 0.6em);
//text sizes
$base-font-size: 14px;
$text-xx-large: 2.5em;
$text-x-large: 2.0em;
$text-large: 1.5em;
$text-medium: 1.0em;
$text-medium: 1.2em;
$text-small: 0.9em;
$text-x-small: 0.8em;
//@media sizes
$break-point-xx-large: 1400px;
$break-point-x-large: 1290px;
$break-point-large: 1000px;
$break-point-large: 1024px;
$break-point-tablet: 800px;
$break-point-mobile: 500px;
$break-point-mobile: 500px;
$break-point-phone: 300px;
$break-point-phone: 300px;

View file

@ -5,6 +5,7 @@
@import '~scss/_body';
@import '~scss/_react-app';
@import '~scss/_text';
@import '~scss/_markdown';
@import '~scss/_link';
@import '~scss/_input';
@ -15,6 +16,7 @@
@import '~scss/_asset-display';
@import '~scss/_asset-preview';
@import '~scss/_asset-blocked';
@import '~scss/_button';
@import '~scss/_button-primary';
@import '~scss/_button-secondary';

View file

@ -1,12 +1,20 @@
import React from 'react';
import { Link } from 'react-router-dom';
import createCanonicalLink from '../../../../utils/createCanonicalLink';
import createCanonicalLink from '@globalutils/createCanonicalLink';
import * as Icon from 'react-feather';
const AssetPreview = ({ defaultThumbnail, claimData }) => {
const {name, fileExt, contentType, thumbnail, title, blocked} = claimData;
const showUrl = createCanonicalLink({asset: {...claimData}});
const embedUrl = `${showUrl}.${fileExt}`;
/*
we'll be assigning media icon based on supported type / mime types
*/
const media = contentType.split('/')[0];
/*
make sure thumb has the right url
*/
const thumb = media === 'image' ? embedUrl : thumbnail;
/*
This blocked section shouldn't be necessary after pagination is reworked,
though it might be useful for channel_mine situations.
@ -16,45 +24,39 @@ const AssetPreview = ({ defaultThumbnail, claimData }) => {
return (
<div className='asset-preview'>
<div className='asset-preview__blocked'>
<h3>Error 451</h3>
<p>Error 451</p>
<p>This content is blocked for legal reasons.</p>
</div>
<h3 className='asset-preview__title'>Blocked Content</h3>
<div className={'asset-preview__label'}>
<div className={'asset-preview__label-text'}>
<p className='asset-preview__title text--medium'>Blocked Content</p>
</div>
</div>
</div>
);
} else {
switch (contentType) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
case 'image/gif':
return (
<Link to={showUrl} className='asset-preview'>
<img
className={'asset-preview__image'}
src={embedUrl}
alt={name}
/>
<h3 className='asset-preview__title'>{title}</h3>
</Link>
);
case 'video/mp4':
return (
<Link to={showUrl} className='asset-preview'>
<div className='asset-preview__play-wrapper'>
<img
className={'asset-preview__video'}
src={thumbnail || defaultThumbnail}
alt={name}
/>
<div className='asset-preview__play-overlay' />
return (
<Link to={showUrl} className='asset-preview'>
<img
className={'asset-preview__image'}
src={thumb || defaultThumbnail}
alt={name}
/>
<div className={'asset-preview__label'}>
<div className={'asset-preview__label-text'}>
<p className='asset-preview__title text--medium'>{title}</p>
</div>
<div className={'asset-preview__label-info'}>
<div className={'text--medium'}>
{ media === 'image' && <Icon.Image />}
{ media === 'text' && <Icon.FileText />}
{ media === 'video' && contentType === 'video/mp4' && <Icon.Video />}
{ media !== 'image' && media !== 'text' && contentType !== 'video/mp4' && <Icon.File />}
</div>
<h3 className='asset-preview__title'>{title}</h3>
</Link>
);
default:
return null;
}
</div>
</div>
</Link>
);
}
};

View file

@ -1,5 +1,5 @@
import React from 'react';
// TODO: factor out longId OR implement tooltip display
const ChannelInfoDisplay = ({name, longId, shortId}) => {
return (
<div>

View file

@ -0,0 +1,52 @@
import React from 'react';
import ReactMarkdown from 'react-markdown';
class FileViewer extends React.Component {
constructor (props) {
super(props);
/*
Prevent memory leak by closing fetch before unmount
*/
this.abortController = new AbortController();
this.abortSignal = this.abortController.signal;
this.state = {
fileLoaded: false,
fileText : '',
};
}
componentDidMount () {
const {sourceUrl} = this.props;
const signal = this.abortSignal;
fetch(sourceUrl, { signal })
.then(response => response.text())
.then((text) => {
this.setState({fileText: text});
this.setState({fileLoaded: true});
return true;
})
.catch(e => { console.log('fetch aborted on unmount ', e) });
}
jessopb commented 2019-01-26 20:50:16 +01:00 (Migrated from github.com)
Review

The signal /abortController piece prevents memory leak from not resolving a fetch promise on unmount. Currently the way I catch the error throws a console log in the browser. Maybe there's a better way.

The signal /abortController piece prevents memory leak from not resolving a fetch promise on unmount. Currently the way I catch the error throws a console log in the browser. Maybe there's a better way.
componentWillUnmount () {
this.abortController.abort();
}
render () {
return (
<div className={'markdown'}>
{
this.state.fileLoaded &&
<ReactMarkdown source={this.state.fileText}/>
}
{
!this.state.fileLoaded &&
<p>Loading your file...</p>
}
</div>
);
}
}
export default FileViewer;

View file

@ -5,9 +5,9 @@ import HorizontalSplit from '@components/HorizontalSplit';
class BlockedLeft extends React.PureComponent {
render () {
return (
<React.Fragment>
<img className='asset-image' src={'https://upload.wikimedia.org/wikipedia/commons/archive/a/af/20120315000030%21OR_451.svg'} alt={'451 image'} />
</React.Fragment>
<div>
<img className={'asset-blocked__image'} src={'/assets/img/451sign.svg'} alt={'451 image'} />
</div>
);
}
}
@ -15,10 +15,10 @@ class BlockedLeft extends React.PureComponent {
class BlockedRight extends React.PureComponent {
render () {
return (
<React.Fragment>
<p>In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content.</p>
<div className={'asset-blocked__text'} >
<p>In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications.</p>
<p><a href={'https://lbry.io/faq/dmca'} >Click here</a> for more information.</p>
</React.Fragment>
</div>
);
}
}

View file

@ -3,6 +3,7 @@ import Row from '@components/Row';
import ProgressBar from '@components/ProgressBar';
import { LOCAL_CHECK, UNAVAILABLE, ERROR, AVAILABLE } from '../../constants/asset_display_states';
import createCanonicalLink from '@globalutils/createCanonicalLink';
import FileViewer from '@components/FileViewer';
class AvailableContent extends React.Component {
render () {
@ -12,6 +13,7 @@ class AvailableContent extends React.Component {
case 'image/jpg':
case 'image/png':
case 'image/gif':
case 'image/svg+xml':
return (
<img
className='asset-image'
@ -31,9 +33,18 @@ class AvailableContent extends React.Component {
<p>Your browser does not support the <code>video</code> element.</p>
</video>
);
case 'text/markdown':
return (
<div className={'asset-document'}><FileViewer sourceUrl={sourceUrl}/></div>
);
default:
return (
<p>Unsupported content type</p>
<img
className='asset-image'
src={thumbnail}
alt={name}
/>
);
}
}

View file

@ -5,11 +5,11 @@ import RowLabeled from '@components/RowLabeled';
import SpaceBetween from '@components/SpaceBetween';
import AssetShareButtons from '@components/AssetShareButtons';
import ClickToCopy from '@components/ClickToCopy';
import HorizontalSplit from '@components/HorizontalSplit';
import siteConfig from '@config/siteConfig.json';
import createCanonicalLink from '@globalutils/createCanonicalLink';
import AssetInfoFooter from '../../components/AssetInfoFooter/index';
import { createPermanentURI } from '@clientutils/createPermanentURI';
import ReactMarkdown from 'react-markdown';
const { details: { host } } = siteConfig;
@ -21,6 +21,8 @@ class AssetInfo extends React.Component {
const canonicalUrl = createCanonicalLink({ asset: { ...claimData, shortId: asset.shortId }});
const assetCanonicalUrl = `${host}${canonicalUrl}`;
// Todo Issue #882 centralize all this media type detection
const embedable = contentType.split('/')[0] === 'image' || contentType === 'video/mp4';
let channelCanonicalUrl;
if (channelName) {
@ -32,133 +34,130 @@ class AssetInfo extends React.Component {
}
return (
<div className='asset-info'>
<HorizontalSplit
leftSide={
description && (
<p className='asset-info__description'>{description}</p>
)
{ description && (
<RowLabeled
label={<Label value={'Description'} />}
content={<div className='asset-info__description'><ReactMarkdown source={description}/></div>}
/>
)}
{editable && (
<RowLabeled
label={<Label value={'Edit'} />}
content={<Link to={`/edit${canonicalUrl}`}>{name}</Link>}
/>
)}
{channelName && (
<RowLabeled
label={
<Label value={'Channel'} />
}
content={
<span className='text'>
<Link className='link--primary' to={channelCanonicalUrl}>{channelName}</Link>
</span>
}
/>
)}
{claimViews ? (
<RowLabeled
label={
<Label value={'Views'} />
}
content={
<span className='text'>
{claimViews}
</span>
}
/>
) : null}
<RowLabeled
label={
<Label value={'Share'} />
}
rightSide={
<div>
{editable && (
<RowLabeled
label={<Label value={'Edit:'} />}
content={<Link to={`/edit${canonicalUrl}`}>{name}</Link>}
/>
)}
{channelName && (
<RowLabeled
label={
<Label value={'Channel'} />
}
content={
<span className='text'>
<Link className='link--primary' to={channelCanonicalUrl}>{channelName}</Link>
</span>
}
/>
)}
{claimViews ? (
<RowLabeled
label={
<Label value={'Views'} />
}
content={
<span className='text'>
{claimViews}
</span>
}
/>
) : null}
content={
<AssetShareButtons
name={name}
assetUrl={assetCanonicalUrl}
/>
}
/>
<RowLabeled
label={
<Label value={'Share'} />
}
content={
<AssetShareButtons
name={name}
assetUrl={assetCanonicalUrl}
/>
}
/>
<RowLabeled
label={
<Label value={'Link'} />
}
content={
<RowLabeled
label={
<Label value={'Link'} />
}
content={
<ClickToCopy
id={'short-link'}
value={assetCanonicalUrl}
/>
}
/>
{embedable && (
<RowLabeled
label={
<Label value={'Embed'} />
}
content={
<div>
{(contentType === 'video/mp4') ? (
<ClickToCopy
id={'short-link'}
value={assetCanonicalUrl}
id={'embed-text-video'}
value={`<iframe src="${host}/video-embed${canonicalUrl}" allowfullscreen="true" style="border:0"></iframe>`}
/>
}
/>
<RowLabeled
label={
<Label value={'Embed'} />
}
content={
<div>
{(contentType === 'video/mp4') ? (
<ClickToCopy
id={'embed-text-video'}
value={`<iframe src="${host}/video-embed${canonicalUrl}" allowfullscreen="true" style="border:0"></iframe>`}
/>
) : (
<ClickToCopy
id={'embed-text-image'}
value={`<img alt="${name}" src="${assetCanonicalUrl}.${fileExt}" />`}
/>
)}
</div>
}
/>
<RowLabeled
label={
<Label value={'LBRY URI'} />
}
content={
) : (
<ClickToCopy
id={'lbry-permanent-url'}
value={`${createPermanentURI(asset)}`}
id={'embed-text-image'}
value={`<img alt="${name}" src="${assetCanonicalUrl}.${fileExt}" />`}
/>
}
/>
)}
</div>
}
/>
)}
<RowLabeled
label={
<Label value={'LBRY URI'} />
}
content={
<ClickToCopy
id={'lbry-permanent-url'}
value={`${createPermanentURI(asset)}`}
/>
}
/>
<SpaceBetween>
<a
className='link--primary'
href={`${assetCanonicalUrl}.${fileExt}`}
>
Direct Link
</a>
<a
className={'link--primary'}
href={`${assetCanonicalUrl}.${fileExt}`}
download={name}
>
Download
</a>
<a
className={'link--primary'}
href={`https://open.lbry.io/${createPermanentURI(asset)}`}
download={name}
>
LBRY URL
</a>
<a
className={'link--primary'}
target='_blank'
href='https://lbry.io/dmca'
>
Report
</a>
</SpaceBetween>
</div>
} />
<SpaceBetween>
<a
className='link--primary'
href={`${assetCanonicalUrl}.${fileExt}`}
>
Direct Link
</a>
<a
className={'link--primary'}
href={`${assetCanonicalUrl}.${fileExt}`}
download={name}
>
Download
</a>
<a
className={'link--primary'}
href={`https://open.lbry.io/${createPermanentURI(asset)}`}
download={name}
>
LBRY URL
</a>
<a
className={'link--primary'}
target='_blank'
href='https://lbry.io/dmca'
>
Report
</a>
</SpaceBetween>
<AssetInfoFooter />
</div>
);

View file

@ -2,7 +2,7 @@ import React from 'react';
const AssetTitle = ({ title }) => {
return (
<h1 className='asset-title'>{title}</h1>
<h2 className='asset-title'>{title}</h2>
);
};

View file

@ -10,34 +10,36 @@ class FaqPage extends React.Component {
pageUri={'tos'}
>
<Row>
<h1>Frequently Asked Questions</h1>
</Row>
<Row>
<h3>What is spee.ch?</h3>
<p>Spee.ch is a media-hosting site that reads from and publishes content to the <a href='http://lbry.io/'>LBRY blockchain</a>.</p>
</Row>
<Row>
<h3>OK But Why Should I Care?</h3>
<p>Spee.ch is a fast and easy way to host your images, videos, and other content. What makes this different from other similar sites is that Spee.ch is hosted on the LBRY blockchain. That means it is impossible for your content to be censored via digital means. Even if we took down Spee.ch today, all content would remain immutably stored on the LBRY blockchain.</p>
<p>Blockchain technology doesnt solve <a href='https://xkcd.com/538/'>the 5 dollar wrench attack</a>, but it solves just about every other problem in media hosting and distribution.</p>
<p>Even better - you can host your own clone of Spee.ch to get even more control over your content. <a href='https://github.com/lbryio/spee.ch/blob/master/README.md'>CLICK HERE FOR INFO</a>.</p>
<p>Spee.ch is just the beginning of what will soon be a vibrant ecosystem of LBRY-powered apps. Use LBRY and youre one step closer to true freedom.</p>
</Row>
<Row>
<h3>How to Use spee.ch</h3>
<p>Its easy. Drag the image or video file of your choice into the center of the spee.ch homepage.</p>
<p>Spee.ch is currently best suited for web optimized MP4 video and standard image filetypes (JPEG, PNG, GIF).</p>
<p>If you want to refer to a piece of content repeatedly, or to build a collection of related content, you could create a channel. Channels work both for private collections and for public repositories. Theres more info about how to do this <a href='https://spee.ch/login'>on the channel page</a>.</p>
<p>Published files will be wiewable and embeddable with any web browser and accesible in the LBRY app. You can also use spee.ch to view free and non-NSFW content published on LBRY network from LBRY app. You just need to replace "lbry://" with "http://spee.ch/" in the URL.</p>
</Row>
<Row>
<h3>How Long Does Content Stay on Spee.ch?</h3>
<p>All content uploaded on spee.ch is guaranteed to stay up for at least 10 years with no maintenance. Future updates will likely extend that time horizon further as blockchain technology improves.</p>
</Row>
<Row>
<h3>Contribute</h3>
<p>If you have an idea for your own spee.ch-like site on top of LBRY, fork our <a href='https://github.com/lbryio/spee.ch'>github repo</a> and go to town!</p>
<p>If you want to improve spee.ch, join <a href='https://chat.lbry.io/'>our discord channel</a> or solve one of our <a href='https://github.com/lbryio/spee.ch/issues'>github issues</a>.</p>
<Row>
<h1>Frequently Asked Questions</h1>
</Row>
<Row>
<h3>What is spee.ch?</h3>
<p>Spee.ch is a media-hosting site that reads from and publishes content to the <a href='http://lbry.io/'>LBRY blockchain</a>.</p>
</Row>
<Row>
<h3>OK But Why Should I Care?</h3>
<p>Spee.ch is a fast and easy way to host your images, videos, and other content. What makes this different from other similar sites is that Spee.ch is hosted on the LBRY blockchain. That means it is impossible for your content to be censored via digital means. Even if we took down Spee.ch today, all content would remain immutably stored on the LBRY blockchain.</p>
<p>Blockchain technology doesnt solve <a href='https://xkcd.com/538/'>the 5 dollar wrench attack</a>, but it solves just about every other problem in media hosting and distribution.</p>
<p>Even better - you can host your own clone of Spee.ch to get even more control over your content. <a href='https://github.com/lbryio/spee.ch/blob/master/README.md'>CLICK HERE FOR INFO</a>.</p>
<p>Spee.ch is just the beginning of what will soon be a vibrant ecosystem of LBRY-powered apps. Use LBRY and youre one step closer to true freedom.</p>
</Row>
<Row>
<h3>How to Use spee.ch</h3>
<p>Its easy. Drag the image or video file of your choice into the center of the spee.ch homepage.</p>
<p>Spee.ch is currently best suited for web optimized MP4 video and standard image filetypes (JPEG, PNG, GIF).</p>
<p>If you want to refer to a piece of content repeatedly, or to build a collection of related content, you could create a channel. Channels work both for private collections and for public repositories. Theres more info about how to do this <a href='https://spee.ch/login'>on the channel page</a>.</p>
<p>Published files will be wiewable and embeddable with any web browser and accesible in the LBRY app. You can also use spee.ch to view free and non-NSFW content published on LBRY network from LBRY app. You just need to replace "lbry://" with "http://spee.ch/" in the URL.</p>
</Row>
<Row>
<h3>How Long Does Content Stay on Spee.ch?</h3>
<p>All content uploaded on spee.ch is guaranteed to stay up for at least 10 years with no maintenance. Future updates will likely extend that time horizon further as blockchain technology improves.</p>
</Row>
<Row>
<h3>Contribute</h3>
<p>If you have an idea for your own spee.ch-like site on top of LBRY, fork our <a href='https://github.com/lbryio/spee.ch'>github repo</a> and go to town!</p>
<p>If you want to improve spee.ch, join <a href='https://chat.lbry.io/'>our discord channel</a> or solve one of our <a href='https://github.com/lbryio/spee.ch/issues'>github issues</a>.</p>
</Row>
</Row>
</PageLayout>
);

View file

@ -38,13 +38,16 @@ class ShowAssetDetails extends React.Component {
asset={asset}
>
<div className="asset-main">
<AssetDisplay />
<AssetTitle />
<button className='collapse-button' onClick={this.collapse}>
{this.state.closed ? <Icon.PlusCircle className='plus-icon' /> : <Icon.MinusCircle />}
</button>
<AssetDisplay />
<div>
<button className='collapse-button' onClick={this.collapse}>
{this.state.closed ? <Icon.PlusCircle className='plus-icon' /> : <Icon.MinusCircle />}
</button>
</div>
</div>
{!this.state.closed && <AssetInfo />}
</PageLayout>
);
} else {

View file

@ -10,54 +10,56 @@ class TosPage extends React.Component {
pageUri={'tos'}
>
<Row>
<h1>Terms of Service</h1>
<p>Last updated: September 25, 2018</p>
<p>Please read these Terms of Service ("Terms", "Terms of Service") carefully before using the <a className='link--primary' href='https://spee.ch'>https://spee.ch</a> website (the "Service") operated by <a className='link--primary' href='https://lbry.io'>LBRY INC</a> ("us", "we", or "our").</p>
<p>Your access to and use of the Service is conditioned upon your acceptance of and compliance with these Terms. These Terms apply to all visitors, users and others who wish to access or use the Service.</p>
<p>By accessing or using the Service you agree to be bound by these Terms. If you disagree with any part of the terms then you do not have permission to access the Service.</p>
</Row>
<Row>
<h3>Links To Other Web Sites</h3>
<p>Our Service may contain links to third party web sites or services that are not owned or controlled by <a className='link--primary' href='https://lbry.io'>LBRY INC</a></p>
<p><a className='link--primary' href='https://lbry.io'>LBRY INC</a> has no control over, and assumes no responsibility for the content, privacy policies, or practices of any third party web sites or services. We do not warrant the offerings of any of these entities/individuals or their websites.</p>
<p>You acknowledge and agree that <a className='link--primary' href='https://lbry.io'>LBRY INC</a> shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such third party web sites or services.</p>
<p>We strongly advise you to read the terms and conditions and privacy policies of any third party web sites or services that you visit.</p>
</Row>
<Row>
<h3>Termination</h3>
<p>We may terminate or suspend your access to the Service immediately, without prior notice or liability, under our sole discretion, for any reason whatsoever and without limitation, including but not limited to a breach of the Terms.</p>
<p>All provisions of the Terms which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability.</p>
</Row>
<Row>
<h3>Indemnification</h3>
<p>You agree to defend, indemnify and hold harmless LBRY INC and its licensee and licensors, and their employees, contractors, agents, officers and directors, from and against any and all claims, damages, obligations, losses, liabilities, costs or debt, and expenses (including but not limited to attorney's fees), resulting from or arising out of a) your use and access of the Service, or b) a breach of these Terms.</p>
</Row>
<Row>
<h3>Limitation Of Liability</h3>
<p>In no event shall <a className='link--primary' href='https://lbry.io'>LBRY INC</a>, nor its directors, employees, partners, agents, suppliers, or affiliates, be liable for any indirect, incidental, special, consequential or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses, resulting from (i) your access to or use of or inability to access or use the Service; (ii) any conduct or content of any third party on the Service; (iii) any content obtained from the Service; and (iv) unauthorized access, use or alteration of your transmissions or content, whether based on warranty, contract, tort (including negligence) or any other legal theory, whether or not we have been informed of the possibility of such damage, and even if a remedy set forth herein is found to have failed of its essential purpose.</p>
</Row>
<Row>
<h3>Disclaimer</h3>
<p>Your use of the Service is at your sole risk. The Service is provided on an "AS IS" and "AS AVAILABLE" basis. The Service is provided without warranties of any kind, whether express or implied, including, but not limited to, implied warranties of merchantability, fitness for a particular purpose, non-infringement or course of performance.</p>
<p><a className='link--primary' href='https://lbry.io'>LBRY INC</a> its subsidiaries, affiliates, and its licensors do not warrant that a) the Service will function uninterrupted, secure or available at any particular time or location; b) any errors or defects will be corrected; c) the Service is free of viruses or other harmful components; or d) the results of using the Service will meet your requirements.</p>
</Row>
<Row>
<h3>Exclusions</h3>
<p>Some jurisdictions do not allow the exclusion of certain warranties or the exclusion or limitation of liability for consequential or incidental damages, so the limitations above may not apply to you.</p>
</Row>
<Row>
<h3>Governing Law</h3>
<p>These Terms shall be governed and construed in accordance with the laws of Delaware, United States, without regard to its conflict of law provisions.</p>
<p>Our failure to enforce any right or provision of these Terms will not be considered a waiver of those rights. If any provision of these Terms is held to be invalid or unenforceable by a court, the remaining provisions of these Terms will remain in effect. These Terms constitute the entire agreement between us regarding our Service, and supersede and replace any prior agreements we might have had between us regarding the Service.</p>
</Row>
<Row>
<h3>Changes</h3>
<p>We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is material we will provide at least 30 days notice prior to any new terms taking effect. What constitutes a material change will be determined at our sole discretion.</p>
<p>By continuing to access or use our Service after any revisions become effective, you agree to be bound by the revised terms. If you do not agree to the new terms, you are no longer authorized to use the Service.</p>
</Row>
<Row>
<h3>Contact Us</h3>
<p>If you have any questions about these Terms, please <a className='link--primary' href='mailto:hello@lbry.io'>contact us</a>.</p>
<Row>
<h1>Terms of Service</h1>
<p>Last updated: September 25, 2018</p>
<p>Please read these Terms of Service ("Terms", "Terms of Service") carefully before using the <a className='link--primary' href='https://spee.ch'>https://spee.ch</a> website (the "Service") operated by <a className='link--primary' href='https://lbry.io'>LBRY INC</a> ("us", "we", or "our").</p>
<p>Your access to and use of the Service is conditioned upon your acceptance of and compliance with these Terms. These Terms apply to all visitors, users and others who wish to access or use the Service.</p>
<p>By accessing or using the Service you agree to be bound by these Terms. If you disagree with any part of the terms then you do not have permission to access the Service.</p>
</Row>
<Row>
<h3>Links To Other Web Sites</h3>
<p>Our Service may contain links to third party web sites or services that are not owned or controlled by <a className='link--primary' href='https://lbry.io'>LBRY INC</a></p>
<p><a className='link--primary' href='https://lbry.io'>LBRY INC</a> has no control over, and assumes no responsibility for the content, privacy policies, or practices of any third party web sites or services. We do not warrant the offerings of any of these entities/individuals or their websites.</p>
<p>You acknowledge and agree that <a className='link--primary' href='https://lbry.io'>LBRY INC</a> shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such content, goods or services available on or through any such third party web sites or services.</p>
<p>We strongly advise you to read the terms and conditions and privacy policies of any third party web sites or services that you visit.</p>
</Row>
<Row>
<h3>Termination</h3>
<p>We may terminate or suspend your access to the Service immediately, without prior notice or liability, under our sole discretion, for any reason whatsoever and without limitation, including but not limited to a breach of the Terms.</p>
<p>All provisions of the Terms which by their nature should survive termination shall survive termination, including, without limitation, ownership provisions, warranty disclaimers, indemnity and limitations of liability.</p>
</Row>
<Row>
<h3>Indemnification</h3>
<p>You agree to defend, indemnify and hold harmless LBRY INC and its licensee and licensors, and their employees, contractors, agents, officers and directors, from and against any and all claims, damages, obligations, losses, liabilities, costs or debt, and expenses (including but not limited to attorney's fees), resulting from or arising out of a) your use and access of the Service, or b) a breach of these Terms.</p>
</Row>
<Row>
<h3>Limitation Of Liability</h3>
<p>In no event shall <a className='link--primary' href='https://lbry.io'>LBRY INC</a>, nor its directors, employees, partners, agents, suppliers, or affiliates, be liable for any indirect, incidental, special, consequential or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses, resulting from (i) your access to or use of or inability to access or use the Service; (ii) any conduct or content of any third party on the Service; (iii) any content obtained from the Service; and (iv) unauthorized access, use or alteration of your transmissions or content, whether based on warranty, contract, tort (including negligence) or any other legal theory, whether or not we have been informed of the possibility of such damage, and even if a remedy set forth herein is found to have failed of its essential purpose.</p>
</Row>
<Row>
<h3>Disclaimer</h3>
<p>Your use of the Service is at your sole risk. The Service is provided on an "AS IS" and "AS AVAILABLE" basis. The Service is provided without warranties of any kind, whether express or implied, including, but not limited to, implied warranties of merchantability, fitness for a particular purpose, non-infringement or course of performance.</p>
<p><a className='link--primary' href='https://lbry.io'>LBRY INC</a> its subsidiaries, affiliates, and its licensors do not warrant that a) the Service will function uninterrupted, secure or available at any particular time or location; b) any errors or defects will be corrected; c) the Service is free of viruses or other harmful components; or d) the results of using the Service will meet your requirements.</p>
</Row>
<Row>
<h3>Exclusions</h3>
<p>Some jurisdictions do not allow the exclusion of certain warranties or the exclusion or limitation of liability for consequential or incidental damages, so the limitations above may not apply to you.</p>
</Row>
<Row>
<h3>Governing Law</h3>
<p>These Terms shall be governed and construed in accordance with the laws of Delaware, United States, without regard to its conflict of law provisions.</p>
<p>Our failure to enforce any right or provision of these Terms will not be considered a waiver of those rights. If any provision of these Terms is held to be invalid or unenforceable by a court, the remaining provisions of these Terms will remain in effect. These Terms constitute the entire agreement between us regarding our Service, and supersede and replace any prior agreements we might have had between us regarding the Service.</p>
</Row>
<Row>
<h3>Changes</h3>
<p>We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is material we will provide at least 30 days notice prior to any new terms taking effect. What constitutes a material change will be determined at our sole discretion.</p>
<p>By continuing to access or use our Service after any revisions become effective, you agree to be bound by the revised terms. If you do not agree to the new terms, you are no longer authorized to use the Service.</p>
</Row>
<Row>
<h3>Contact Us</h3>
<p>If you have any questions about these Terms, please <a className='link--primary' href='mailto:hello@lbry.io'>contact us</a>.</p>
</Row>
</Row>
</PageLayout>
);

View file

@ -4,41 +4,41 @@ import createMetaTagsArray from './createMetaTagsArray';
import createCanonicalLink from '@globalutils/createCanonicalLink';
const {
details: {
host,
title: siteTitle,
twitter,
},
assetDefaults: {
description: defaultDescription,
thumbnail: defaultThumbnail,
},
details: { host, title: siteTitle, twitter },
assetDefaults: { description: defaultDescription, thumbnail: defaultThumbnail },
} = siteConfig;
const VIDEO = 'VIDEO';
const IMAGE = 'IMAGE';
const GIF = 'GIF';
const TEXT = 'TEXT';
const determineMediaType = (contentType) => {
const determineMediaType = contentType => {
switch (contentType) {
case 'image/jpg':
case 'image/jpeg':
case 'image/png':
case 'image/svg+xml':
return IMAGE;
case 'image/gif':
return GIF;
case 'video/mp4':
case 'video/webm':
return VIDEO;
case 'text/markdown':
case 'text/plain':
return TEXT;
default:
return '';
}
};
const createAssetMetaTags = (asset) => {
const createAssetMetaTags = asset => {
const { claimData } = asset;
const { contentType } = claimData;
const canonicalLink = createCanonicalLink({ asset: { ...asset.claimData, shortId: asset.shortId }});
const canonicalLink = createCanonicalLink({
asset: { ...asset.claimData, shortId: asset.shortId },
});
const showUrl = `${host}${canonicalLink}`;
const serveUrl = `${showUrl}.${claimData.fileExt}`;
@ -48,14 +48,14 @@ const createAssetMetaTags = (asset) => {
const ogThumbnail = claimData.thumbnail || defaultThumbnail;
// {property: 'og:title'] = ogTitle},
const metaTags = {
'og:title' : ogTitle,
'twitter:title' : ogTitle,
'og:description' : ogDescription,
'og:title': ogTitle,
'twitter:title': ogTitle,
'og:description': ogDescription,
'twitter:description': ogDescription,
'og:url' : showUrl,
'og:site_name' : siteTitle,
'twitter:site' : twitter,
'fb:app_id' : '1371961932852223',
'og:url': showUrl,
'og:site_name': siteTitle,
'twitter:site': twitter,
'fb:app_id': '1371961932852223',
};
if (determineMediaType(contentType) === VIDEO) {
const videoEmbedUrl = `${host}/video-embed${canonicalLink}`;

View file

@ -1,4 +1,4 @@
const determineContentTypeFromExtension = (thumbnail) => {
const determineContentTypeFromExtension = thumbnail => {
if (thumbnail) {
const fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
switch (fileExt) {
@ -11,6 +11,11 @@ const determineContentTypeFromExtension = (thumbnail) => {
return 'image/gif';
case 'mp4':
return 'video/mp4';
case 'svg':
return 'image/svg+xml';
case 'md':
case 'markdown':
return 'text/markdown';
default:
return '';
}

View file

@ -18,6 +18,7 @@ export function validateFile(file) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
case 'image/svg+xml':
if (file.size > maxSizeImage) {
throw new Error(`Sorry, images are limited to ${maxSizeImage / SIZE_MB} megabytes.`);
}

1152
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -63,6 +63,7 @@
"react-feather": "^1.1.4",
"react-ga": "^2.5.3",
"react-helmet": "^5.2.0",
"react-markdown": "^4.0.6",
"react-redux": "^5.1.1",
"react-router-dom": "^4.3.1",
"react-select": "^2.1.1",

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.0"
width="750"
height="600"
id="svg2450">
<defs
id="defs2453" />
<path
d="M 49.593496,600 C 12.021823,606.01223 0.374478,584.2484 0,550.4065 L 0,50 C 0.26436,7.306444 22.977953,0.531662 49.593496,0 L 700,0 C 737.99534,1.025643 751.96677,19.66882 750,50 L 750,550.4065 C 750.61508,591.42014 731.34035,604.25656 700,600 L 49.593496,600 z"
id="path2477"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="M 376,580 C 120.0594,566.76664 10.515234,350.74039 13,201 C 25.291003,120.21718 21.978524,25.547435 376,19 C 654.93316,28.316194 722.07201,66.834005 735,201 C 734.63343,336.58827 649.04448,559.11396 376,580 z"
id="path2475"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
d="M 266.04757,331.75287 L 266.04757,401.89337 L 224.48919,401.89337 L 224.48919,331.75287 L 104.10864,331.75287 L 104.10864,302.29861 L 224.48919,111.77756 L 266.04757,111.77756 L 266.04757,292.77124 L 283.37127,292.77124 L 283.37127,331.75287 L 266.04757,331.75287 z M 224.48919,184.09839 L 155.20761,292.77124 L 224.48919,292.77124 L 224.48919,184.09839 z M 489.08856,300.14471 C 489.08838,315.43793 486.77811,329.57922 482.15776,342.56862 C 477.53705,355.55814 471.18548,366.81655 463.10301,376.34389 C 455.02023,385.87128 445.49507,393.37689 434.52749,398.86073 C 423.55966,404.34459 411.57888,407.08651 398.58511,407.08652 C 385.59997,407.08651 373.11705,404.9216 361.13632,400.59178 C 349.15548,396.26196 337.8266,389.91259 327.14962,381.54364 L 354.42355,352.1026 C 361.6384,357.00948 368.27849,360.61693 374.34382,362.92496 C 380.40903,365.23306 387.04911,366.38709 394.26409,366.38705 C 402.05152,366.38709 409.26642,364.72652 415.90881,361.40533 C 422.55099,358.08423 428.25287,353.39323 433.01448,347.33231 C 437.77583,341.27149 441.52864,334.0566 444.2729,325.68759 C 447.01689,317.31874 448.38896,308.08208 448.38909,297.9776 C 448.38896,282.09436 444.7793,269.67971 437.56013,260.73363 C 430.3407,251.78782 420.67018,247.31485 408.54855,247.3147 C 395.8453,247.31485 384.30058,252.07633 373.91436,261.59915 L 334.07382,256.40601 L 341.43408,111.77756 L 477.83013,111.77756 L 477.83013,152.49023 L 379.96643,152.49023 L 376.94039,213.53943 C 384.15522,210.94084 390.00026,209.28027 394.47552,208.55771 C 398.95061,207.83553 403.93232,207.47435 409.42068,207.47415 C 421.2516,207.47435 432.07395,209.71193 441.88775,214.18692 C 451.70129,218.66228 460.07242,225.01386 467.00117,233.24167 C 473.9296,241.46982 479.34298,251.21301 483.24131,262.47128 C 487.1393,273.72983 489.08838,286.28763 489.08856,300.14471 L 489.08856,300.14471 z M 557.52004,401.89337 L 557.52004,163.74866 L 539.33743,163.74866 L 539.33743,132.1405 C 547.70633,131.55935 554.41689,129.5376 559.46913,126.07523 C 564.52127,122.61341 568.63306,117.84752 571.8045,111.77756 L 599.5277,111.77756 L 599.5277,401.89337 L 557.52004,401.89337 z"
id="text3960"
style="font-size:433px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:100%;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial" />
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -2,7 +2,7 @@ const logger = require('winston');
const {
assetDefaults: { thumbnail: defaultThumbnail },
details: { host }
details: { host },
} = require('@config/siteConfig');
const getterMethods = {
@ -15,8 +15,12 @@ const getterMethods = {
return 'png';
case 'image/gif':
return 'gif';
case 'image/svg+xml':
return 'svg';
case 'video/mp4':
return 'mp4';
case 'text/markdown':
return 'md';
default:
logger.debug('setting unknown file type as file extension jpg');
return 'jpg';
@ -31,141 +35,134 @@ const getterMethods = {
generated_channel() {
console.log(this);
//
}
}
export default (sequelize, {
BOOLEAN,
DATE,
DECIMAL,
ENUM,
INTEGER,
STRING,
TEXT,
}) => sequelize.define(
'claim',
{
id: {
primaryKey: true,
type: INTEGER,
set() { },
},
transaction_hash_id: {
type: STRING,
set() { },
},
vout: {
type: INTEGER,
set() { },
},
name: {
type: STRING,
set() { },
},
claim_id: {
type: STRING,
set() { },
},
claim_type: {
type: INTEGER,
set() { },
},
publisher_id: {
type: STRING,
set() { },
},
publisher_sig: {
type: STRING,
set() { },
},
certificate: {
type: STRING,
set() { },
},
sd_hash: {
type: STRING,
set() { },
},
transaction_time: {
type: INTEGER,
set() { },
},
version: {
type: STRING,
set() { },
},
valid_at_height: {
type: INTEGER,
set() { },
},
height: {
type: INTEGER,
set() { },
},
effective_amount: {
type: INTEGER,
set() { },
},
author: {
type: STRING,
set() { },
},
description: {
type: STRING,
set() { },
},
content_type: {
type: STRING,
set() { },
},
is_nsfw: {
type: BOOLEAN,
set() { },
},
language: {
type: STRING,
set() { },
},
thumbnail_url: {
type: STRING,
set() { },
},
title: {
type: STRING,
set() { },
},
fee: {
type: DECIMAL(58, 8),
set() { },
},
fee_currency: {
type: STRING,
set() { },
},
bid_state: {
type: ENUM('Active', 'Expired', 'Controlling', 'Spent', 'Accepted'),
set() { },
},
created_at: {
type: DATE(6),
set() { },
},
modified_at: {
type: DATE(6),
set() { },
},
fee_address: {
type: STRING,
set() { },
},
claim_address: {
type: STRING,
set() { },
},
},
{
freezeTableName: true,
getterMethods,
timestamps: false, // don't use default timestamps columns
}
);
};
export default (sequelize, { BOOLEAN, DATE, DECIMAL, ENUM, INTEGER, STRING, TEXT }) =>
sequelize.define(
'claim',
{
id: {
primaryKey: true,
type: INTEGER,
set() {},
},
transaction_hash_id: {
type: STRING,
set() {},
},
vout: {
type: INTEGER,
set() {},
},
name: {
type: STRING,
set() {},
},
claim_id: {
type: STRING,
set() {},
},
claim_type: {
type: INTEGER,
set() {},
},
publisher_id: {
type: STRING,
set() {},
},
publisher_sig: {
type: STRING,
set() {},
},
certificate: {
type: STRING,
set() {},
},
sd_hash: {
type: STRING,
set() {},
},
transaction_time: {
type: INTEGER,
set() {},
},
version: {
type: STRING,
set() {},
},
valid_at_height: {
type: INTEGER,
set() {},
},
height: {
type: INTEGER,
set() {},
},
effective_amount: {
type: INTEGER,
set() {},
},
author: {
type: STRING,
set() {},
},
description: {
type: STRING,
set() {},
},
content_type: {
type: STRING,
set() {},
},
is_nsfw: {
type: BOOLEAN,
set() {},
},
language: {
type: STRING,
set() {},
},
thumbnail_url: {
type: STRING,
set() {},
},
title: {
type: STRING,
set() {},
},
fee: {
type: DECIMAL(58, 8),
set() {},
},
fee_currency: {
type: STRING,
set() {},
},
bid_state: {
type: ENUM('Active', 'Expired', 'Controlling', 'Spent', 'Accepted'),
set() {},
},
created_at: {
type: DATE(6),
set() {},
},
modified_at: {
type: DATE(6),
set() {},
},
fee_address: {
type: STRING,
set() {},
},
claim_address: {
type: STRING,
set() {},
},
},
{
freezeTableName: true,
getterMethods,
timestamps: false, // don't use default timestamps columns
}
);

View file

@ -18,92 +18,77 @@ const returnShortId = (claimsArray, longId) => {
shortIdLength += 1;
shortId = longId.substring(0, shortIdLength);
possibleMatches = possibleMatches.filter(element => {
return (element.claim_id && (element.claim_id.substring(0, shortIdLength) === shortId));
return element.claim_id && element.claim_id.substring(0, shortIdLength) === shortId;
});
}
return shortId;
};
const isLongClaimId = (claimId) => {
return (claimId && (claimId.length === 40));
}
const isLongClaimId = claimId => {
return claimId && claimId.length === 40;
};
const isShortClaimId = (claimId) => {
return (claimId && (claimId.length < 40));
}
const isShortClaimId = claimId => {
return claimId && claimId.length < 40;
};
export default (db, table, sequelize) => ({
getClaimChannelName: async publisher_id => {
return await table
.findAll({
where: { claim_id: publisher_id },
attributes: ['name'],
})
.then(result => {
if (result.length === 0) {
throw new Error(`no record found for ${claimId}`);
} else if (result.length !== 1) {
logger.warn(`more than one record matches ${claimId} in db.Claim`);
}
getClaimChannelName: async (publisher_id) => {
return await table.findAll({
where : { claim_id: publisher_id },
attributes: ['name'],
}).then(result => {
if(result.length === 0) {
throw new Error(`no record found for ${claimId}`);
} else if(result.length !== 1) {
logger.warn(`more than one record matches ${claimId} in db.Claim`);
}
return result[0].name;
});
return result[0].name;
});
},
getShortClaimIdFromLongClaimId: async (claimId, claimName, pendingClaim) => {
logger.debug(`claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`);
return await table.findAll({
where: { name: claimName },
order: [['height', 'ASC']],
}).then(result => {
if(result.length === 0) {
throw new Error('No claim(s) found with that claim name');
}
return await table
.findAll({
where: { name: claimName },
order: [['height', 'ASC']],
})
.then(result => {
if (result.length === 0) {
throw new Error('No claim(s) found with that claim name');
}
let list = result.map(claim => claim.dataValues);
if (pendingClaim) {
list = list.concat(pendingClaim);
}
let list = result.map(claim => claim.dataValues);
if (pendingClaim) {
list = list.concat(pendingClaim);
}
return returnShortId(list, claimId);
});
return returnShortId(list, claimId);
});
},
getAllChannelClaims: async (channelClaimId, params) => {
getAllChannelClaims: async (channelClaimId, bidState) => {
logger.debug(`claim.getAllChannelClaims for ${channelClaimId}`);
const defaultWhereClauses = {
bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] }
const whereClause = bidState || {
[sequelize.Op.or]: [
{ bid_state: 'Controlling' },
{ bid_state: 'Active' },
{ bid_state: 'Accepted' },
],
};
const addWhereClauses = (whereClauses, params) => {
/*
input params = { col: ['Val', 'Val']}
output = { col: { Op.or : [ { Op.eq: 'Value'},...]}, col2:...}
*/
const cols = Object.keys(params)
for (let colKey in cols){
let col = Object.keys(params)[colKey]
whereClauses[col] = {}
whereClauses[col][sequelize.Op.or] = []
for (let itemKey in params[col] ){
let itemsArr = params[col]
whereClauses[col][sequelize.Op.or].push({ [sequelize.Op.eq]: itemsArr[itemKey] })
}
}
return whereClauses;
}
const whereClause = addWhereClauses(defaultWhereClauses, params);
const selectWhere = {
...whereClause,
publisher_id: channelClaimId,
};
return await table.findAll({
where: selectWhere,
order: [['height', 'DESC'],['claim_id', 'ASC']],
})
return await table
.findAll({
where: selectWhere,
order: [['height', 'DESC'], ['claim_id', 'ASC']],
})
.then(channelClaimsArray => {
if (channelClaimsArray.length === 0) {
return null;
@ -114,67 +99,80 @@ export default (db, table, sequelize) => ({
getClaimIdByLongChannelId: async (channelClaimId, claimName) => {
logger.debug(`finding claim id for claim ${claimName} from channel ${channelClaimId}`);
return await table.findAll({
where: { name: claimName, publisher_id: channelClaimId, bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] } },
order: [['id', 'ASC']],
})
.then(result => {
switch (result.length) {
case 0:
return null;
case 1:
return result[0].claim_id;
default:
// Does this actually happen??? (from converted code)
logger.warn(`${result.length} records found for "${claimName}" in channel "${channelClaimId}"`);
return result[0].claim_id;
}
});
return await table
.findAll({
where: {
name: claimName,
publisher_id: channelClaimId,
bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] },
},
order: [['id', 'ASC']],
})
.then(result => {
switch (result.length) {
case 0:
return null;
case 1:
return result[0].claim_id;
default:
// Does this actually happen??? (from converted code)
logger.warn(
`${result.length} records found for "${claimName}" in channel "${channelClaimId}"`
);
return result[0].claim_id;
}
});
},
validateLongClaimId: async (name, claimId) => {
return await table.findOne({
where: {
name,
claim_id: claimId,
},
}).then(result => {
if (!result) {
return false;
}
return claimId;
});
return await table
.findOne({
where: {
name,
claim_id: claimId,
},
})
.then(result => {
if (!result) {
return false;
}
return claimId;
});
},
getLongClaimIdFromShortClaimId: async (name, shortId) => {
return await table.findAll({
where: {
name,
claim_id: {
[sequelize.Op.like]: `${shortId}%`,
}},
order: [['height', 'ASC']],
})
.then(result => {
if(result.length === 0) {
return null;
}
return await table
.findAll({
where: {
name,
claim_id: {
[sequelize.Op.like]: `${shortId}%`,
},
},
order: [['height', 'ASC']],
})
.then(result => {
if (result.length === 0) {
return null;
}
return result[0].claim_id;
});
return result[0].claim_id;
});
},
getTopFreeClaimIdByClaimName: async (name) => {
return await table.findAll({
// TODO: Limit 1
where: { name, bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] } },
order: [['effective_amount', 'DESC'], ['height', 'ASC']],
}).then(result => {
if(result.length === 0) {
return null;
}
return result[0].claim_id;
})
getTopFreeClaimIdByClaimName: async name => {
return await table
.findAll({
// TODO: Limit 1
where: { name, bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] } },
order: [['effective_amount', 'DESC'], ['height', 'ASC']],
})
.then(result => {
if (result.length === 0) {
return null;
}
return result[0].claim_id;
});
},
getLongClaimId: async (claimName, claimId) => {
@ -191,60 +189,63 @@ export default (db, table, sequelize) => ({
resolveClaim: async (name, claimId) => {
logger.debug(`Claim.resolveClaim: ${name} ${claimId}`);
return table.findAll({
where: { name, claim_id: claimId },
}).then(claimArray => {
if(claimArray.length === 0) {
return null;
} else if(claimArray.length !== 1) {
logger.warn(`more than one record matches ${name}#${claimId} in db.Claim`);
}
return table
.findAll({
where: { name, claim_id: claimId },
})
.then(claimArray => {
if (claimArray.length === 0) {
return null;
} else if (claimArray.length !== 1) {
logger.warn(`more than one record matches ${name}#${claimId} in db.Claim`);
}
return claimArray[0];
});
return claimArray[0];
});
},
resolveClaimInChannel: async (claimName, channelId) => {
logger.debug(`Claim.resolveClaimByNames: ${claimName} in ${channelId}`);
return table.findAll({
where: {
name: claimName,
publisher_id: channelId,
},
}).then(claimArray => {
if (claimArray.length === 0) {
return null;
} else if (claimArray.length !== 1) {
logger.warn(`more than one record matches ${claimName} in ${channelId}`);
}
return table
.findAll({
where: {
name: claimName,
publisher_id: channelId,
},
})
.then(claimArray => {
if (claimArray.length === 0) {
return null;
} else if (claimArray.length !== 1) {
logger.warn(`more than one record matches ${claimName} in ${channelId}`);
}
return claimArray[0];
});
return claimArray[0];
});
},
getOutpoint: async (name, claimId) => {
logger.debug(`finding outpoint for ${name}#${claimId}`);
return await table.findAll({
where : { name, claim_id: claimId },
attributes: ['transaction_hash_id'],
}).then(result => {
if(result.length === 0) {
throw new Error(`no record found for ${name}#${claimId}`);
} else if(result.length !== 1) {
logger.warn(`more than one record matches ${name}#${claimId} in db.Claim`);
}
return await table
.findAll({
where: { name, claim_id: claimId },
attributes: ['transaction_hash_id'],
})
.then(result => {
if (result.length === 0) {
throw new Error(`no record found for ${name}#${claimId}`);
} else if (result.length !== 1) {
logger.warn(`more than one record matches ${name}#${claimId} in db.Claim`);
}
return result[0].transaction_hash_id;
});
return result[0].transaction_hash_id;
});
},
getCurrentHeight: async () => {
return await table
.max('height')
.then(result => {
return (result || 100000);
return await table.max('height').then(result => {
return result || 100000;
});
},
})
});

View file

@ -1,20 +1,15 @@
const db = require('../../../../models');
const chainquery = require('chainquery').default;
const getClaimData = require('server/utils/getClaimData');
const { returnPaginatedChannelClaims } = require('./channelPagination.js');
const getChannelClaims = async (channelName, channelLongId, page) => {
const params = {
content_type: ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'video/mp4'],
};
let channelShortId = await chainquery.claim.queries.getShortClaimIdFromLongClaimId(
channelLongId,
channelName
);
let channelClaims;
if (channelLongId) {
channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelLongId, params);
channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelLongId);
}
/*
Put mempool unconfirmed claims at the beginning

View file

@ -1,21 +1,18 @@
const logger = require('winston');
const {
publishing: {
maxSizeImage = 10000000,
maxSizeGif = 50000000,
maxSizeVideo = 50000000,
}
publishing: { maxSizeImage = 10000000, maxSizeGif = 50000000, maxSizeVideo = 50000000 },
} = require('@config/siteConfig');
const SIZE_MB = 1000000;
const validateFileTypeAndSize = (file) => {
const validateFileTypeAndSize = file => {
// check file type and size
switch (file.type) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
case 'image/svg+xml':
if (file.size > maxSizeImage) {
logger.debug('publish > file validation > .jpeg/.jpg/.png was too big');
throw new Error(`Sorry, images are limited to ${maxSizeImage / SIZE_MB} megabytes.`);
@ -35,7 +32,11 @@ const validateFileTypeAndSize = (file) => {
break;
default:
logger.debug('publish > file validation > unrecognized file type');
throw new Error('The ' + file.type + ' content type is not supported. Only, image/jpg, image/png, image/gif, and video/mp4 content types are currently supported.');
throw new Error(
'The ' +
file.type +
' content type is not supported. Only, image/jpg, image/png, image/gif, and video/mp4 content types are currently supported.'
);
}
return file;
};

View file

@ -1,12 +1,17 @@
const logger = require('winston');
const returnShortId = require('./utils/returnShortId.js');
const isApprovedChannel = require('../../utils/isApprovedChannel');
const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('@config/siteConfig');
const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig');
const {
assetDefaults: { thumbnail: defaultThumbnail },
details: { host },
} = require('@config/siteConfig');
const {
publishing: { serveOnlyApproved, approvedChannels },
} = require('@config/siteConfig');
const NO_CLAIM = 'NO_CLAIM';
function determineFileExtensionFromContentType (contentType) {
function determineFileExtensionFromContentType(contentType) {
switch (contentType) {
case 'image/jpeg':
case 'image/jpg':
@ -17,20 +22,22 @@ function determineFileExtensionFromContentType (contentType) {
return 'gif';
case 'video/mp4':
return 'mp4';
case 'image/svg+xml':
return 'svg';
default:
logger.debug('setting unknown file type as file extension jpg');
return 'jpg';
}
}
function determineThumbnail (storedThumbnail, defaultThumbnail) {
function determineThumbnail(storedThumbnail, defaultThumbnail) {
if (storedThumbnail === '') {
return defaultThumbnail;
}
return storedThumbnail;
}
function prepareClaimData (claim) {
function prepareClaimData(claim) {
// logger.debug('preparing claim data based on resolved data:', claim);
claim['thumbnail'] = determineThumbnail(claim.thumbnail, defaultThumbnail);
claim['fileExt'] = determineFileExtensionFromContentType(claim.contentType);
@ -38,12 +45,12 @@ function prepareClaimData (claim) {
return claim;
}
function isLongClaimId (claimId) {
return (claimId && (claimId.length === 40));
function isLongClaimId(claimId) {
return claimId && claimId.length === 40;
}
function isShortClaimId (claimId) {
return (claimId && (claimId.length < 40));
function isShortClaimId(claimId) {
return claimId && claimId.length < 40;
}
module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
@ -51,141 +58,141 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
'Claim',
{
address: {
type : STRING,
type: STRING,
default: null,
},
amount: {
type : DECIMAL(19, 8),
type: DECIMAL(19, 8),
default: null,
},
claimId: {
type : STRING,
type: STRING,
default: null,
},
claimSequence: {
type : INTEGER,
type: INTEGER,
default: null,
},
decodedClaim: {
type : BOOLEAN,
type: BOOLEAN,
default: null,
},
depth: {
type : INTEGER,
type: INTEGER,
default: null,
},
effectiveAmount: {
type : DECIMAL(19, 8),
type: DECIMAL(19, 8),
default: null,
},
hasSignature: {
type : BOOLEAN,
type: BOOLEAN,
default: null,
},
height: {
type : INTEGER,
type: INTEGER,
default: null,
},
hex: {
type : TEXT('long'),
type: TEXT('long'),
default: null,
},
name: {
type : STRING,
type: STRING,
default: null,
},
nout: {
type : INTEGER,
type: INTEGER,
default: null,
},
txid: {
type : STRING,
type: STRING,
default: null,
},
validAtHeight: {
type : INTEGER,
type: INTEGER,
default: null,
},
outpoint: {
type : STRING,
type: STRING,
default: null,
},
claimType: {
type : STRING,
type: STRING,
default: null,
},
certificateId: {
type : STRING,
type: STRING,
default: null,
},
author: {
type : STRING,
type: STRING,
default: null,
},
description: {
type : TEXT('long'),
type: TEXT('long'),
default: null,
},
language: {
type : STRING,
type: STRING,
default: null,
},
license: {
type : STRING,
type: STRING,
default: null,
},
licenseUrl: {
type : STRING,
type: STRING,
default: null,
},
nsfw: {
type : BOOLEAN,
type: BOOLEAN,
default: null,
},
preview: {
type : STRING,
type: STRING,
default: null,
},
thumbnail: {
type : STRING,
type: STRING,
default: null,
},
title: {
type : STRING,
type: STRING,
default: null,
},
metadataVersion: {
type : STRING,
type: STRING,
default: null,
},
contentType: {
type : STRING,
type: STRING,
default: null,
},
source: {
type : STRING,
type: STRING,
default: null,
},
sourceType: {
type : STRING,
type: STRING,
default: null,
},
sourceVersion: {
type : STRING,
type: STRING,
default: null,
},
streamVersion: {
type : STRING,
type: STRING,
default: null,
},
valueVersion: {
type : STRING,
type: STRING,
default: null,
},
channelName: {
type : STRING,
type: STRING,
allowNull: true,
default : null,
default: null,
},
},
{
@ -201,14 +208,13 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getShortClaimIdFromLongClaimId = function (claimId, claimName) {
Claim.getShortClaimIdFromLongClaimId = function(claimId, claimName) {
logger.debug(`Claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`);
return new Promise((resolve, reject) => {
this
.findAll({
where: { name: claimName },
order: [['height', 'ASC']],
})
this.findAll({
where: { name: claimName },
order: [['height', 'ASC']],
})
.then(result => {
switch (result.length) {
case 0:
@ -223,15 +229,14 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getAllChannelClaims = function (channelClaimId) {
Claim.getAllChannelClaims = function(channelClaimId) {
logger.debug(`Claim.getAllChannelClaims for ${channelClaimId}`);
return new Promise((resolve, reject) => {
this
.findAll({
where: { certificateId: channelClaimId },
order: [['height', 'DESC']],
raw : true, // returns an array of only data, not an array of instances
})
this.findAll({
where: { certificateId: channelClaimId },
order: [['height', 'DESC']],
raw: true, // returns an array of only data, not an array of instances
})
.then(channelClaimsArray => {
switch (channelClaimsArray.length) {
case 0:
@ -251,14 +256,13 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getClaimIdByLongChannelId = function (channelClaimId, claimName) {
Claim.getClaimIdByLongChannelId = function(channelClaimId, claimName) {
logger.debug(`finding claim id for claim ${claimName} from channel ${channelClaimId}`);
return new Promise((resolve, reject) => {
this
.findAll({
where: { name: claimName, certificateId: channelClaimId },
order: [['id', 'ASC']],
})
this.findAll({
where: { name: claimName, certificateId: channelClaimId },
order: [['id', 'ASC']],
})
.then(result => {
switch (result.length) {
case 0:
@ -266,7 +270,9 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
case 1:
return resolve(result[0].claimId);
default:
logger.warn(`${result.length} records found for "${claimName}" in channel "${channelClaimId}"`);
logger.warn(
`${result.length} records found for "${claimName}" in channel "${channelClaimId}"`
);
return resolve(result[0].claimId);
}
})
@ -276,7 +282,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.validateLongClaimId = function (name, claimId) {
Claim.validateLongClaimId = function(name, claimId) {
return new Promise((resolve, reject) => {
this.findOne({
where: {
@ -297,17 +303,17 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getLongClaimIdFromShortClaimId = function (name, shortId) {
Claim.getLongClaimIdFromShortClaimId = function(name, shortId) {
return new Promise((resolve, reject) => {
this
.findAll({
where: {
name,
claimId: {
[sequelize.Op.like]: `${shortId}%`,
}},
order: [['height', 'ASC']],
})
this.findAll({
where: {
name,
claimId: {
[sequelize.Op.like]: `${shortId}%`,
},
},
order: [['height', 'ASC']],
})
.then(result => {
switch (result.length) {
case 0:
@ -323,13 +329,12 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getTopFreeClaimIdByClaimName = function (name) {
Claim.getTopFreeClaimIdByClaimName = function(name) {
return new Promise((resolve, reject) => {
this
.findAll({
where: { name },
order: [['effectiveAmount', 'DESC'], ['height', 'ASC']],
})
this.findAll({
where: { name },
order: [['effectiveAmount', 'DESC'], ['height', 'ASC']],
})
.then(result => {
switch (result.length) {
case 0:
@ -345,7 +350,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getLongClaimId = function (claimName, claimId) {
Claim.getLongClaimId = function(claimName, claimId) {
logger.debug(`getLongClaimId(${claimName}, ${claimId})`);
if (isLongClaimId(claimId)) {
return this.validateLongClaimId(claimName, claimId);
@ -356,13 +361,12 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
}
};
Claim.fetchClaim = function (name, claimId) {
Claim.fetchClaim = function(name, claimId) {
logger.debug(`Claim.resolveClaim: ${name} ${claimId}`);
return new Promise((resolve, reject) => {
this
.findAll({
where: { name, claimId },
})
this.findAll({
where: { name, claimId },
})
.then(claimArray => {
switch (claimArray.length) {
case 0:
@ -380,13 +384,15 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.resolveClaim = function (name, claimId) {
Claim.resolveClaim = function(name, claimId) {
return new Promise((resolve, reject) => {
this
.fetchClaim(name, claimId)
this.fetchClaim(name, claimId)
.then(claim => {
logger.info('resolveClaim claims:', claim);
if (serveOnlyApproved && !isApprovedChannel({ longId: claim.certificateId }, approvedChannels)) {
if (
serveOnlyApproved &&
!isApprovedChannel({ longId: claim.certificateId }, approvedChannels)
) {
throw new Error('This content is unavailable');
}
return resolve(claim);
@ -397,13 +403,12 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getOutpoint = function (name, claimId) {
Claim.getOutpoint = function(name, claimId) {
logger.debug(`finding outpoint for ${name}#${claimId}`);
return this
.findAll({
where : { name, claimId },
attributes: ['outpoint'],
})
return this.findAll({
where: { name, claimId },
attributes: ['outpoint'],
})
.then(result => {
logger.debug('outpoint result');
switch (result.length) {
@ -421,10 +426,9 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.getCurrentHeight = function () {
Claim.getCurrentHeight = function() {
return new Promise((resolve, reject) => {
return this
.max('height')
return this.max('height')
.then(result => {
if (result) {
return resolve(result);