Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pinning services stage 1 #1685

Merged
merged 27 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
aaf0a8f
Feat/pinning settings (#1535)
rafaelramalho19 Jul 15, 2020
327df82
Feat/pinning settings custom modal (#1546)
rafaelramalho19 Jul 17, 2020
5fcb1ba
Feat/redesign files bar (#1513)
rafaelramalho19 Jul 22, 2020
11b9c69
Feat/pinning settings (#1535) (#1557)
rafaelramalho19 Aug 17, 2020
9cbf791
chore: fix tslint in directory selector
rafaelramalho19 Sep 29, 2020
1296778
chore: fix tscheck issues in upper directory selector
rafaelramalho19 Sep 29, 2020
14d26f0
chore: fix tslint in selectors
rafaelramalho19 Sep 29, 2020
3e48dc2
feat: add pinning to files page (#1678)
rafaelramalho19 Nov 3, 2020
820e234
feat: prepare pinning services for stage 1
rafaelramalho19 Nov 3, 2020
aeb9d0b
chore: remove remote pins from settings page for stage 1
rafaelramalho19 Nov 4, 2020
92966af
chore: update pinning manager padding
rafaelramalho19 Nov 5, 2020
685b257
chore: update modal horizontal padding
rafaelramalho19 Nov 5, 2020
b2e3c94
chore: make settings page pinning table responsive
rafaelramalho19 Nov 5, 2020
dc0d642
chore: update settings page description in pinning table
rafaelramalho19 Nov 5, 2020
9fa7b96
chore: update header style
rafaelramalho19 Nov 6, 2020
e188bfd
chore: update settings page in smaller viewports
rafaelramalho19 Nov 6, 2020
7591194
chore: remove outline on pinning tables focus
rafaelramalho19 Nov 6, 2020
747dd6c
chore: update translation
rafaelramalho19 Nov 9, 2020
0836a6f
chore: update translation
rafaelramalho19 Nov 9, 2020
29fccf4
chore: add titles to bar options
rafaelramalho19 Nov 9, 2020
12c9313
chore: add titles to bar options
rafaelramalho19 Nov 9, 2020
90f5bf1
Update public/locales/en/files.json
jessicaschilling Nov 9, 2020
02a0ead
feat: add pins size to the settings page
rafaelramalho19 Nov 9, 2020
6860cf0
feat: add number of pins to settings page
rafaelramalho19 Nov 9, 2020
3f5c51b
chore: feat tslint
rafaelramalho19 Nov 9, 2020
e59f689
chore: fix error in button
rafaelramalho19 Nov 16, 2020
7089d93
test(e2e): more reliable api test suite
lidel Dec 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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