Skip to content

Commit

Permalink
feat: pinning services stage 1 (#1685)
Browse files Browse the repository at this point in the history
* Feat/pinning settings (#1535)

* Feat/pinning settings custom modal (#1546)

Co-authored-by: Jessica Schilling <[email protected]>

* Feat/redesign files bar (#1513)

* Feat/pinning settings (#1535) (#1557)

* feat: add pinning services service modal

* chore: update translation for service modal

* chore: fix tslint in directory selector

* chore: fix tscheck issues in upper directory selector

* chore: fix tslint in selectors

* feat: add pinning to files page (#1678)

* chore: refactor files page

* feat: add pinning services mock to files page

* chore: update remotePin dimensions

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update local pin icon dimension

Co-authored-by: Jessica Schilling <[email protected]>

* chore: change pin icon fill color

Co-authored-by: Jessica Schilling <[email protected]>

* chore: change remote pin icon fill color

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update pinning modal text size

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update pinning modal text size

Co-authored-by: Jessica Schilling <[email protected]>

* chore: pinning modal secondary text changes

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update pinning modal image size

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update pinning modal pin icon

Co-authored-by: Jessica Schilling <[email protected]>

* chore: add pin status column to fileslist

* chore: fix modals icons

* chore: fix linting error

* chore: fix files sorting

Co-authored-by: Jessica Schilling <[email protected]>

* feat: prepare pinning services for stage 1

* chore: remove remote pins from settings page for stage 1

* chore: update pinning manager padding

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update modal horizontal padding

Co-authored-by: Jessica Schilling <[email protected]>

* chore: make settings page pinning table responsive

* chore: update settings page description in pinning table

* chore: update header style

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update settings page in smaller viewports

* chore: remove outline on pinning tables focus

* chore: update translation

Co-authored-by: Jessica Schilling <[email protected]>

* chore: update translation

Co-authored-by: Jessica Schilling <[email protected]>

* chore: add titles to bar options

* chore: add titles to bar options

* Update public/locales/en/files.json

* feat: add pins size to the settings page

* feat: add number of pins to settings page

* chore: feat tslint

* chore: fix error in button

* test(e2e): more reliable api test suite

This changes the way we enter API address/config from programmatic
to full simulation of user input and adds tiny slow down between each
key stroke.

This should solve the problem of newly added UI feedback not engaging,
and make CI both more reliable and green again.

While at it, made it CI-agnostic, in preparation for move to
GithubActions

Co-authored-by: Jessica Schilling <[email protected]>
Co-authored-by: Marcin Rataj <[email protected]>
  • Loading branch information
3 people authored Dec 14, 2020
1 parent 80dd9f3 commit 2a312a3
Show file tree
Hide file tree
Showing 66 changed files with 1,672 additions and 630 deletions.
2 changes: 2 additions & 0 deletions .storybook/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { configure } from '@storybook/react'
import 'react-virtualized/styles.css'
import '../src/index.css'


const req = require.context('../src', true, /\.stories\.js$/)

function loadStories () {
Expand Down
488 changes: 259 additions & 229 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"react-dom": "^16.13.1",
"react-faux-dom": "^4.5.0",
"react-helmet": "^5.2.1",
"react-hook-form": "^6.0.6",
"react-i18next": "^11.7.0",
"react-identicons": "^1.2.4",
"react-joyride": "^2.1.1",
Expand Down
13 changes: 8 additions & 5 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@
<noscript>
You need to enable JavaScript to run this app.
</noscript>


<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<div id="portal-dropdown" class="fixed top-0 left-0"></div>
</body>
</html>
4 changes: 4 additions & 0 deletions public/locales/en/app.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"actions": {
"add": "Add",
"apply": "Apply",
"browse": "Browse",
"cancel": "Cancel",
"change": "Change",
Expand All @@ -23,6 +24,7 @@
"save": "Save",
"saving": "Saving…",
"selectAll": "Select all",
"setPinning": "Set pinning",
"submit": "Submit",
"unpin": "Unpin",
"unselectAll": "Unselect all"
Expand Down Expand Up @@ -59,6 +61,7 @@
"gateway": "Gateway",
"in": "In",
"latency": "Latency",
"loading": "Loading",
"location": "Location",
"name": "Name",
"node": "Node",
Expand All @@ -68,6 +71,7 @@
"peers": "Peers",
"pinNoun": "Pin",
"pins": "Pins",
"pinStatus": "Pin Status",
"publicKey": "Public key",
"rateIn": "Rate in",
"rateOut": "Rate out",
Expand Down
12 changes: 12 additions & 0 deletions public/locales/en/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
"descriptionFolder": "{count, plural, one {Are you sure you want to delete this folder? This action is permanent and cannot be reversed.} other {Are you sure you want to delete these {count} folders? This action is permanent and cannot be reversed.}}",
"descriptionFile": "{count, plural, one {Are you sure you want to delete this file? This action is permanent and cannot be reversed.} other {Are you sure you want to delete these {count} files? This action is permanent and cannot be reversed.}}"
},
"pinningModal": {
"title": "Select where you would like to pin these items.",
"footer": "Need to add or configure a pinning service? Go to <1>Settings</1>.",
"localNode": "Local node",
"totalSize": "Total size: {size}"
},
"addByPathModal": {
"title": "Import from IPFS",
"description": "Insert an IPFS path (CID) to import.",
Expand Down Expand Up @@ -91,6 +97,12 @@
"hashUnavailable": "hash unavailable",
"checkboxLabel": "View more options for {name}",
"pinned": "Pinned",
"blocks": "Blocks",
"allBlocks": "All blocks",
"allBlocksDescription": "Total size of blocks on your entire IPFS node; this includes everything in Files, plus all locally pinned items and any temporary cached data",
"filesDescription": "Total size of data in the current directory (if a subdirectory, the size of all data in Files is also displayed)",
"more": "More",
"files": "Files",
"cidNotFileNorDir": "The current link isn't a file, nor a directory. Try to <0>inspect</0> it instead.",
"sortBy": "Sort items by {name}"
}
47 changes: 47 additions & 0 deletions public/locales/en/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
{
"title": "Settings",
"save": "Save",
"saving": "Saving…",
"reset": "Reset",
"pinningServices": {
"title": "Pinning Services",
"description": "Support for third-party remote pinning services will be implemented in a future release, and you'll be able to add and configure them here. In the meantime, use local pinning when you want to ensure an item on your node is never garbage-collected, even if you remove it from Files."
},
"language": "Language",
"analytics": "Analytics",
"cliTutorMode": "CLI Tutor Mode",
Expand All @@ -12,6 +19,46 @@
},
"apiDescription": "<0>If your node is configured with a <1>custom API address</1>, including a port other than the default 5001, enter it here.</0>",
"cliDescription": "<0>Enable this option to display a \"view code\" <1></1> icon next to common IPFS commands. Clicking it opens a modal with that command's CLI code, so you can paste it into the IPFS command-line interface in your terminal.</0>",
"pinningModal": {
"title": "Select a pinning service provider.",
"description": "Don’t see your pinning service provider? <1>Add a custom one.<1>"
},
"pinningServiceModal": {
"title": "Configure a custom pinning service.",
"description": "Want to make your custom pinning service available to others? <1>Learn how.<1>",
"service": "Service",
"nickname": "Nickname",
"nicknamePlaceholder": "Name for your service",
"apiEndpoint": "API endpoint",
"apiEndpointPlaceholder": "URL for its API endpoint",
"secretApiKey": "Secret API key",
"autoUpload": "Auto upload"
},
"errors": {
"nickname": "Nickname is required",
"apiEndpoint": "Must be a valid URL",
"secretApiKey": "Secret key is required"
},
"actions": {
"addService": "Add Service",
"edit": "Change",
"close": "Close",
"save": "Save",
"cancel": "Cancel"
},
"edit": "Edit",
"visitService": "Visit service",
"remove": "Remove",
"localPinning": "Local Pinning",
"service": "Service",
"size": "Size",
"pins": "Pins",
"autoUpload": "Auto Upload",
"autoUploadKeys": {
"ALL_FILES": "All files",
"DISABLED": "Disabled",
"PINS_ONLY": "Pins only"
},
"fetchingSettings": "Fetching settings...",
"configApiNotAvailable": "The IPFS config API is not available. Please disable the \"IPFS Companion\" Web Extension and try again.",
"ipfsDaemonOffline": "The IPFS daemon is offline. Please turn it on and try again.",
Expand Down
4 changes: 2 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ export class App extends Component {

return connectDropTarget(
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div className='sans-serif h-100' onClick={getNavHelper(this.props.doUpdateUrl)}>
<div className='sans-serif h-100 relative' onClick={getNavHelper(this.props.doUpdateUrl)}>
{/* Tinted overlay that appears when dragging and dropping an item */}
{ canDrop && isOver && <div className='w-100 h-100 top-0 left-0 absolute' style={{ background: 'rgba(99, 202, 210, 0.2)' }} /> }
{ canDrop && isOver && <div className='h-100 top-0 right-0 fixed appOverlay' style={{ background: 'rgba(99, 202, 210, 0.2)' }} /> }
<div className='flex flex-row-reverse-l flex-column-reverse justify-end justify-start-l' style={{ minHeight: '100vh' }}>
<div className='flex-auto-l'>
<div className='flex items-center ph3 ph4-l' style={{ WebkitAppRegion: 'drag', height: 75, background: '#F0F6FA', paddingTop: '20px', paddingBottom: '15px' }}>
Expand Down
67 changes: 53 additions & 14 deletions src/bundles/files/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ const getPins = async function * (ipfs) {

const actions = () => ({
/**
* Fetches list of pins and updates `state.pins` on succeful completion.
* Fetches list of pins and updates `state.pins` on successful completion.
* @returns {function(Context):Promise<{pins: CID[]}>}
*/
doPinsFetch: () => perform(ACTIONS.PIN_LIST, async (ipfs) => {
Expand Down Expand Up @@ -463,21 +463,28 @@ const actions = () => ({
doFilesDismissErrors: () => send({ type: ACTIONS.DISMISS_ERRORS }),

/**
* @param {string} path
*/
doFilesNavigateTo: (path) =>
* @param {Object} fileArgs
* @param {string} fileArgs.path
* @param {string|CID} fileArgs.cid
*/
doFilesNavigateTo: ({ path, cid }) =>
/**
* @param {Context} context
*/
async ({ store }) => {
const link = path.split('/').map(p => encodeURIComponent(p)).join('/')
const files = store.selectFiles()
const url = store.selectFilesPathInfo()

if (files && files.path === link && url) {
await store.doFilesFetch()
} else {
await store.doUpdateHash(link)
try {
const link = path.split('/').map(p => encodeURIComponent(p)).join('/')
const files = store.selectFiles()
const url = store.selectFilesPathInfo()

if (files && files.path === link && url) {
await store.doFilesFetch()
} else {
await store.doUpdateHash(link)
}
} catch (e) {
console.error(e)
store.doUpdateHash(`/ipfs/${cid}`)
}
},

Expand All @@ -496,13 +503,45 @@ const actions = () => ({
doFilesClear: () => send({ type: ACTIONS.CLEAR_ALL }),

/**
* Gets size of the MFS. On succesful completion `state.mfsSize` will get
* Gets total size of the local pins. On successful completion `state.mfsSize` will get
* updated.
*/
doPinsSizeGet: () => perform(ACTIONS.PINS_SIZE_GET, async (ipfs) => {
const allPinsCids = await ipfs.pin.ls({ type: 'recursive' })

let pinsSize = 0
let numberOfPins = 0

for await (const { cid } of allPinsCids) {
pinsSize += (await ipfs.files.stat(`/ipfs/${cid.toString()}`)).cumulativeSize
numberOfPins++
}

return { pinsSize, numberOfPins }
}),

/**
* Gets size of the MFS. On successful completion `state.mfsSize` will get
* updated.
*/
doFilesSizeGet: () => perform(ACTIONS.SIZE_GET, async (ipfs) => {
const stat = await ipfs.files.stat('/')
return { size: stat.cumulativeSize }
})
}),

/**
* @param {string|CID} cid
*/
doGetFileSizeThroughCid: (cid) =>
/**
* @param {Object} store
* @param {Function} store.getIpfs
*/
async (store) => {
const ipfs = store.getIpfs()
const stat = await ipfs.files.stat(`/ipfs/${cid}`)
return stat.cumulativeSize
}
})

export default actions
Expand Down
4 changes: 4 additions & 0 deletions src/bundles/files/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export const ACTIONS = {
PIN_LIST: ('FILES_PIN_LIST'),
/** @type {'FILES_SIZE_GET'} */
SIZE_GET: ('FILES_SIZE_GET'),
/** @type {'FILES_PINS_SIZE_GET'} */
PINS_SIZE_GET: ('FILES_PINS_SIZE_GET'),
/** @type {'FILES_DISMISS_ERRORS'} */
DISMISS_ERRORS: ('FILES_DISMISS_ERRORS'),
/** @type {'FILES_CLEAR_ALL'} */
Expand Down Expand Up @@ -58,6 +60,8 @@ export const IGNORED_FILES = [
export const DEFAULT_STATE = {
pageContent: null,
mfsSize: -1,
pinsSize: 0,
numberOfPins: 0,
pins: [],
sorting: { // TODO: cache this
by: SORTING.BY_NAME,
Expand Down
19 changes: 18 additions & 1 deletion src/bundles/files/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,29 @@ export default () => {
pageContent: {
...pageContent,
content
}
},
sorting: action.payload
}
} else {
return state
}
}
case ACTIONS.PINS_SIZE_GET: {
const { task, type } = action
const pinsSize = task.status === 'Exit' && task.result.ok
? task.result.value.pinsSize
: 0

const numberOfPins = task.status === 'Exit' && task.result.ok
? task.result.value.numberOfPins
: 0

return {
...updateJob(state, task, type),
pinsSize,
numberOfPins
}
}
case ACTIONS.SIZE_GET: {
const { task, type } = action
const mfsSize = task.status === 'Exit' && task.result.ok
Expand Down
5 changes: 4 additions & 1 deletion src/bundles/files/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export type Model = {
pins: string[]
sorting: Sorting
mfsSize: number
pinsSize: number
numberOfPins: number

pending: PendingJob<any, any>[]
finished: FinishedJob<any>[]
Expand Down Expand Up @@ -67,6 +69,7 @@ export type Message =
| Perform<'FILES_PIN_REMOVE', Error, Pin[], void>
| Perform<'FILES_PIN_LIST', Error, { pins: CID[] }, void>
| Perform<'FILES_SIZE_GET', Error, { size: number }, void>
| Perform<'FILES_PINS_SIZE_GET', Error, { pinsSize: number, numberOfPins: number }, void>

export type MakeDir = Perform<'FILES_MAKEDIR', Error, void, void>
export type WriteProgress = { paths: string[], progress: number }
Expand Down Expand Up @@ -119,7 +122,7 @@ type FileContent = {
pinned: boolean
}

type DirectoryContent = {
export type DirectoryContent = {
type: 'directory',
fetched: Time,
path: string,
Expand Down
22 changes: 22 additions & 0 deletions src/bundles/files/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ const selectors = () => ({
*/
selectFiles: (state) => state.files.pageContent,

/**
* @param {Model} state
*/
selectCurrentDirectorySize: (state) => {
return state.files.pageContent?.type === 'directory' && state.files.pageContent?.content?.reduce((prev, curr) => prev + curr.size, 0)
},

/**
* @param {Model} state
*/
Expand All @@ -28,6 +35,16 @@ const selectors = () => ({
*/
selectFilesSize: (state) => state.files.mfsSize,

/**
* @param {Model} state
*/
selectPinsSize: (state) => state.files.pinsSize,

/**
* @param {Model} state
*/
selectNumberOfPins: (state) => state.files.numberOfPins,

/**
* @param {Model} state
*/
Expand Down Expand Up @@ -68,6 +85,11 @@ const selectors = () => ({
*/
selectFilesErrors: (state) => state.files.failed,

/**
* @param {Model} state
*/
selectHasUpperDirectory: (state) => state.files.pageContent?.type === 'directory' && state.files.pageContent?.upper,

selectFilesPathInfo: createSelector(
'selectRouteInfo',
/**
Expand Down
Loading

0 comments on commit 2a312a3

Please sign in to comment.