2020-07-29 19:07:13 +03:00
import * as React from "react" ;
import * as NavigationData from "~/common/navigation-data" ;
import * as Actions from "~/common/actions" ;
import * as State from "~/common/state" ;
import * as Credentials from "~/common/credentials" ;
2020-08-02 09:00:04 +03:00
import * as Validations from "~/common/validations" ;
2020-08-20 08:20:18 +03:00
import * as FileUtilities from "~/common/file-utilities" ;
2020-08-09 11:38:16 +03:00
import * as System from "~/components/system" ;
2020-07-29 19:07:13 +03:00
// NOTE(jim):
// Scenes each have an ID and can be navigated to with _handleAction
import SceneDeals from "~/scenes/SceneDeals" ;
import SceneEditAccount from "~/scenes/SceneEditAccount" ;
import SceneFile from "~/scenes/SceneFile" ;
import SceneFilesFolder from "~/scenes/SceneFilesFolder" ;
import SceneHome from "~/scenes/SceneHome" ;
import SceneSettings from "~/scenes/SceneSettings" ;
import SceneWallet from "~/scenes/SceneWallet" ;
import SceneSlates from "~/scenes/SceneSlates" ;
import SceneLocalData from "~/scenes/SceneLocalData" ;
import SceneSettingsDeveloper from "~/scenes/SceneSettingsDeveloper" ;
import SceneSignIn from "~/scenes/SceneSignIn" ;
import SceneSlate from "~/scenes/SceneSlate" ;
2020-08-08 03:17:54 +03:00
import SceneActivity from "~/scenes/SceneActivity" ;
2020-08-18 22:41:41 +03:00
import SceneDirectory from "~/scenes/SceneDirectory" ;
2020-08-25 00:15:51 +03:00
import SceneProfile from "~/scenes/SceneProfile" ;
2020-07-29 19:07:13 +03:00
// NOTE(jim):
// Sidebars each have a decorator and can be shown to with _handleAction
import SidebarCreateSlate from "~/components/sidebars/SidebarCreateSlate" ;
import SidebarCreateWalletAddress from "~/components/sidebars/SidebarCreateWalletAddress" ;
import SidebarWalletSendFunds from "~/components/sidebars/SidebarWalletSendFunds" ;
import SidebarFileStorageDeal from "~/components/sidebars/SidebarFileStorageDeal" ;
import SidebarAddFileToBucket from "~/components/sidebars/SidebarAddFileToBucket" ;
2020-08-02 10:55:55 +03:00
import SidebarDragDropNotice from "~/components/sidebars/SidebarDragDropNotice" ;
2020-08-09 11:08:46 +03:00
import SidebarSingleSlateSettings from "~/components/sidebars/SidebarSingleSlateSettings" ;
2020-08-28 09:57:04 +03:00
import SidebarFilecoinArchive from "~/components/sidebars/SidebarFilecoinArchive" ;
2020-07-29 19:07:13 +03:00
// NOTE(jim):
// Core components to the application structure.
import ApplicationNavigation from "~/components/core/ApplicationNavigation" ;
import ApplicationHeader from "~/components/core/ApplicationHeader" ;
import ApplicationLayout from "~/components/core/ApplicationLayout" ;
import WebsitePrototypeWrapper from "~/components/core/WebsitePrototypeWrapper" ;
import Cookies from "universal-cookie" ;
const cookies = new Cookies ( ) ;
2020-08-14 12:17:19 +03:00
const SIDEBARS = {
2020-08-28 09:57:04 +03:00
SIDEBAR _FILECOIN _ARCHIVE : < SidebarFilecoinArchive / > ,
2020-08-14 12:17:19 +03:00
SIDEBAR _FILE _STORAGE _DEAL : < SidebarFileStorageDeal / > ,
SIDEBAR _WALLET _SEND _FUNDS : < SidebarWalletSendFunds / > ,
SIDEBAR _CREATE _WALLET _ADDRESS : < SidebarCreateWalletAddress / > ,
SIDEBAR _ADD _FILE _TO _BUCKET : < SidebarAddFileToBucket / > ,
SIDEBAR _CREATE _SLATE : < SidebarCreateSlate / > ,
SIDEBAR _DRAG _DROP _NOTICE : < SidebarDragDropNotice / > ,
SIDEBAR _SINGLE _SLATE _SETTINGS : < SidebarSingleSlateSettings / > ,
} ;
const SCENES = {
HOME : < SceneHome / > ,
2020-08-25 00:15:51 +03:00
DIRECTORY : < SceneDirectory / > ,
PROFILE : < SceneProfile / > ,
VIEW _SLATE : < SceneSlate / > ,
2020-08-14 12:17:19 +03:00
WALLET : < SceneWallet / > ,
FOLDER : < SceneFilesFolder / > ,
FILE : < SceneFile / > ,
SLATE : < SceneSlate / > ,
DEALS : < SceneDeals / > ,
SETTINGS : < SceneSettings / > ,
SETTINGS _DEVELOPER : < SceneSettingsDeveloper / > ,
EDIT _ACCOUNT : < SceneEditAccount / > ,
SLATES : < SceneSlates / > ,
LOCAL _DATA : < SceneLocalData / > ,
2020-08-18 22:43:41 +03:00
NETWORK : < SceneActivity / > ,
2020-08-18 22:41:41 +03:00
DIRECTORY : < SceneDirectory / > ,
2020-08-14 12:17:19 +03:00
} ;
2020-07-29 19:07:13 +03:00
export default class ApplicationPage extends React . Component {
2020-08-25 00:15:51 +03:00
_body ;
2020-07-29 19:07:13 +03:00
state = {
2020-08-25 09:46:02 +03:00
selected : { } ,
viewer : this . props . viewer ,
2020-08-18 22:28:33 +03:00
history : [ { id : "V1_NAVIGATION_HOME" , scrollTop : 0 , data : null } ] ,
2020-07-29 19:07:13 +03:00
currentIndex : 0 ,
data : null ,
sidebar : null ,
sidebarLoading : false ,
2020-08-03 00:09:55 +03:00
online : null ,
2020-07-29 19:07:13 +03:00
} ;
async componentDidMount ( ) {
window . addEventListener ( "dragenter" , this . _handleDragEnter ) ;
window . addEventListener ( "dragleave" , this . _handleDragLeave ) ;
window . addEventListener ( "dragover" , this . _handleDragOver ) ;
window . addEventListener ( "drop" , this . _handleDrop ) ;
2020-08-03 00:09:55 +03:00
window . addEventListener ( "online" , this . _handleOnlineStatus ) ;
window . addEventListener ( "offline" , this . _handleOnlineStatus ) ;
2020-07-29 19:07:13 +03:00
}
componentWillUnmount ( ) {
window . removeEventListener ( "dragenter" , this . _handleDragEnter ) ;
window . removeEventListener ( "dragleave" , this . _handleDragLeave ) ;
window . removeEventListener ( "dragover" , this . _handleDragOver ) ;
window . removeEventListener ( "drop" , this . _handleDrop ) ;
}
2020-08-03 00:09:55 +03:00
_handleOnlineStatus = async ( ) => {
window . alert ( navigator . onLine ? "online" : "offline" ) ;
this . setState ( { online : navigator . onLine } ) ;
} ;
2020-08-20 08:20:18 +03:00
_handleUploadFile = async ( { file , slate } ) => {
2020-08-21 10:07:39 +03:00
return await FileUtilities . upload ( { file , slate , context : this } ) ;
2020-08-20 08:20:18 +03:00
} ;
_handleRegisterFileLoading = ( { fileLoading } ) => {
2020-08-16 01:38:09 +03:00
return this . setState ( {
fileLoading ,
} ) ;
} ;
2020-07-29 19:07:13 +03:00
_handleDragEnter = ( e ) => {
2020-08-22 12:32:40 +03:00
e . preventDefault ( ) ;
2020-08-02 10:55:55 +03:00
if ( this . state . sidebar ) {
return ;
}
2020-08-22 12:32:40 +03:00
// NOTE(jim): Only allow the sidebar to show with file drag and drop.
2020-08-25 09:46:02 +03:00
if (
e . dataTransfer . items &&
e . dataTransfer . items . length &&
e . dataTransfer . items [ 0 ] . kind !== "file"
) {
2020-08-22 12:32:40 +03:00
return ;
}
2020-08-02 10:55:55 +03:00
this . _handleAction ( {
type : "SIDEBAR" ,
value : "SIDEBAR_DRAG_DROP_NOTICE" ,
} ) ;
2020-07-29 19:07:13 +03:00
} ;
_handleDragLeave = ( e ) => {
2020-08-02 10:55:55 +03:00
e . preventDefault ( ) ;
2020-07-29 19:07:13 +03:00
} ;
_handleDragOver = ( e ) => {
e . preventDefault ( ) ;
} ;
_handleSidebarLoading = ( sidebarLoading ) => this . setState ( { sidebarLoading } ) ;
_handleDrop = async ( e ) => {
2020-08-22 12:32:40 +03:00
// NOTE(jim): If this is true, then drag and drop came from a slate object.
const data = e . dataTransfer . getData ( "slate-object-drag-data" ) ;
if ( data ) {
return ;
}
2020-07-29 19:07:13 +03:00
e . preventDefault ( ) ;
this . setState ( { fileLoading : true } ) ;
2020-07-30 13:22:16 +03:00
// TODO(jim):
// Refactor later
const navigation = NavigationData . generate ( this . state . viewer ) ;
const next = this . state . history [ this . state . currentIndex ] ;
const current = NavigationData . getCurrentById ( navigation , next . id ) ;
let slate ;
if ( current . target && current . target . slatename ) {
slate = { ... current . target , id : current . target . slateId } ;
}
2020-08-16 01:38:09 +03:00
const files = [ ] ;
let fileLoading = { } ;
2020-08-22 12:32:40 +03:00
if ( e . dataTransfer . items && e . dataTransfer . items . length ) {
2020-07-29 19:07:13 +03:00
for ( var i = 0 ; i < e . dataTransfer . items . length ; i ++ ) {
if ( e . dataTransfer . items [ i ] . kind === "file" ) {
var file = e . dataTransfer . items [ i ] . getAsFile ( ) ;
2020-07-30 13:22:16 +03:00
2020-08-02 09:00:04 +03:00
if ( Validations . isFileTypeAllowed ( file . type ) ) {
2020-08-02 10:29:24 +03:00
this . _handleAction ( {
type : "SIDEBAR" ,
value : "SIDEBAR_ADD_FILE_TO_BUCKET" ,
data : slate ,
} ) ;
2020-08-16 01:38:09 +03:00
files . push ( file ) ;
fileLoading [ ` ${ file . lastModified } - ${ file . name } ` ] = {
name : file . name ,
loaded : 0 ,
total : file . size ,
} ;
2020-08-02 09:00:04 +03:00
}
2020-07-29 19:07:13 +03:00
}
}
}
2020-08-16 01:38:09 +03:00
if ( ! files . length ) {
alert ( "TODO: Files not supported error" ) ;
2020-08-22 10:09:23 +03:00
this . _handleRegisterFileLoading ( { fileLoading : null } ) ;
2020-08-15 05:08:17 +03:00
return ;
}
2020-08-16 01:38:09 +03:00
// NOTE(jim): Stages each file.
2020-08-20 08:20:18 +03:00
this . _handleRegisterFileLoading ( { fileLoading } ) ;
2020-08-16 01:38:09 +03:00
// NOTE(jim): Uploads each file.
for ( let i = 0 ; i < files . length ; i ++ ) {
await this . _handleUploadFile ( { file : files [ i ] , slate } ) ;
}
// NOTE(jim): Rehydrates user.
await this . rehydrate ( { resetFiles : true } ) ;
2020-07-29 19:07:13 +03:00
} ;
2020-08-15 05:08:17 +03:00
rehydrate = async ( options ) => {
2020-07-29 19:07:13 +03:00
const response = await Actions . hydrateAuthenticatedUser ( ) ;
if ( ! response || response . error ) {
2020-08-24 08:59:37 +03:00
alert ( "TODO: error fetching authenticated viewer" ) ;
2020-07-29 19:07:13 +03:00
return null ;
}
2020-08-24 08:59:37 +03:00
console . log ( "REHYDRATION CALL" , response ) ;
2020-08-15 05:08:17 +03:00
const updates = {
2020-08-26 06:13:06 +03:00
viewer : response . data ,
2020-08-15 05:08:17 +03:00
} ;
if ( options && options . resetFiles ) {
updates . fileLoading = null ;
2020-08-15 05:18:55 +03:00
updates . sidebar = null ;
2020-08-15 05:08:17 +03:00
}
this . setState ( updates ) ;
2020-07-29 19:07:13 +03:00
return { rehydrated : true } ;
} ;
_handleSubmit = async ( data ) => {
let response ;
2020-08-09 11:08:46 +03:00
2020-07-29 19:07:13 +03:00
if ( data . type === "CREATE_SLATE" ) {
response = await Actions . createSlate ( {
name : data . name ,
} ) ;
}
if ( data . type === "CREATE_WALLET_ADDRESS" ) {
response = await Actions . updateViewer ( {
type : "CREATE_FILECOIN_ADDRESS" ,
address : {
name : data . name ,
type : data . wallet _type ,
makeDefault : data . makeDefault ,
} ,
} ) ;
}
if ( data . type === "SEND_WALLET_ADDRESS_FILECOIN" ) {
response = await Actions . sendFilecoin ( {
source : data . source ,
target : data . target ,
amount : data . amount ,
} ) ;
}
console . log ( { response } ) ;
await this . rehydrate ( ) ;
this . _handleDismissSidebar ( ) ;
return response ;
} ;
_handleCancel = ( ) => {
this . _handleDismissSidebar ( ) ;
} ;
_handleDeleteYourself = async ( ) => {
// TODO(jim):
// Put this somewhere better for messages.
2020-08-25 09:46:02 +03:00
const message =
"Do you really want to delete your account? It will be permanently removed" ;
2020-07-29 19:07:13 +03:00
if ( ! window . confirm ( message ) ) {
return false ;
}
let response = await Actions . deleteViewer ( ) ;
console . log ( "DELETE_VIEWER" , response ) ;
await this . _handleSignOut ( ) ;
} ;
_handleAuthenticate = async ( state ) => {
// NOTE(jim): Kills existing session cookie if there is one.
const jwt = cookies . get ( Credentials . session . key ) ;
if ( jwt ) {
cookies . remove ( Credentials . session . key ) ;
}
// NOTE(jim): Acts as our existing username exists check.
// If the user exists, move on the sign in anyways.
let response = await Actions . createUser ( state ) ;
console . log ( "CREATE_USER" , response ) ;
response = await Actions . signIn ( state ) ;
if ( response . error ) {
console . log ( "SIGN IN ERROR" , response ) ;
return null ;
}
if ( response . token ) {
// NOTE(jim):
// + One week.
// + Only requests to the same site.
// + Not using sessionStorage so the cookie doesn't leave when the browser dies.
cookies . set ( Credentials . session . key , response . token , true , {
path : "/" ,
maxAge : 3600 * 24 * 7 ,
sameSite : "strict" ,
} ) ;
}
return await this . rehydrate ( ) ;
} ;
_handleSignOut = ( ) => {
const jwt = cookies . get ( Credentials . session . key ) ;
if ( jwt ) {
cookies . remove ( Credentials . session . key ) ;
window . location . reload ( ) ;
}
} ;
_handleViewerChange = ( e ) => {
this . setState ( {
viewer : { ... this . state . viewer , [ e . target . name ] : e . target . value } ,
} ) ;
} ;
_handleSelectedChange = ( e ) => {
this . setState ( {
selected : { ... this . state . selected , [ e . target . name ] : e . target . value } ,
} ) ;
} ;
_handleDismissSidebar = ( ) => {
2020-08-25 00:15:51 +03:00
this . setState ( { sidebar : null , sidebarLoading : false } ) ;
2020-07-29 19:07:13 +03:00
} ;
_handleAction = ( options ) => {
console . log ( options ) ;
if ( options . type === "NAVIGATE" ) {
2020-08-25 00:15:51 +03:00
return this . _handleNavigateTo (
{ id : options . value , scene : options . scene } ,
options . data
) ;
2020-07-29 19:07:13 +03:00
}
if ( options . type === "NEW_WINDOW" ) {
return window . open ( options . value ) ;
}
if ( options . type === "ACTION" ) {
return alert ( JSON . stringify ( options ) ) ;
}
if ( options . type === "DOWNLOAD" ) {
return alert ( JSON . stringify ( options ) ) ;
}
if ( options . type === "SIDEBAR" ) {
return this . setState ( {
2020-08-14 12:17:19 +03:00
sidebar : SIDEBARS [ options . value ] ,
2020-07-29 19:07:13 +03:00
data : options . data ,
} ) ;
}
return alert ( JSON . stringify ( options ) ) ;
} ;
_handleNavigateTo = ( next , data = null ) => {
2020-08-25 00:15:51 +03:00
let body = document . getElementById ( "slate-client-body" ) ;
this . state . history [ this . state . currentIndex ] . scrollTop = body . scrollTop ; //window.scrollY => body.scrollTop (where body is the body of the ApplicationLayout)
this . state . history [ this . state . currentIndex ] . data = this . state . data ; //BUG FIX: was originally = data. So it was setting it equal to the data for the next one rather than the current one
2020-07-29 19:07:13 +03:00
if ( this . state . currentIndex !== this . state . history . length - 1 ) {
const adjustedArray = [ ... this . state . history ] ;
adjustedArray . length = this . state . currentIndex + 1 ;
return this . setState (
{
history : [ ... adjustedArray , next ] ,
currentIndex : this . state . currentIndex + 1 ,
data ,
sidebar : null ,
} ,
2020-08-25 00:15:51 +03:00
( ) => body . scrollTo ( 0 , 0 )
2020-07-29 19:07:13 +03:00
) ;
}
this . setState (
{
history : [ ... this . state . history , next ] ,
currentIndex : this . state . currentIndex + 1 ,
data ,
sidebar : null ,
} ,
2020-08-25 00:15:51 +03:00
( ) => body . scrollTo ( 0 , 0 )
2020-07-29 19:07:13 +03:00
) ;
} ;
_handleBack = ( ) => {
2020-08-25 00:15:51 +03:00
let body = document . getElementById ( "slate-client-body" ) ;
this . state . history [ this . state . currentIndex ] . scrollTop = body . scrollTop ;
this . state . history [ this . state . currentIndex ] . data = this . state . data ; //BUG FIX: if you go back, it doesn't save the data for that page. so if you go forward to it again, it breaks. changed data => this.state.data
2020-07-29 19:07:13 +03:00
const next = this . state . history [ this . state . currentIndex - 1 ] ;
this . setState (
{
currentIndex : this . state . currentIndex - 1 ,
sidebar : null ,
data : { ... next . data } ,
} ,
( ) => {
console . log ( { next } ) ;
2020-08-25 00:15:51 +03:00
body . scrollTo ( 0 , next . scrollTop ) ;
2020-07-29 19:07:13 +03:00
}
) ;
} ;
_handleForward = ( ) => {
2020-08-25 00:15:51 +03:00
let body = document . getElementById ( "slate-client-body" ) ;
this . state . history [ this . state . currentIndex ] . scrollTop = body . scrollTop ;
2020-07-29 19:07:13 +03:00
const next = this . state . history [ this . state . currentIndex + 1 ] ;
this . setState (
{
currentIndex : this . state . currentIndex + 1 ,
sidebar : null ,
data : { ... next . data } ,
} ,
( ) => {
console . log ( { next } ) ;
2020-08-25 00:15:51 +03:00
body . scrollTo ( 0 , next . scrollTop ) ;
2020-07-29 19:07:13 +03:00
}
) ;
} ;
render ( ) {
2020-08-26 01:24:01 +03:00
console . log ( { viewer : this . state . viewer } ) ;
2020-07-29 19:07:13 +03:00
// NOTE(jim): Not authenticated.
if ( ! this . state . viewer ) {
return (
< WebsitePrototypeWrapper
title = "Slate: sign in"
description = "Sign in to your Slate account to manage your assets."
2020-08-25 09:46:02 +03:00
url = "https://slate.host/_"
>
< SceneSignIn
onAuthenticate = { this . _handleAuthenticate }
onNavigateTo = { this . _handleNavigateTo }
/ >
2020-07-29 19:07:13 +03:00
< / W e b s i t e P r o t o t y p e W r a p p e r >
) ;
}
// NOTE(jim): Authenticated.
const navigation = NavigationData . generate ( this . state . viewer ) ;
const next = this . state . history [ this . state . currentIndex ] ;
const current = NavigationData . getCurrentById ( navigation , next . id ) ;
2020-08-25 00:15:51 +03:00
console . log ( this . state . history ) ;
2020-07-29 19:07:13 +03:00
const navigationElement = (
< ApplicationNavigation
viewer = { this . state . viewer }
activeId = { current . target . id }
activeIds = { current . activeIds }
navigation = { navigation }
onNavigateTo = { this . _handleNavigateTo }
onAction = { this . _handleAction }
2020-08-08 03:10:28 +03:00
onSignOut = { this . _handleSignOut }
2020-07-29 19:07:13 +03:00
/ >
) ;
2020-08-10 13:18:43 +03:00
let headerElement = (
2020-07-29 19:07:13 +03:00
< ApplicationHeader
viewer = { this . state . viewer }
pageTitle = { current . target . pageTitle }
currentIndex = { this . state . currentIndex }
2020-08-20 08:45:43 +03:00
history = { this . state . history }
2020-08-25 02:24:29 +03:00
onAction = { this . _handleAction }
2020-07-29 19:07:13 +03:00
onBack = { this . _handleBack }
onForward = { this . _handleForward }
/ >
) ;
2020-08-25 00:15:51 +03:00
const scene = React . cloneElement (
SCENES [
this . state . history [ this . state . currentIndex ] . scene ||
current . target . decorator
] ,
{
current : current . target ,
data : this . state . data ,
viewer : this . state . viewer ,
selected : this . state . selected ,
onNavigateTo : this . _handleNavigateTo ,
onSelectedChange : this . _handleSelectedChange ,
onViewerChange : this . _handleViewerChange ,
onDeleteYourself : this . _handleDeleteYourself ,
onAction : this . _handleAction ,
onBack : this . _handleBack ,
onForward : this . _handleForward ,
onRehydrate : this . rehydrate ,
sceneId : current . target . id ,
}
) ;
2020-07-29 19:07:13 +03:00
let sidebarElement ;
if ( this . state . sidebar ) {
sidebarElement = React . cloneElement ( this . state . sidebar , {
selected : this . state . selected ,
viewer : this . state . viewer ,
data : this . state . data ,
fileLoading : this . state . fileLoading ,
sidebarLoading : this . state . sidebarLoading ,
onSelectedChange : this . _handleSelectedChange ,
onSubmit : this . _handleSubmit ,
onCancel : this . _handleCancel ,
2020-08-20 08:20:18 +03:00
onRegisterFileLoading : this . _handleRegisterFileLoading ,
2020-08-16 01:38:09 +03:00
onUploadFile : this . _handleUploadFile ,
2020-07-29 19:07:13 +03:00
onSidebarLoading : this . _handleSidebarLoading ,
2020-08-09 11:08:46 +03:00
onAction : this . _handleAction ,
2020-07-29 19:07:13 +03:00
onRehydrate : this . rehydrate ,
} ) ;
}
const title = ` Slate : ${ current . target . pageTitle } ` ;
2020-08-20 08:45:43 +03:00
const description = "" ;
2020-08-07 02:06:54 +03:00
const url = "https://slate.host/_" ;
2020-07-29 19:07:13 +03:00
return (
< React . Fragment >
2020-08-25 09:46:02 +03:00
< WebsitePrototypeWrapper
description = { description }
title = { title }
url = { url }
>
2020-07-29 19:07:13 +03:00
< ApplicationLayout
header = { headerElement }
2020-08-20 08:45:43 +03:00
navigation = { navigationElement }
2020-07-29 19:07:13 +03:00
sidebar = { sidebarElement }
2020-08-25 09:46:02 +03:00
onDismissSidebar = { this . _handleDismissSidebar }
>
2020-07-29 19:07:13 +03:00
{ scene }
< / A p p l i c a t i o n L a y o u t >
2020-08-09 11:38:16 +03:00
< System . GlobalCarousel / >
2020-08-25 02:24:29 +03:00
< System . GlobalModal / >
2020-07-29 19:07:13 +03:00
< / W e b s i t e P r o t o t y p e W r a p p e r >
< / R e a c t . F r a g m e n t >
) ;
}
}