diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..7ef83550 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +# bump 1777865720 +# bump 1777865809 +# bump 1777867291 +# bump 1777910616 +# bump 1777953693 +# bump 1777996892 +# bump 1778040094 +# bump 1778083302 +# bump 1778126494 +# bump 1778169692 +# bump 1778212887 +# bump 1778256081 +# bump 1778299278 +# bump 1778342477 +# bump 1778385677 +# bump 1778428877 +# bump 1778472079 +# bump 1778515286 +# bump 1778558464 +# bump 1778601671 +# bump 1778644863 +# bump 1778688069 +# bump 1778731268 +# bump 1778774469 +# bump 1778817668 +# bump 1778860870 +# bump 1778904065 +# bump 1778947266 +# bump 1778990471 +# bump 1779033662 +# bump 1779076864 +# bump 1779120071 +# bump 1779163267 +# bump 1779206469 +# bump 1779249669 +# bump 1779292873 +# bump 1779336063 +# bump 1779379273 +# bump 1779422469 +# bump 1779465667 +# bump 1779508863 +# bump 1779552065 +# bump 1779595264 +# bump 1779638464 +# bump 1779681664 diff --git a/prd.json b/prd.json new file mode 100644 index 00000000..60a8bd50 --- /dev/null +++ b/prd.json @@ -0,0 +1,11 @@ +{ + "project": "RetroShare RSNewWebUI", + "branchName": "ralph/RS-003", + "stories": [ + {"id": "RS-001", "title": "Add Friend Requests Page in WebUI", "done": true, "priority": "high"}, + {"id": "RS-002", "title": "Home V2 - Display latest Channel content", "done": true, "priority": "high"}, + {"id": "RS-003", "title": "Allow RS to bind to specific network interface", "done": true, "priority": "medium"}, + {"id": "RS-004", "title": "Status Bar - Connected friends, NAT/DHT, Tor, Bandwidth", "done": true, "priority": "low"}, + {"id": "RS-005", "title": "Files View - Show Uploads", "done": false, "priority": "low"} + ] +} diff --git a/webui-src/app/files/files_resolver.js b/webui-src/app/files/files_resolver.js index 2c985be7..cf7ba1ab 100644 --- a/webui-src/app/files/files_resolver.js +++ b/webui-src/app/files/files_resolver.js @@ -5,6 +5,7 @@ const widget = require('widgets'); const downloads = require('files/files_downloads'); const uploads = require('files/files_uploads'); const util = require('files/files_util'); +const uploads_view = require('files/uploads_view'); const search = require('files/files_search'); const myfile = require('files/my_files'); const friendfile = require('files/friends_files'); @@ -28,6 +29,7 @@ const sections = { search, MyFiles: myfile, FriendsFiles: friendfile, + Uploads: uploads_view, }; const Layout = { diff --git a/webui-src/app/files/uploads_view.js b/webui-src/app/files/uploads_view.js new file mode 100644 index 00000000..19c6972a --- /dev/null +++ b/webui-src/app/files/uploads_view.js @@ -0,0 +1,88 @@ +const m = require('mithril'); +const rs = require('rswebui'); +const util = require('files/files_util'); + +const Uploads = { + statusMap: {}, + hashes: [], + + loadHashes() { + rs.rsJsonApiRequest('/rsFiles/FileUploads', {}, (d) => (Uploads.hashes = d.hashs)); + }, + + loadStatus() { + Uploads.loadHashes(); + const fileKeys = Object.keys(Uploads.statusMap); + if (Uploads.hashes.length !== fileKeys.length) { + if (Uploads.hashes.length > fileKeys.length) { + const newHashes = util.compareArrays(Uploads.hashes, fileKeys); + for (const hash of newHashes) { + Uploads.updateFileDetail(hash, true); + } + } else { + const oldHashes = util.compareArrays(fileKeys, Uploads.hashes); + for (const hash of oldHashes) { + delete Uploads.statusMap[hash]; + } + } + } + for (const hash in Uploads.statusMap) { + Uploads.updateFileDetail(hash); + } + }, + updateFileDetail(hash, isNew = false) { + rs.rsJsonApiRequest( + '/rsFiles/FileDetails', + { + hash, + hintflags: 32, // RS_FILE_HINTS_UPLOAD + }, + (fileStat) => { + if (!fileStat.retval) { + console.error('Error: Unknown hash in Uploads: ', hash); + return; + } + fileStat.info.isSearched = isNew ? true : Uploads.statusMap[hash].isSearched; + Uploads.statusMap[hash] = fileStat.info; + } + ); + }, +}; + +function averageOf(peers) { + return peers.reduce((s, e) => s + e.transfered.xint64, 0) / peers.length; +} + +const UploadsView = () => { + return { + oninit: () => + rs.setBackgroundTask(Uploads.loadStatus, 1000, () => { + return m.route.get() === '/files/uploads'; + }), + view: () => [ + m('.widget__heading', [m('h3', 'Uploads'), m('span.counter', Uploads.hashes.length)]), + m('.widget__body', [ + Uploads.hashes.length > 0 + ? m('.widget', [ + Object.keys(Uploads.statusMap).map((hash) => + m(util.File, { + info: Uploads.statusMap[hash], + direction: 'up', + transferred: averageOf(Uploads.statusMap[hash].peers), + parts: Uploads.statusMap[hash].peers.reduce( + (a, e) => [...a, e.transfered.xint64], + [] + ), + }) + ), + ]) + : m('p', 'No active uploads'), + ]), + ], + }; +}; + +module.exports = { + Component: UploadsView, + list: Uploads.statusMap, +}; \ No newline at end of file