cuts release from staging #905
54 changed files with 2725 additions and 1475 deletions
5
.prettierrc.json
Normal file
5
.prettierrc.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100,
|
||||
"singleQuote": true
|
||||
}
|
|
@ -85,6 +85,8 @@ $ npm run configure
|
|||
#### Build & start the app
|
||||
|
||||
```
|
||||
$ npm run build
|
||||
|
||||
$ npm run start
|
||||
```
|
||||
|
||||
|
@ -114,7 +116,7 @@ curl -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/
|
|||
```
|
||||
Parameters:
|
||||
|
||||
* `name` (required)
|
||||
* `name` (required, must be unique across the instance)
|
||||
* `file` (required) (must be type .mp4, .jpeg, .jpg, .gif, or .png)
|
||||
* `nsfw` (optional)
|
||||
* `license` (optional)
|
||||
|
@ -262,4 +264,4 @@ We take security seriously. Please contact security@lbry.io regarding any securi
|
|||
|
||||
## Contact
|
||||
|
||||
The primary contact for this project is [@skhameneh](mailto:shawn@lbry.io).
|
||||
The primary contact for this project is [@jessopb](mailto:jessop@lbry.io).
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"publishing": {
|
||||
"primaryClaimAddress": null,
|
||||
"uploadDirectory": "/home/lbry/Uploads",
|
||||
"lbrynetHome": "/home/lbry",
|
||||
"thumbnailChannel": null,
|
||||
"thumbnailChannelId": null,
|
||||
"additionalClaimAddresses": [],
|
||||
|
@ -39,6 +40,18 @@
|
|||
"maxSizeGif": 50000000,
|
||||
"maxSizeVideo": 50000000
|
||||
},
|
||||
"serving": {
|
||||
"markdownSettings": {
|
||||
"skipHtml": true,
|
||||
"privilegedDisallowedTypesDescriptions": ["Image"],
|
||||
"privilegedDisallowedTypesMain": [],
|
||||
"publicDisallowedTypesDescriptions": ["Image"],
|
||||
"publicDisallowedTypesMain": []
|
||||
},
|
||||
"customFileExtensions": {
|
||||
"application/example-type": "example"
|
||||
}
|
||||
},
|
||||
"startup": {
|
||||
"performChecks": true,
|
||||
"performUpdates": true
|
||||
|
|
7
client/scss/_asset-blocked.scss
Normal file
7
client/scss/_asset-blocked.scss
Normal file
|
@ -0,0 +1,7 @@
|
|||
.asset-blocked__image {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.asset-blocked__text {
|
||||
width: 100%;
|
||||
}
|
|
@ -1,29 +1,28 @@
|
|||
.asset-main {
|
||||
height: 75vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.asset-document {
|
||||
width: 100%;
|
||||
padding: $thin-padding;
|
||||
height: fit-content;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.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 +109,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 +121,7 @@
|
|||
}
|
||||
|
||||
.asset-footer {
|
||||
border-top: 1px solid $grey-border;
|
||||
border-top: $subtle-border;
|
||||
padding-top: $primary-padding;
|
||||
margin-top: $primary-padding;
|
||||
color: $grey;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,23 +1,24 @@
|
|||
.channel-claims-display {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-gap: 16px;
|
||||
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: 1040px) {
|
||||
.channel-claims-display {
|
||||
@media (min-width: $break-point-large) and (max-width: $break-point-x-large){
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: $break-point-tablet) and (max-width: $break-point-large) {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 1039px) {
|
||||
.channel-claims-display {
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -2,13 +2,17 @@
|
|||
padding-top: $thin-padding;
|
||||
padding-bottom: $thin-padding;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
font-size: $text-medium;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.label-radio {
|
||||
padding-left: $thin-padding;
|
||||
padding-right: $thin-padding;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media (max-width: $break-point-tablet ) {
|
||||
|
|
141
client/scss/_markdown.scss
Normal file
141
client/scss/_markdown.scss
Normal file
|
@ -0,0 +1,141 @@
|
|||
.markdown-preview {
|
||||
// Headers
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: 600;
|
||||
margin-bottom: $tertiary-padding;
|
||||
padding-top: $tertiary-padding;
|
||||
}
|
||||
|
||||
// Paragraphs
|
||||
p {
|
||||
font-size: 1.15rem;
|
||||
margin-bottom: $tertiary-padding;
|
||||
white-space: pre-line;
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
margin-left: 0.2rem;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-radius: 8px;
|
||||
background: $blockquote-background;
|
||||
padding: $tertiary-padding;
|
||||
min-width: 60%;
|
||||
}
|
||||
|
||||
// Strikethrough text
|
||||
del {
|
||||
}
|
||||
|
||||
// Tables
|
||||
table {
|
||||
width: 100%;
|
||||
margin-bottom: 1.2rem;
|
||||
background-color: $base-color;
|
||||
border-spacing: 0;
|
||||
border: .5px solid $chrome-color;
|
||||
|
||||
tr {
|
||||
td,
|
||||
th,
|
||||
td:first-of-type,
|
||||
th:first-of-type,
|
||||
td:last-of-type,
|
||||
th:last-of-type {
|
||||
padding: $thin-padding $tertiary-padding;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
td:last-of-type {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th {
|
||||
background: $chrome-color;
|
||||
}
|
||||
|
||||
}
|
||||
tr:nth-child(even){
|
||||
background: $chrome-color;
|
||||
}
|
||||
}
|
||||
|
||||
// Image
|
||||
img {
|
||||
margin-bottom: $tertiary-padding;
|
||||
margin-top: $tertiary-padding;
|
||||
padding: $secondary-padding;
|
||||
object-fit: scale-down;
|
||||
max-width: 100%;
|
||||
border: $subtle-border;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 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: $tertiary-padding;
|
||||
padding: $tertiary-padding;
|
||||
|
||||
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: $thin-padding;
|
||||
|
||||
> li {
|
||||
list-style-position: outside;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: initial;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-left: $primary-padding;
|
||||
|
||||
p {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
.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;
|
||||
padding: $tertiary-padding;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,21 +4,21 @@
|
|||
|
||||
.row-labeled {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
padding-bottom: $tertiary-padding;
|
||||
}
|
||||
|
||||
.row-labeled-label {
|
||||
width: 30%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
.row-labeled-content {
|
||||
align-self: center;
|
||||
width: 70%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: $break-point-tablet ) {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
select {
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
background: $base-color;
|
||||
border: 0;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,40 +1,62 @@
|
|||
$base-color: white;
|
||||
$primary-color: #005da0;
|
||||
//backgrounds
|
||||
$base-color: white; //default white
|
||||
$card-color: white; //default white
|
||||
$chrome-color: lightgray; //default white (navbar)
|
||||
$blockquote-background: #EEEEFF;
|
||||
$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);
|
||||
$blockquote-text: $text-color;
|
||||
|
||||
|
||||
//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-phone: 300px;
|
||||
$break-point-phone: 300px;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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}
|
||||
src={thumb || defaultThumbnail}
|
||||
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' />
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className='asset-preview__title'>{title}</h3>
|
||||
</Link>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
// TODO: factor out longId OR implement tooltip display
|
||||
const ChannelInfoDisplay = ({name, longId, shortId}) => {
|
||||
return (
|
||||
<div>
|
||||
<h2>channel name: {name}</h2>
|
||||
<p className={'text--secondary'}>full channel id: {longId}</p>
|
||||
<p className={'text--secondary'}>short channel id: {shortId}</p>
|
||||
<h2>{name}:{shortId}</h2>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
53
client/src/components/FileViewer/index.jsx
Normal file
53
client/src/components/FileViewer/index.jsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import React from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
// TODO: get markdown settings from siteConfig
|
||||
|
||||
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) });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.abortController.abort();
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className={'markdown'}>
|
||||
{
|
||||
this.state.fileLoaded &&
|
||||
<ReactMarkdown className={'markdown-preview'} source={this.state.fileText} skipHtml />
|
||||
}
|
||||
{
|
||||
!this.state.fileLoaded &&
|
||||
<p>Loading your file...</p>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FileViewer;
|
|
@ -1,16 +1,13 @@
|
|||
import React from 'react';
|
||||
import createCanonicalLink from '../../../../utils/createCanonicalLink';
|
||||
import createCanonicalLink from '@globalutils/createCanonicalLink';
|
||||
import HorizontalSplit from '@components/HorizontalSplit';
|
||||
/*
|
||||
This component shouldn't be necessary after pagination is reworked,
|
||||
though it might be useful for channel_mine situations.
|
||||
*/
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +15,10 @@ class BlockedLeft extends React.PureComponent {
|
|||
class BlockedRight extends React.PureComponent {
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +50,6 @@ class AssetBlocked extends React.Component {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default AssetBlocked;
|
||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import Row from '@components/Row';
|
||||
import ProgressBar from '@components/ProgressBar';
|
||||
import { LOCAL_CHECK, UNAVAILABLE, ERROR, AVAILABLE } from '../../constants/asset_display_states';
|
||||
import createCanonicalLink from '../../../../utils/createCanonicalLink';
|
||||
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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +64,7 @@ class AssetDisplay extends React.Component {
|
|||
if (typeof contentType === 'string') {
|
||||
fileExt = contentType.split('/')[1] || 'jpg';
|
||||
}
|
||||
const sourceUrl = `${createCanonicalLink({ asset: asset.claimData })}.${fileExt}?${outpoint}`;
|
||||
const sourceUrl = `${createCanonicalLink({ asset: asset.claimData })}.${fileExt}?outpoint=${outpoint}`;
|
||||
return (
|
||||
<div className={'asset-display'}>
|
||||
{(status === LOCAL_CHECK) &&
|
||||
|
@ -95,6 +106,6 @@ class AssetDisplay extends React.Component {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default AssetDisplay;
|
||||
|
|
|
@ -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 '../../../../utils/createCanonicalLink';
|
||||
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,9 @@ 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
|
||||
// Todo get markdown settings from siteConfig
|
||||
const embedable = contentType.split('/')[0] === 'image' || contentType === 'video/mp4';
|
||||
|
||||
let channelCanonicalUrl;
|
||||
if (channelName) {
|
||||
|
@ -32,21 +35,20 @@ class AssetInfo extends React.Component {
|
|||
}
|
||||
return (
|
||||
<div className='asset-info'>
|
||||
<HorizontalSplit
|
||||
leftSide={
|
||||
description && (
|
||||
<p className='asset-info__description'>{description}</p>
|
||||
)
|
||||
}
|
||||
rightSide={
|
||||
<div>
|
||||
{ description && (
|
||||
<RowLabeled
|
||||
label={<Label value={'Description'} />}
|
||||
content={<div className='asset-info__description'><ReactMarkdown className={'markdown-preview'} skipHtml disallowedTypes={['image']} source={description} /></div>}
|
||||
/>
|
||||
)}
|
||||
{editable && (
|
||||
<RowLabeled
|
||||
label={<Label value={'Edit:'} />}
|
||||
label={<Label value={'Edit'} />}
|
||||
content={<Link to={`/edit${canonicalUrl}`}>{name}</Link>}
|
||||
/>
|
||||
)}
|
||||
{channelName && (
|
||||
|
||||
<RowLabeled
|
||||
label={
|
||||
<Label value={'Channel'} />
|
||||
|
@ -94,7 +96,7 @@ class AssetInfo extends React.Component {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{embedable && (
|
||||
<RowLabeled
|
||||
label={
|
||||
<Label value={'Embed'} />
|
||||
|
@ -109,13 +111,13 @@ class AssetInfo extends React.Component {
|
|||
) : (
|
||||
<ClickToCopy
|
||||
id={'embed-text-image'}
|
||||
value={`<img src="${assetCanonicalUrl}.${fileExt}"/>`}
|
||||
value={`<img alt="${name}" src="${assetCanonicalUrl}.${fileExt}" />`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
)}
|
||||
<RowLabeled
|
||||
label={
|
||||
<Label value={'LBRY URI'} />
|
||||
|
@ -157,8 +159,6 @@ class AssetInfo extends React.Component {
|
|||
Report
|
||||
</a>
|
||||
</SpaceBetween>
|
||||
</div>
|
||||
} />
|
||||
<AssetInfoFooter />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
|
||||
const AssetTitle = ({ title }) => {
|
||||
return (
|
||||
<h1 className='asset-title'>{title}</h1>
|
||||
<h2 className='asset-title'>{title}</h2>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,11 +3,14 @@ import { selectFile, updateError, clearFile } from '../../actions/publish';
|
|||
import { selectAsset } from '../../selectors/show';
|
||||
import View from './view';
|
||||
import siteConfig from '@config/siteConfig.json';
|
||||
import createCanonicalLink from '../../../../utils/createCanonicalLink';
|
||||
import createCanonicalLink from '@globalutils/createCanonicalLink';
|
||||
|
||||
const { assetDefaults: { thumbnail: defaultThumbnail } } = siteConfig;
|
||||
const {
|
||||
assetDefaults: { thumbnail: defaultThumbnail },
|
||||
} = siteConfig;
|
||||
|
||||
const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate } }) => {
|
||||
const mapStateToProps = ({ show, publish: { file, thumbnail, error, isUpdate } }) => {
|
||||
const fileError = error.file;
|
||||
const obj = { file, thumbnail, fileError, isUpdate };
|
||||
let asset, name, claimId, fileExt, outpoint, sourceUrl;
|
||||
if (isUpdate) {
|
||||
|
@ -28,14 +31,17 @@ const mapStateToProps = ({ show, publish: { file, thumbnail, fileError, isUpdate
|
|||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
selectFile: (file) => {
|
||||
selectFile: file => {
|
||||
dispatch(selectFile(file));
|
||||
},
|
||||
setFileError: (value) => {
|
||||
setFileError: value => {
|
||||
dispatch(clearFile());
|
||||
dispatch(updateError('file', value));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(View);
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(View);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { NavLink, withRouter } from 'react-router-dom';
|
||||
import NavBarChannelOptionsDropdown from '@components/NavBarChannelOptionsDropdown';
|
||||
import createCanonicalLink from '../../../../utils/createCanonicalLink';
|
||||
import createCanonicalLink from '@globalutils/createCanonicalLink';
|
||||
|
||||
const VIEW = 'VIEW';
|
||||
const LOGOUT = 'LOGOUT';
|
||||
|
|
|
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
|||
import createPageTitle from '../../utils/createPageTitle';
|
||||
import createMetaTags from '../../utils/createMetaTags';
|
||||
import oEmbed from '../../utils/oEmbed.js';
|
||||
import createCanonicalLink from '../../../../utils/createCanonicalLink';
|
||||
import createCanonicalLink from '@globalutils/createCanonicalLink';
|
||||
|
||||
import siteConfig from '@config/siteConfig.json';
|
||||
const { details: { host } } = siteConfig;
|
||||
|
|
|
@ -9,6 +9,7 @@ class FaqPage extends React.Component {
|
|||
pageTitle={'Frequently Asked Questions'}
|
||||
pageUri={'tos'}
|
||||
>
|
||||
<Row>
|
||||
<Row>
|
||||
<h1>Frequently Asked Questions</h1>
|
||||
</Row>
|
||||
|
@ -39,6 +40,7 @@ class FaqPage extends React.Component {
|
|||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,13 +38,16 @@ class ShowAssetDetails extends React.Component {
|
|||
asset={asset}
|
||||
>
|
||||
<div className="asset-main">
|
||||
<AssetDisplay />
|
||||
<AssetTitle />
|
||||
<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 {
|
||||
|
|
|
@ -9,6 +9,7 @@ class TosPage extends React.Component {
|
|||
pageTitle={'Terms of Service'}
|
||||
pageUri={'tos'}
|
||||
>
|
||||
<Row>
|
||||
<Row>
|
||||
<h1>Terms of Service</h1>
|
||||
<p>Last updated: September 25, 2018</p>
|
||||
|
@ -59,6 +60,7 @@ class TosPage extends React.Component {
|
|||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
import siteConfig from '@config/siteConfig.json';
|
||||
import determineContentTypeFromExtension from './determineContentTypeFromExtension';
|
||||
import createMetaTagsArray from './createMetaTagsArray';
|
||||
import createCanonicalLink from '../../../utils/createCanonicalLink';
|
||||
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}`;
|
||||
|
||||
|
@ -46,6 +46,9 @@ const createAssetMetaTags = (asset) => {
|
|||
const ogDescription = claimData.description || defaultDescription;
|
||||
const ogThumbnailContentType = determineContentTypeFromExtension(claimData.thumbnail);
|
||||
const ogThumbnail = claimData.thumbnail || defaultThumbnail;
|
||||
|
||||
console.log('asset.claimData', asset.claimData);
|
||||
|
||||
// {property: 'og:title'] = ogTitle},
|
||||
const metaTags = {
|
||||
'og:title': ogTitle,
|
||||
|
@ -85,8 +88,6 @@ const createAssetMetaTags = (asset) => {
|
|||
// image tags
|
||||
metaTags['og:image'] = serveUrl;
|
||||
metaTags['og:image'] = serveUrl;
|
||||
metaTags['og:image:width'] = 600;
|
||||
metaTags['og:image:height'] = 315;
|
||||
metaTags['og:image:type'] = contentType;
|
||||
metaTags['twitter:image'] = serveUrl;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import siteConfig from '@config/siteConfig.json';
|
||||
import determineContentTypeFromExtension from './determineContentTypeFromExtension';
|
||||
import createMetaTagsArray from './createMetaTagsArray';
|
||||
import createCanonicalLink from '../../../utils/createCanonicalLink';
|
||||
import createCanonicalLink from '@globalutils/createCanonicalLink';
|
||||
|
||||
const {
|
||||
details: {
|
||||
|
|
|
@ -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 '';
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import siteConfig from '@config/siteConfig.json';
|
||||
|
||||
const {
|
||||
publishing: {
|
||||
maxSizeImage = 10000000,
|
||||
maxSizeGif = 50000000,
|
||||
maxSizeVideo = 50000000,
|
||||
}
|
||||
publishing: { maxSizeImage = 10000000, maxSizeGif = 50000000, maxSizeVideo = 50000000 },
|
||||
} = siteConfig;
|
||||
|
||||
// TODO: central constants location
|
||||
const SIZE_MB = 1000000;
|
||||
|
||||
export function validateFile(file) {
|
||||
if (!file) {
|
||||
|
@ -21,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.`);
|
||||
}
|
||||
|
@ -36,6 +34,9 @@ export function validateFile (file) {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.');
|
||||
throw new Error(
|
||||
file.type +
|
||||
' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
6
lintstagedrc.json
Normal file
6
lintstagedrc.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"linters": {
|
||||
"src/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
|
||||
"src/**/*.{js,jsx}": ["eslint --fix", "flow focus-check --color always", "git add"]
|
||||
}
|
||||
}
|
1983
package-lock.json
generated
1983
package-lock.json
generated
File diff suppressed because it is too large
Load diff
23
package.json
23
package.json
|
@ -51,6 +51,7 @@
|
|||
"inquirer": "^5.2.0",
|
||||
"ip": "^1.1.5",
|
||||
"make-dir": "^1.3.0",
|
||||
"mime-types": "^2.1.21",
|
||||
"module-alias": "^2.1.0",
|
||||
"mysql2": "^1.6.4",
|
||||
"npm": "^6.3.0",
|
||||
|
@ -63,6 +64,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",
|
||||
|
@ -103,13 +105,15 @@
|
|||
"extract-css-chunks-webpack-plugin": "^3.2.1",
|
||||
"file-loader": "^2.0.0",
|
||||
"har-validator": "^5.1.3",
|
||||
"husky": "^1.1.3",
|
||||
"husky": "^1.3.1",
|
||||
"lint-staged": "^8.1.0",
|
||||
"md5-file": "^4.0.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"mocha": "^5.2.0",
|
||||
"ndb": "^1.0.26",
|
||||
"ndb": "^1.0.42",
|
||||
"node-sass": "^4.11.0",
|
||||
"nodemon": "^1.18.6",
|
||||
"prettier": "1.15.3",
|
||||
"react-color": "^2.14.1",
|
||||
"react-hot-loader": "^4.6.0",
|
||||
"redux-devtools": "^3.4.1",
|
||||
|
@ -128,7 +132,18 @@
|
|||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "eslint ."
|
||||
}
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"git add"
|
||||
],
|
||||
"*.{json,css,md}": [
|
||||
"prettier --write",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
24
public/assets/img/451sign.svg
Normal file
24
public/assets/img/451sign.svg
Normal 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 |
|
@ -1,48 +1,14 @@
|
|||
const logger = require('winston');
|
||||
|
||||
const {
|
||||
assetDefaults: { thumbnail: defaultThumbnail },
|
||||
details: { host }
|
||||
} = require('@config/siteConfig');
|
||||
const mime = require('mime-types');
|
||||
|
||||
const getterMethods = {
|
||||
generated_extension() {
|
||||
switch (this.content_type) {
|
||||
case 'image/jpeg':
|
||||
case 'image/jpg':
|
||||
return 'jpg';
|
||||
case 'image/png':
|
||||
return 'png';
|
||||
case 'image/gif':
|
||||
return 'gif';
|
||||
case 'video/mp4':
|
||||
return 'mp4';
|
||||
default:
|
||||
logger.debug('setting unknown file type as file extension jpg');
|
||||
return 'jpg';
|
||||
}
|
||||
return mime.extension(this.content_type) ? mime.extension(this.content_type) : 'jpg';
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: Factor this out.
|
||||
generated_thumbnail() {
|
||||
return this.thumbnail_url || defaultThumbnail;
|
||||
},
|
||||
|
||||
generated_channel() {
|
||||
console.log(this);
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
export default (sequelize, {
|
||||
BOOLEAN,
|
||||
DATE,
|
||||
DECIMAL,
|
||||
ENUM,
|
||||
INTEGER,
|
||||
STRING,
|
||||
TEXT,
|
||||
}) => sequelize.define(
|
||||
export default (sequelize, { BOOLEAN, DATE, DECIMAL, ENUM, INTEGER, STRING, TEXT }) =>
|
||||
sequelize.define(
|
||||
'claim',
|
||||
{
|
||||
id: {
|
||||
|
|
|
@ -18,27 +18,28 @@ 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({
|
||||
getClaimChannelName: async publisher_id => {
|
||||
return await table
|
||||
.findAll({
|
||||
where: { claim_id: publisher_id },
|
||||
attributes: ['name'],
|
||||
}).then(result => {
|
||||
})
|
||||
.then(result => {
|
||||
if (result.length === 0) {
|
||||
throw new Error(`no record found for ${claimId}`);
|
||||
} else if (result.length !== 1) {
|
||||
|
@ -51,10 +52,12 @@ export default (db, table, sequelize) => ({
|
|||
|
||||
getShortClaimIdFromLongClaimId: async (claimId, claimName, pendingClaim) => {
|
||||
logger.debug(`claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`);
|
||||
return await table.findAll({
|
||||
return await table
|
||||
.findAll({
|
||||
where: { name: claimName },
|
||||
order: [['height', 'ASC']],
|
||||
}).then(result => {
|
||||
})
|
||||
.then(result => {
|
||||
if (result.length === 0) {
|
||||
throw new Error('No claim(s) found with that claim name');
|
||||
}
|
||||
|
@ -68,39 +71,21 @@ export default (db, table, sequelize) => ({
|
|||
});
|
||||
},
|
||||
|
||||
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({
|
||||
return await table
|
||||
.findAll({
|
||||
where: selectWhere,
|
||||
order: [['height', 'DESC'], ['claim_id', 'ASC']],
|
||||
})
|
||||
|
@ -114,8 +99,13 @@ 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'] } },
|
||||
return await table
|
||||
.findAll({
|
||||
where: {
|
||||
name: claimName,
|
||||
publisher_id: channelClaimId,
|
||||
bid_state: { [sequelize.Op.or]: ['Controlling', 'Active', 'Accepted'] },
|
||||
},
|
||||
order: [['id', 'ASC']],
|
||||
})
|
||||
.then(result => {
|
||||
|
@ -126,19 +116,23 @@ export default (db, table, sequelize) => ({
|
|||
return result[0].claim_id;
|
||||
default:
|
||||
// Does this actually happen??? (from converted code)
|
||||
logger.warn(`${result.length} records found for "${claimName}" in channel "${channelClaimId}"`);
|
||||
logger.warn(
|
||||
`${result.length} records found for "${claimName}" in channel "${channelClaimId}"`
|
||||
);
|
||||
return result[0].claim_id;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
validateLongClaimId: async (name, claimId) => {
|
||||
return await table.findOne({
|
||||
return await table
|
||||
.findOne({
|
||||
where: {
|
||||
name,
|
||||
claim_id: claimId,
|
||||
},
|
||||
}).then(result => {
|
||||
})
|
||||
.then(result => {
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
@ -147,12 +141,14 @@ export default (db, table, sequelize) => ({
|
|||
},
|
||||
|
||||
getLongClaimIdFromShortClaimId: async (name, shortId) => {
|
||||
return await table.findAll({
|
||||
return await table
|
||||
.findAll({
|
||||
where: {
|
||||
name,
|
||||
claim_id: {
|
||||
[sequelize.Op.like]: `${shortId}%`,
|
||||
}},
|
||||
},
|
||||
},
|
||||
order: [['height', 'ASC']],
|
||||
})
|
||||
.then(result => {
|
||||
|
@ -164,17 +160,19 @@ export default (db, table, sequelize) => ({
|
|||
});
|
||||
},
|
||||
|
||||
getTopFreeClaimIdByClaimName: async (name) => {
|
||||
return await table.findAll({
|
||||
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 => {
|
||||
})
|
||||
.then(result => {
|
||||
if (result.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return result[0].claim_id;
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
getLongClaimId: async (claimName, claimId) => {
|
||||
|
@ -191,9 +189,11 @@ export default (db, table, sequelize) => ({
|
|||
|
||||
resolveClaim: async (name, claimId) => {
|
||||
logger.debug(`Claim.resolveClaim: ${name} ${claimId}`);
|
||||
return table.findAll({
|
||||
return table
|
||||
.findAll({
|
||||
where: { name, claim_id: claimId },
|
||||
}).then(claimArray => {
|
||||
})
|
||||
.then(claimArray => {
|
||||
if (claimArray.length === 0) {
|
||||
return null;
|
||||
} else if (claimArray.length !== 1) {
|
||||
|
@ -206,12 +206,14 @@ export default (db, table, sequelize) => ({
|
|||
|
||||
resolveClaimInChannel: async (claimName, channelId) => {
|
||||
logger.debug(`Claim.resolveClaimByNames: ${claimName} in ${channelId}`);
|
||||
return table.findAll({
|
||||
return table
|
||||
.findAll({
|
||||
where: {
|
||||
name: claimName,
|
||||
publisher_id: channelId,
|
||||
},
|
||||
}).then(claimArray => {
|
||||
})
|
||||
.then(claimArray => {
|
||||
if (claimArray.length === 0) {
|
||||
return null;
|
||||
} else if (claimArray.length !== 1) {
|
||||
|
@ -225,10 +227,12 @@ export default (db, table, sequelize) => ({
|
|||
getOutpoint: async (name, claimId) => {
|
||||
logger.debug(`finding outpoint for ${name}#${claimId}`);
|
||||
|
||||
return await table.findAll({
|
||||
return await table
|
||||
.findAll({
|
||||
where: { name, claim_id: claimId },
|
||||
attributes: ['transaction_hash_id'],
|
||||
}).then(result => {
|
||||
})
|
||||
.then(result => {
|
||||
if (result.length === 0) {
|
||||
throw new Error(`no record found for ${name}#${claimId}`);
|
||||
} else if (result.length !== 1) {
|
||||
|
@ -240,11 +244,8 @@ export default (db, table, sequelize) => ({
|
|||
},
|
||||
|
||||
getCurrentHeight: async () => {
|
||||
return await table
|
||||
.max('height')
|
||||
.then(result => {
|
||||
return (result || 100000);
|
||||
return await table.max('height').then(result => {
|
||||
return result || 100000;
|
||||
});
|
||||
},
|
||||
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
const db = require('../../../../models');
|
||||
const chainquery = require('chainquery').default;
|
||||
const getClaimData = require('server/utils/getClaimData');
|
||||
const { returnPaginatedChannelClaims } = require('./channelPagination.js');
|
||||
|
||||
const getChannelClaims = async (channelName, channelShortId, page) => {
|
||||
const channelId = await chainquery.claim.queries.getLongClaimId(channelName, channelShortId);
|
||||
const params = { content_type: [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'video/mp4',
|
||||
] };
|
||||
const getChannelClaims = async (channelName, channelLongId, page) => {
|
||||
let channelShortId = await chainquery.claim.queries.getShortClaimIdFromLongClaimId(
|
||||
channelLongId,
|
||||
channelName
|
||||
);
|
||||
let channelClaims;
|
||||
if (channelId) {
|
||||
channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelId, params);
|
||||
if (channelLongId) {
|
||||
channelClaims = await chainquery.claim.queries.getAllChannelClaims(channelLongId);
|
||||
}
|
||||
|
||||
/*
|
||||
Put mempool unconfirmed claims at the beginning
|
||||
*/
|
||||
const split = channelClaims.reduce(
|
||||
(acc, val) => val.dataValues.height === 0 ? { ...acc, zero: acc.zero.concat(val) } : { ...acc, nonzero: acc.nonzero.concat(val) },
|
||||
(acc, val) =>
|
||||
val.dataValues.height === 0
|
||||
? { ...acc, zero: acc.zero.concat(val) }
|
||||
: { ...acc, nonzero: acc.nonzero.concat(val) },
|
||||
{ zero: [], nonzero: [] }
|
||||
);
|
||||
channelClaims = split.zero.concat(split.nonzero);
|
||||
|
||||
const processingChannelClaims = channelClaims ? channelClaims.map((claim) => getClaimData(claim, channelName, channelShortId)) : [];
|
||||
const processingChannelClaims = channelClaims
|
||||
? channelClaims.map(claim => getClaimData(claim, channelName, channelShortId))
|
||||
: [];
|
||||
const processedChannelClaims = await Promise.all(processingChannelClaims);
|
||||
|
||||
return returnPaginatedChannelClaims(channelName, channelId, processedChannelClaims, page);
|
||||
return returnPaginatedChannelClaims(channelName, channelShortId, processedChannelClaims, page);
|
||||
};
|
||||
|
||||
module.exports = getChannelClaims;
|
||||
|
|
|
@ -3,7 +3,7 @@ const logger = require('winston');
|
|||
const { details: { host }, publishing: { disabled, disabledMessage } } = require('@config/siteConfig');
|
||||
|
||||
const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js');
|
||||
const isApprovedChannel = require('../../../../../utils/isApprovedChannel');
|
||||
const isApprovedChannel = require('@globalutils/isApprovedChannel');
|
||||
const { publishing: { publishOnlyApproved, approvedChannels } } = require('@config/siteConfig');
|
||||
|
||||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
|
@ -18,7 +18,7 @@ const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js');
|
|||
const authenticateUser = require('./authentication.js');
|
||||
|
||||
const chainquery = require('chainquery').default;
|
||||
const createCanonicalLink = require('../../../../../utils/createCanonicalLink');
|
||||
const createCanonicalLink = require('@globalutils/createCanonicalLink');
|
||||
|
||||
const CLAIM_TAKEN = 'CLAIM_TAKEN';
|
||||
const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL';
|
||||
|
@ -115,7 +115,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
if (channelName) {
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(claimData.certificateId, channelName);
|
||||
} else {
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(claimId, name, claimData).catch(error => {
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(claimId, name, claimData).catch(() => {
|
||||
return claimId.slice(0, 1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,29 +14,29 @@ const parsePublishApiRequestFiles = ({file, thumbnail}, isUpdate) => {
|
|||
}
|
||||
return {};
|
||||
}
|
||||
throw new Error('no file with key of [file] found in request');
|
||||
throw new Error('No file with key of [file] found in request');
|
||||
}
|
||||
if (!file.path) {
|
||||
throw new Error('no file path found');
|
||||
throw new Error('No file path found');
|
||||
}
|
||||
if (!file.type) {
|
||||
throw new Error('no file type found');
|
||||
throw new Error('No file type found');
|
||||
}
|
||||
if (!file.size) {
|
||||
throw new Error('no file size found');
|
||||
throw new Error('No file size found');
|
||||
}
|
||||
// validate the file name
|
||||
if (!file.name) {
|
||||
throw new Error('no file name found');
|
||||
throw new Error('No file name found');
|
||||
}
|
||||
if (file.name.indexOf('.') < 0) {
|
||||
throw new Error('no file extension found in file name');
|
||||
throw new Error('No file extension found in file name');
|
||||
}
|
||||
if (file.name.indexOf('.') === 0) {
|
||||
throw new Error('file name cannot start with "."');
|
||||
throw new Error('File name cannot start with "."');
|
||||
}
|
||||
if (/'/.test(file.name)) {
|
||||
throw new Error('apostrophes are not allowed in the file name');
|
||||
throw new Error('Apostrophes are not allowed in the file name');
|
||||
}
|
||||
|
||||
// validate the 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;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ const parsePublishApiRequestFiles = require('../publish/parsePublishApiRequestFi
|
|||
const authenticateUser = require('../publish/authentication.js');
|
||||
const createThumbnailPublishParams = require('../publish/createThumbnailPublishParams.js');
|
||||
const chainquery = require('chainquery').default;
|
||||
const createCanonicalLink = require('../../../../../utils/createCanonicalLink');
|
||||
const createCanonicalLink = require('@globalutils/createCanonicalLink');
|
||||
|
||||
/*
|
||||
route to update a claim through the daemon
|
||||
|
@ -153,7 +153,7 @@ const claimUpdate = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
if (channelName) {
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(result.certificateId, channelName);
|
||||
} else {
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(result.claimId, name, result).catch(error => {
|
||||
return chainquery.claim.queries.getShortClaimIdFromLongClaimId(result.claimId, name, result).catch(() => {
|
||||
return result.claimId.slice(0, 1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,10 +3,7 @@ const db = require('../../../models');
|
|||
const getClaimId = require('../../utils/getClaimId');
|
||||
|
||||
const {
|
||||
details: {
|
||||
host,
|
||||
title: siteTitle,
|
||||
},
|
||||
details: { host, title: siteTitle },
|
||||
} = require('@config/siteConfig');
|
||||
|
||||
const getOEmbedDataForAsset = (channelName, channelClaimId, claimName, claimId) => {
|
||||
|
@ -43,19 +40,23 @@ const getOEmbedDataForAsset = (channelName, channelClaimId, claimName, claimId)
|
|||
.then(fileRecord => {
|
||||
fileData = fileRecord.dataValues;
|
||||
logger.debug('file data:', fileData);
|
||||
const serveUrl = `${host}/${fileData.claimId}/${fileData.name}.${fileData.fileType.substring(fileData.fileType.indexOf('/') + 1)}`;
|
||||
const serveUrl = `${host}/${fileData.claimId}/${fileData.name}.${fileData.fileType.substring(
|
||||
fileData.fileType.indexOf('/') + 1
|
||||
)}`;
|
||||
// set the resource type
|
||||
if (fileData.fileType === 'video/mp4') {
|
||||
data['type'] = 'video';
|
||||
data['html'] = `<video width="100%" controls poster="${claimData.thumbnail}" src="${serveUrl}"/></video>`;
|
||||
data['html'] = `<video width="100%" controls poster="${
|
||||
claimData.thumbnail
|
||||
}" src="${serveUrl}"/></video>`;
|
||||
} else {
|
||||
data['type'] = 'picture';
|
||||
data['url'] = serveUrl;
|
||||
}
|
||||
// get the data
|
||||
data['title'] = claimData.title;
|
||||
data['width'] = fileData.width || 600;
|
||||
data['height'] = fileData.height || 400;
|
||||
data['width'] = fileData.fileWidth || 600;
|
||||
data['height'] = fileData.fileHeight || 400;
|
||||
data['author_name'] = siteTitle;
|
||||
data['author_url'] = host;
|
||||
})
|
||||
|
|
|
@ -36,7 +36,7 @@ const {
|
|||
const { sessionKey } = require('@private/authConfig.json');
|
||||
|
||||
// configure.js doesn't handle new keys in config.json files yet. Make sure it doens't break.
|
||||
let bLE;
|
||||
let finalBlockListEndpoint;
|
||||
|
||||
function Server () {
|
||||
this.initialize = () => {
|
||||
|
@ -176,30 +176,29 @@ function Server () {
|
|||
return;
|
||||
}
|
||||
if (blockListEndpoint) {
|
||||
bLE = blockListEndpoint;
|
||||
finalBlockListEndpoint = blockListEndpoint;
|
||||
} else if (!blockListEndpoint) {
|
||||
if (typeof (blockListEndpoint) !== 'string') {
|
||||
logger.warn('blockListEndpoint is null due to outdated siteConfig file. \n' +
|
||||
'Continuing with default LBRY blocklist api endpoint. \n ' +
|
||||
'(Specify /"blockListEndpoint" : ""/ to disable.')
|
||||
bLE = 'https://api.lbry.io/file/list_blocked';
|
||||
'(Specify /"blockListEndpoint" : ""/ to disable.');
|
||||
finalBlockListEndpoint = 'https://api.lbry.io/file/list_blocked';
|
||||
}
|
||||
}
|
||||
logger.info(`Peforming updates...`);
|
||||
if (!bLE) {
|
||||
logger.info('Configured for no Block List')
|
||||
if (!finalBlockListEndpoint) {
|
||||
logger.info('Configured for no Block List');
|
||||
db.Tor.refreshTable().then((updatedTorList) => {
|
||||
logger.info('Tor list updated, length:', updatedTorList.length);
|
||||
});
|
||||
} else {
|
||||
|
||||
return Promise.all([
|
||||
db.Blocked.refreshTable(bLE),
|
||||
db.Blocked.refreshTable(finalBlockListEndpoint),
|
||||
db.Tor.refreshTable()])
|
||||
.then(([updatedBlockedList, updatedTorList]) => {
|
||||
logger.info('Blocked list updated, length:', updatedBlockedList.length);
|
||||
logger.info('Tor list updated, length:', updatedTorList.length);
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
this.start = () => {
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
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';
|
||||
|
||||
|
@ -17,6 +22,8 @@ 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';
|
||||
|
@ -39,11 +46,11 @@ function prepareClaimData (claim) {
|
|||
}
|
||||
|
||||
function isLongClaimId(claimId) {
|
||||
return (claimId && (claimId.length === 40));
|
||||
return claimId && claimId.length === 40;
|
||||
}
|
||||
|
||||
function isShortClaimId(claimId) {
|
||||
return (claimId && (claimId.length < 40));
|
||||
return claimId && claimId.length < 40;
|
||||
}
|
||||
|
||||
module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
||||
|
@ -204,8 +211,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
Claim.getShortClaimIdFromLongClaimId = function(claimId, claimName) {
|
||||
logger.debug(`Claim.getShortClaimIdFromLongClaimId for ${claimName}#${claimId}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
.findAll({
|
||||
this.findAll({
|
||||
where: { name: claimName },
|
||||
order: [['height', 'ASC']],
|
||||
})
|
||||
|
@ -226,8 +232,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
Claim.getAllChannelClaims = function(channelClaimId) {
|
||||
logger.debug(`Claim.getAllChannelClaims for ${channelClaimId}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
.findAll({
|
||||
this.findAll({
|
||||
where: { certificateId: channelClaimId },
|
||||
order: [['height', 'DESC']],
|
||||
raw: true, // returns an array of only data, not an array of instances
|
||||
|
@ -254,8 +259,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
Claim.getClaimIdByLongChannelId = function(channelClaimId, claimName) {
|
||||
logger.debug(`finding claim id for claim ${claimName} from channel ${channelClaimId}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
.findAll({
|
||||
this.findAll({
|
||||
where: { name: claimName, certificateId: channelClaimId },
|
||||
order: [['id', 'ASC']],
|
||||
})
|
||||
|
@ -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);
|
||||
}
|
||||
})
|
||||
|
@ -299,13 +305,13 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
|
||||
Claim.getLongClaimIdFromShortClaimId = function(name, shortId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
.findAll({
|
||||
this.findAll({
|
||||
where: {
|
||||
name,
|
||||
claimId: {
|
||||
[sequelize.Op.like]: `${shortId}%`,
|
||||
}},
|
||||
},
|
||||
},
|
||||
order: [['height', 'ASC']],
|
||||
})
|
||||
.then(result => {
|
||||
|
@ -325,8 +331,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
|
||||
Claim.getTopFreeClaimIdByClaimName = function(name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
.findAll({
|
||||
this.findAll({
|
||||
where: { name },
|
||||
order: [['effectiveAmount', 'DESC'], ['height', 'ASC']],
|
||||
})
|
||||
|
@ -359,8 +364,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
Claim.fetchClaim = function(name, claimId) {
|
||||
logger.debug(`Claim.resolveClaim: ${name} ${claimId}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
.findAll({
|
||||
this.findAll({
|
||||
where: { name, claimId },
|
||||
})
|
||||
.then(claimArray => {
|
||||
|
@ -382,11 +386,13 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
|
||||
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);
|
||||
|
@ -399,8 +405,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
|
||||
Claim.getOutpoint = function(name, claimId) {
|
||||
logger.debug(`finding outpoint for ${name}#${claimId}`);
|
||||
return this
|
||||
.findAll({
|
||||
return this.findAll({
|
||||
where: { name, claimId },
|
||||
attributes: ['outpoint'],
|
||||
})
|
||||
|
@ -423,8 +428,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
|
||||
Claim.getCurrentHeight = function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
return this
|
||||
.max('height')
|
||||
return this.max('height')
|
||||
.then(result => {
|
||||
if (result) {
|
||||
return resolve(result);
|
||||
|
|
|
@ -12,10 +12,8 @@ import * as httpContext from 'express-http-context';
|
|||
import Reducers from '@reducers';
|
||||
import GAListener from '@components/GAListener';
|
||||
import App from '@app';
|
||||
import Sagas from '@sagas';
|
||||
import Actions from '@actions';
|
||||
|
||||
const createCanonicalLink = require('../../utils/createCanonicalLink');
|
||||
const createCanonicalLink = require('@globalutils/createCanonicalLink');
|
||||
|
||||
const getCanonicalUrlFromShow = show => {
|
||||
const requestId = show.requestList[show.request.id];
|
||||
|
@ -56,7 +54,6 @@ export default (req, res) => {
|
|||
|
||||
const runSaga = (action !== false && saga !== false);
|
||||
const renderPage = (store) => {
|
||||
|
||||
// Workaround, remove when a solution for async httpContext exists
|
||||
const showState = store.getState().show;
|
||||
const assetKeys = Object.keys(showState.assetList);
|
||||
|
@ -128,7 +125,7 @@ export default (req, res) => {
|
|||
res.redirect(canonicalUrl);
|
||||
}
|
||||
|
||||
return renderPage(store)
|
||||
return renderPage(store);
|
||||
});
|
||||
} else {
|
||||
const store = createStore(Reducers);
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
const chainquery = require('chainquery').default;
|
||||
const db = require('server/models');
|
||||
|
||||
const fetchClaimData = async (params) => {
|
||||
const fetchClaimData = async params => {
|
||||
let { claimId, claimName: name } = params;
|
||||
if (claimId === 'none') claimId = null;
|
||||
|
||||
const [cq, local] = await Promise.all([
|
||||
chainquery.claim.queries.resolveClaim(name, claimId).then(res => res.dataValues).catch(() => {}),
|
||||
chainquery.claim.queries.resolveClaim(name, claimId).catch(() => {}),
|
||||
db.Claim.resolveClaim(name, claimId).catch(() => {}),
|
||||
]);
|
||||
|
||||
// Todo: don't use localdb to get post publish content
|
||||
if (!cq && !local) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,52 +1,62 @@
|
|||
const { details: { host } } = require('@config/siteConfig');
|
||||
const {
|
||||
details: { host },
|
||||
assetDefaults: { thumbnail },
|
||||
} = require('@config/siteConfig');
|
||||
const chainquery = require('chainquery').default;
|
||||
const { getClaim } = require('server/lbrynet');
|
||||
// const { getClaim } = require('server/lbrynet');
|
||||
const { isBlocked } = require('./blockList');
|
||||
|
||||
module.exports = async (data, chName = null, chShortId = null) => {
|
||||
// TODO: Refactor getching the channel name out; requires invasive changes.
|
||||
const certificateId = data.publisher_id || data.certificateId;
|
||||
let lbrynetClaimResult = null;
|
||||
let lbrynetFileExt = null;
|
||||
const dataVals = data.dataValues ? data.dataValues : data;
|
||||
const txid = dataVals.transaction_hash_id || dataVals.txid;
|
||||
let nout;
|
||||
|
||||
if (typeof dataVals.vout === 'number') {
|
||||
nout = dataVals.vout;
|
||||
} else {
|
||||
nout = dataVals.nout;
|
||||
}
|
||||
|
||||
const outpoint = `${txid}:${nout}`;
|
||||
const certificateId = dataVals.publisher_id || dataVals.certificateId;
|
||||
const fileExt = data.generated_extension || dataVals.fileExt;
|
||||
|
||||
let channelShortId = chShortId;
|
||||
let channelName = chName;
|
||||
// TODO: Factor blocked out
|
||||
let blocked;
|
||||
const outPoint = `${data.transaction_hash_id}:${data.vout}`;
|
||||
if (isBlocked(outPoint)) {
|
||||
|
||||
if (isBlocked(outpoint)) {
|
||||
blocked = true;
|
||||
}
|
||||
|
||||
if (!chName && certificateId && !channelName) {
|
||||
channelName = await chainquery.claim.queries.getClaimChannelName(certificateId).catch(() => {
|
||||
});
|
||||
channelName = await chainquery.claim.queries.getClaimChannelName(certificateId).catch(() => {});
|
||||
}
|
||||
|
||||
if (!chShortId && certificateId && channelName) {
|
||||
channelShortId = await chainquery.claim.queries.getShortClaimIdFromLongClaimId(certificateId, channelName).catch(() => null);
|
||||
channelShortId = await chainquery.claim.queries
|
||||
.getShortClaimIdFromLongClaimId(certificateId, channelName)
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
if (!chName && !chShortId && !data.fileExt) {
|
||||
const lbrynetUri = `${data.name}#${data.claim_id}`;
|
||||
lbrynetClaimResult = await getClaim(lbrynetUri).catch(() => {
|
||||
return 'invalid URI';
|
||||
});
|
||||
lbrynetFileExt = lbrynetClaimResult && lbrynetClaimResult.file_name && lbrynetClaimResult.file_name.includes('.') && lbrynetClaimResult.file_name.split('.').slice(-1).pop();
|
||||
}
|
||||
// Find a solution for the legacy application/octet-stream file extensions
|
||||
|
||||
return ({
|
||||
name : data.name,
|
||||
title : data.title,
|
||||
return {
|
||||
name: dataVals.name,
|
||||
title: dataVals.title,
|
||||
certificateId,
|
||||
channelName,
|
||||
channelShortId,
|
||||
contentType: data.content_type || data.contentType,
|
||||
claimId : data.claim_id || data.claimId,
|
||||
fileExt : data.generated_extension || data.fileExt || lbrynetFileExt,
|
||||
description: data.description,
|
||||
thumbnail : data.generated_thumbnail || data.thumbnail_url || data.thumbnail,
|
||||
outpoint : outPoint || data.outpoint,
|
||||
contentType: dataVals.content_type || data.contentType,
|
||||
claimId: dataVals.claim_id || data.claimId,
|
||||
fileExt: fileExt,
|
||||
description: dataVals.description,
|
||||
thumbnail: dataVals.thumbnail_url || data.thumbnail || thumbnail,
|
||||
outpoint,
|
||||
host,
|
||||
pending : Boolean(data.height === 0),
|
||||
pending: Boolean(dataVals.height === 0),
|
||||
blocked: blocked,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ async function getMediaDimensions (fileType, filePath) {
|
|||
[ height, width ] = await getVideoHeightAndWidth(filePath);
|
||||
break;
|
||||
default:
|
||||
logger.error('unable to create File data for unspported file type:', fileType);
|
||||
logger.error('unable to create File dimension data for unspported file type:', fileType);
|
||||
break;
|
||||
}
|
||||
return {
|
||||
|
|
Loading…
Add table
Reference in a new issue