add option to delete files

This commit is contained in:
2020-02-18 14:57:27 +01:00
parent 408e3f7e52
commit 449c69b12d
20 changed files with 524 additions and 212 deletions

View File

@@ -24,7 +24,12 @@ type FileInfo struct {
MimeType string `json:"mime_type"`
MimeImage string `json:"mime_image"`
ThumbnailHREF string `json:"thumbnail_href"`
Availability string `json:"availability"`
Availability string `json:"availability"`
AvailabilityMessage string `json:"availability_message"`
AvailabilityName string `json:"availability_name"`
CanEdit bool `json:"can_edit"`
}
// GetFileInfo gets the FileInfo from the pixeldrain API

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
<path fill="#{{.Style.InputTextColor.RGB}}" d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
</svg>

After

Width:  |  Height:  |  Size: 292 B

View File

@@ -0,0 +1,95 @@
let modal_global_index = 100
// Modal creates a new modal window and shows it. The width and height are will
// be adjusted to the screen size. Content should be added to the `body`
// property
function Modal(parent, closeCallback, title, width, height) {
this.parent = parent
this.closeCallback = closeCallback
this.title = title
this.width = width
this.height = height
this.visible = false
this.background = document.createElement("div")
this.background.classList = "modal_background"
this.background.addEventListener("click", e => { this.close() })
this.window = document.createElement("div")
this.window.classList = "modal_window"
this.window.style.width = this.width
this.window.style.height = this.height
this.window.addEventListener("click", e => { e.stopPropagation() })
this.header = document.createElement("div")
this.header.classList = "modal_header highlight_1"
this.titleDiv = document.createElement("div")
this.titleDiv.classList = "modal_title"
this.titleDiv.innerText = this.title
this.btnClose = document.createElement("button")
this.btnClose.classList = "modal_btn_close button_red"
this.btnClose.innerHTML = '<i class="icon small">close</i>'
this.btnClose.addEventListener("click", e => { this.close() })
this.body = document.createElement("div")
this.body.classList = "modal_body"
// And add all the elements to eachother.
this.header.append(this.titleDiv)
this.header.append(this.btnClose)
this.window.append(this.header)
this.window.append(this.body)
this.background.append(this.window)
}
Modal.prototype.setTitle = function(title) {
this.title = title
this.titleDiv.innerText = title
}
Modal.prototype.setBody = function(element) {
this.body.innerHTML = ""
this.body.append(element)
}
Modal.prototype.open = function() {
if (this.visible) { return }
this.visible = true
console.debug("Showing modal "+this.title)
// Each time a modal is shown it gets a z-index which is one higher of the
// previous one. This makes sure they are always shown and closed in order
this.background.style.zIndex = modal_global_index
modal_global_index++
this.parent.prepend(this.background)
// If an element is created and shown in the same frame it won't render the
// transition. So here we wait for a few frames to make it visible
this.background.style.display = ""
setTimeout(() => { this.background.style.opacity = 1 }, 40)
// This is a workaround for a chrome bug which makes it so hidden
// windows can't be scrolled after they are shown
this.body.focus()
}
Modal.prototype.close = function() {
if (!this.visible) { return }
this.visible = false
if (this.closeCallback) {
this.closeCallback()
}
// First we make it invisible with a transition. When we remove it so the
// user can click through it
this.background.style.opacity = 0
// Wait for the animation to finish and remove the window
setTimeout(() => {this.parent.removeChild(this.background)}, 400)
}

View File

@@ -53,7 +53,7 @@ function DirectoryElement(directoryArea, footer) {
// files in the directory and the last scroll position. These are used for
// rendering the file list correctly
// type: {icon, name, href, type, size, sizeLabel, dateCreated}
// type: {icon, name, href, type, size, sizeLabel, dateCreated, selected}
this.allFiles = []
// This array contains indexes referring to places in the allFiles array
@@ -77,6 +77,7 @@ DirectoryElement.prototype.addFile = function(icon, name, href, type, size, size
size: size,
sizeLabel: sizeLabel,
dateCreated: dateCreated,
selected: false,
})
}

View File

@@ -0,0 +1,99 @@
function DirectoryNode(file, index) {
this.el = document.createElement("div")
this.el.classList = "node"
if (file.selected) {
this.el.classList += " node_selected"
}
this.el.href = file.href
this.el.target = "_blank"
this.el.title = file.name
this.el.setAttribute("fileindex", index)
this.el.addEventListener("click", e => {
if (e.detail > 1) {
return // Prevent dblclick from triggering click
}
if (e.which == 2) {
// Middle mouse button opens the file in a new window
this.open(true)
return
}
this.select()
})
this.el.addEventListener("tap")
this.el.addEventListener("dblclick", e => {
this.open(false)
})
{
let cell = document.createElement("div")
let thumb = document.createElement("img")
thumb.src = file.icon
cell.appendChild(thumb)
let label = document.createElement("span")
label.innerText = file.name
cell.appendChild(label)
cell.appendChild(label)
this.el.appendChild(cell)
}
{
let cell = document.createElement("div")
cell.style.width = this.fieldDateWidth
let label = document.createElement("span")
label.innerText = printDate(new Date(file.dateCreated), true, true, false)
cell.appendChild(label)
this.el.appendChild(cell)
}
{
let cell = document.createElement("div")
cell.style.width = this.fieldSizeWidth
let label = document.createElement("span")
label.innerText = file.sizeLabel
cell.appendChild(label)
this.el.appendChild(cell)
}
{
let cell = document.createElement("div")
cell.style.width = this.fieldTypeWidth
let label = document.createElement("span")
label.innerText = file.type
cell.appendChild(label)
this.el.appendChild(cell)
}
return this.el
}
DirectoryNode.prototype.select = function() {
if (this.el.classList.contains("node_selected")) {
this.el.classList = "node"
file.selected = false
} else {
this.el.classList = "node node_selected"
file.selected = true
}
}
DirectoryNode.prototype.open = function(newTab) {
if (newTab) {
window.open(file.href, "_blank")
} else {
window.open(file.href)
}
}
DirectoryNode.prototype.click = function(e) {
if (e.detail > 1) {
return // Prevent dblclick from triggering click
}
if (e.which == 2) {
// Middle mouse button opens the file in a new window
e.preventDefault()
window.open(file.href, "_blank")
return
}
}
DirectoryNode.prototype.doubleClick = function() {
window.open(file.href)
}

View File

@@ -3,32 +3,30 @@ function DetailsWindow(viewer) {
this.visible = false
this.file = null
this.graph = 0
this.modal = new Modal(
document.getElementById("file_viewer"),
() => { this.toggle() },
"File Details", "1200px", "1000px",
)
this.divPopup = document.getElementById("details_popup")
this.btnDetails = document.getElementById("btn_details")
this.btnCloseDetails = document.getElementById("btn_close_details")
this.divFileDetails = document.getElementById("info_file_details")
let clone = document.getElementById("tpl_details_popup").content.cloneNode(true)
this.divFileDetails = clone.querySelector(".info_file_details")
this.modal.setBody(clone)
this.btnDetails.addEventListener("click", () => { this.toggle() })
this.btnCloseDetails.addEventListener("click", () => { this.toggle() })
this.btnDetails = document.getElementById("btn_details")
this.btnDetails.addEventListener("click", () => { this.toggle() })
}
DetailsWindow.prototype.toggle = function() {
if (this.visible) {
this.divPopup.style.opacity = "0"
this.divPopup.style.visibility = "hidden"
this.modal.close()
this.btnDetails.classList.remove("button_highlight")
this.visible = false
} else {
this.divPopup.style.opacity = "1"
this.divPopup.style.visibility = "visible"
this.modal.open()
this.btnDetails.classList.add("button_highlight")
this.visible = true
// This is a workaround for a chrome bug which makes it so hidden
// windows can't be scrolled after they are shown
this.divPopup.focus()
if (this.graph === 0) {
this.renderGraph()
}
@@ -42,8 +40,7 @@ DetailsWindow.prototype.setFile = function(file) {
if (this.viewer.isList) {
desc = file.description
}
this.divFileDetails.innerHTML = "<table>"
+ "<tr><td>Name<td><td>" + escapeHTML(file.name) + "</td></tr>"
this.divFileDetails.innerHTML = "<tr><td>Name<td><td>" + escapeHTML(file.name) + "</td></tr>"
+ "<tr><td>URL<td><td><a href=\""+file.link+"\">"+file.link+"</a></td></tr>"
+ "<tr><td>Mime Type<td><td>" + escapeHTML(file.mime_type) + "</td></tr>"
+ "<tr><td>ID<td><td>" + file.id + "</td></tr>"
@@ -51,7 +48,6 @@ DetailsWindow.prototype.setFile = function(file) {
+ "<tr><td>Bandwidth<td><td>" + formatDataVolume(file.bandwidth_used, 4) + "</td></tr>"
+ "<tr><td>Upload Date<td><td>" + printDate(file.date_created, true, true, true) + "</td></tr>"
+ "<tr><td>Description<td><td>" + escapeHTML(desc) + "</td></tr>"
+ "</table>"
if(this.visible && file.timeseries_href !== "") {
this.updateGraph(file)

View File

@@ -0,0 +1,52 @@
function EditWindow() {
this.visible = false
this.modal = new Modal(
document.getElementById("file_viewer"),
() => { this.toggle() },
"Edit File", "1000px", "auto",
)
let clone = document.getElementById("tpl_edit_file").content.cloneNode(true)
clone.querySelector(".btn_delete_file").addEventListener("click", () => { this.deleteFile() })
this.modal.setBody(clone)
this.btnEdit = document.getElementById("btn_edit")
this.btnEdit.addEventListener("click", () => { this.toggle() })
}
EditWindow.prototype.toggle = function() {
if (this.visible) {
this.modal.close()
this.btnEdit.classList.remove("button_highlight")
this.visible = false
} else if (!this.visible && this.file.can_edit) {
this.modal.open()
this.btnEdit.classList.add("button_highlight")
this.visible = true
}
}
EditWindow.prototype.setFile = function(file) {
this.file = file
this.modal.setTitle("Editing "+file.name)
if (this.file.can_edit) {
this.btnEdit.style.display = ""
} else {
this.btnEdit.style.display = "none"
}
}
EditWindow.prototype.deleteFile = function() {
if (!confirm("Are you sure you want to delete '"+this.file.name+"'?")) {
return
}
fetch(
this.file.get_href, {method: "DELETE"}
).then(resp => {
this.modal.setBody(document.createTextNode("This file has been deleted"))
}).catch(err => {
alert("Error! Could not delete file")
})
}

View File

@@ -1,8 +1,9 @@
function Toolbar(viewer) {
this.viewer = viewer
this.visible = false
this.sharebarVisible = false
this.currentFile = null
this.viewer = viewer
this.visible = false
this.sharebarVisible = false
this.currentFile = null
this.editWindow = null
this.divToolbar = document.getElementById("toolbar")
this.divFilePreview = document.getElementById("filepreview")
@@ -69,71 +70,67 @@ Toolbar.prototype.toggleSharebar = function() {
}
Toolbar.prototype.download = function() {
let triggerDL = (captchaResp = "") => {
if (captchaResp === "") {
this.downloadFrame.src = this.currentFile.download_href
} else {
this.downloadFrame.src = this.currentFile.download_href+"&recaptcha_response="+captchaResp
}
}
if (captchaKey === "none" || captchaKey === ""){
// If the server doesn't support captcha there's no use in checking
// availability
triggerDL()
return
}
if (recaptchaResponse !== "") {
// Captcha already filled in. Use the saved captcha responsse to
// download the file
triggerDL(recaptchaResponse)
// Reset the key
recaptchaResponse = ""
console.debug("Server doesn't support captcha, starting download")
this.downloadFrame.src = this.currentFile.download_href
return
}
fetch(this.currentFile.availability_href).then(resp => {
return resp.json()
}).then(resp => {
let popupDiv = document.getElementById("captcha_popup")
let popupTitle = document.getElementById("captcha_popup_title")
let popupContent = document.getElementById("captcha_popup_content")
if (this.currentFile.availability === "") {
console.debug("File is available, starting download")
this.downloadFrame.src = this.currentFile.download_href
} else {
console.debug("File is not readily available, showing captcha dialog")
let showCaptcha = (title, text) => {
// Create the modal
this.captchaModal = new Modal(
document.getElementById("file_viewer"),
null, title, "500px", "auto",
)
// Clone the popup contents and insert them into the popup
let clone = document.getElementById("tpl_captcha_popup").content.cloneNode(true)
clone.querySelector(".captcha_text").innerText = text
recaptchaElement = clone.querySelector(".captcha_popup_captcha")
this.captchaModal.setBody(clone)
// Set the callback function
recaptchaCallback = token => {
// Download the file using the recaptcha token
this.downloadFrame.src = this.currentFile.download_href+"&recaptcha_response="+token
this.captchaModal.close()
}
let showCaptcha = () => {
// Load the recaptcha script with a load function
let script = document.createElement("script")
script.src = "https://www.google.com/recaptcha/api.js?onload=loadCaptcha&render=explicit"
document.body.appendChild(script)
// Show the popup
popupDiv.style.opacity = "1"
popupDiv.style.visibility = "visible"
this.captchaModal.open()
}
if (resp.value === "file_rate_limited_captcha_required") {
popupTitle.innerText = "Rate limiting enabled!"
popupContent.innerText = "This file is using a suspicious "+
"amount of bandwidth relative to its popularity. To "+
"continue downloading this file you will have to "+
"prove that you're a human first."
showCaptcha()
} else if (resp.value === "virus_detected_captcha_required") {
popupTitle.innerText = "Malware warning!"
popupContent.innerText = "According to our scanning "+
"systems this file may contain a virus of type '"+
resp.extra+"'. You can continue downloading this file at "+
"your own risk, but you will have to prove that you're a "+
"human first."
showCaptcha()
} else {
console.warn("resp.value not valid: "+resp.value)
triggerDL()
console.log(this.currentFile.availability)
if (this.currentFile.availability === "file_rate_limited_captcha_required") {
console.debug("Showing rate limiting captcha")
showCaptcha(
"Rate limiting enabled!",
"This file is using a suspicious amount of bandwidth relative "+
"to its popularity. To continue downloading this file you "+
"will have to prove that you're a human first.",
)
} else if (this.currentFile.availability === "virus_detected_captcha_required") {
console.debug("Showing virus captcha")
showCaptcha(
"Malware warning!",
"According to our scanning systems this file may contain a "+
"virus of type '"+this.currentFile.availability_name+"'. You "+
"can continue downloading this file at your own risk, but you "+
"will have to prove that you're a human first.",
)
}
}).catch(e => {
console.warn("fetch availability failed: "+e)
triggerDL()
})
}
}
Toolbar.prototype.copyUrl = function() {
@@ -154,23 +151,13 @@ Toolbar.prototype.copyUrl = function() {
}, 60000)
}
// Called by the google recaptcha script
let recaptchaResponse = ""
let recaptchaElement = null
let recaptchaCallback = null
function loadCaptcha(){
grecaptcha.render("captcha_popup_captcha", {
grecaptcha.render(recaptchaElement, {
sitekey: captchaKey,
theme: "dark",
callback: token => {
recaptchaResponse = token
document.getElementById("btn_download").click()
// Hide the popup
setTimeout(() => {
let popupDiv = document.getElementById("captcha_popup")
popupDiv.style.opacity = "0"
popupDiv.style.visibility = "hidden"
}, 1000)
}
callback: recaptchaCallback,
})
}

View File

@@ -4,6 +4,7 @@ function Viewer(type, viewToken, data) {
this.listNavigator = null
this.detailsWindow = null
this.divFilepreview = null
this.file = null
this.title = "" // Contains either the file name or list title
this.listId = ""
this.viewToken = ""
@@ -14,6 +15,7 @@ function Viewer(type, viewToken, data) {
this.viewToken = viewToken
this.toolbar = new Toolbar(this)
this.detailsWindow = new DetailsWindow(this)
this.editWindow = new EditWindow()
this.divFilepreview = document.getElementById("filepreview")
@@ -63,7 +65,13 @@ function Viewer(type, viewToken, data) {
this.initialized = true
}
Viewer.prototype.getFile = function() {
return this.file
}
Viewer.prototype.setFile = function(file) {
this.file = file
if (this.isList) {
document.getElementById("file_viewer_headerbar_title").style.lineHeight = "1em"
document.getElementById("file_viewer_list_title").innerText = this.title
@@ -76,6 +84,7 @@ Viewer.prototype.setFile = function(file) {
// Relay the file change event to all components
this.detailsWindow.setFile(file)
this.editWindow.setFile(file)
this.toolbar.setFile(file)
// Register a new view. We don't care what this returns becasue we can't
@@ -169,6 +178,7 @@ Viewer.prototype.keyboardEvent = function(evt) {
return // prevent custom shortcuts from interfering with system shortcuts
}
console.debug("Key pressed: "+evt.keyCode)
switch (evt.keyCode) {
case 65: // A or left arrow key go to previous file
case 37:
@@ -200,6 +210,9 @@ Viewer.prototype.keyboardEvent = function(evt) {
case 73: // I to open the details window
this.detailsWindow.toggle()
break
case 69: // E to open the edit window
this.editWindow.toggle()
break
case 81: // Q to close the window
window.close()
break
@@ -217,35 +230,28 @@ function escapeHTML(str) {
}
function fileFromAPIResp(resp) {
let file = {
id: resp.id,
name: resp.name,
mime_type: resp.mime_type,
size: resp.size,
date_created: new Date(resp.date_upload),
date_last_view: new Date(resp.date_last_view),
views: resp.views,
bandwidth_used: resp.bandwidth_used,
description: "",
icon_href: apiEndpoint+"/file/"+resp.id+"/thumbnail",
get_href: apiEndpoint+"/file/"+resp.id,
download_href: apiEndpoint+"/file/"+resp.id+"?download",
availability_href: apiEndpoint+"/file/"+resp.id+"/availability",
view_href: apiEndpoint+"/file/"+resp.id+"/view",
timeseries_href: apiEndpoint+"/file/"+resp.id+"/timeseries",
link: domainURL()+"/u/"+resp.id,
resp.date_created = new Date(resp.date_upload)
resp.date_last_view = new Date(resp.date_last_view)
resp.icon_href = apiEndpoint+"/file/"+resp.id+"/thumbnail"
resp.get_href = apiEndpoint+"/file/"+resp.id
resp.download_href = apiEndpoint+"/file/"+resp.id+"?download"
resp.view_href = apiEndpoint+"/file/"+resp.id+"/view"
resp.timeseries_href = apiEndpoint+"/file/"+resp.id+"/timeseries"
resp.link = domainURL()+"/u/"+resp.id
if (resp.description === undefined) {
resp.description = ""
}
if (resp.description !== undefined) {
file.description = resp.description
}
return file
console.debug("New file:")
console.debug(resp)
return resp
}
function fileFromSkyNet(resp) {
let file = fileFromAPIResp(resp)
file.icon_href = "/res/img/mime/empty.png"
file.get_href = "https://sky.pixeldrain.com/file/"+resp.id
file.download_href = "https://sky.pixeldrain.com/file/"+resp.id+"?attachment=1"
file.availability_href = ""
file.get_href = "https://skydrain.net/file/"+resp.id
file.download_href = "https://skydrain.net/file/"+resp.id+"?attachment=1"
file.view_href = ""
file.timeseries_href = ""
file.link = domainURL()+"/s/"+resp.id

View File

@@ -123,11 +123,15 @@
/* padding-top: 6px; */
}
.node:hover, .node_selected {
.node:hover:not(.node_selected) {
background-color: var(--input_color_dark);
color: var(--input_text_color);
text-decoration: none;
}
.node_selected {
background-color: var(--highlight_color);
color: var(--highlight_text_color);
}
.node > div {
height: 100%;
overflow: hidden;

View File

@@ -17,7 +17,35 @@
local('Cantarell Light'),
local('Cantarell, Light'),
local('Cantarell-Light'),
url("/res/misc/Cantarell-Light.otf") format("opentype");
url(/res/misc/Cantarell-Light.otf) format("opentype");
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'),
local('MaterialIcons-Regular'),
url(/res/misc/MaterialIcons-Regular.woff2) format('woff2'),
url(/res/misc/MaterialIcons-Regular.woff) format('woff'),
url(/res/misc/MaterialIcons-Regular.ttf) format('truetype');
}
.icon {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
text-rendering: optimizeLegibility;
}
.icon.small {
font-size: 16px;
}
/* Page rendering configuration */

View File

@@ -0,0 +1,58 @@
.modal_background {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.6);
opacity: 0;
transition: opacity .4s;
}
.modal_window {
position: absolute;
z-index: inherit;
display: flex;
flex-direction: column;
background-color: var(--layer_2_color);
max-height: 100%;
max-width: 100%;
top: 20%;
left: 50%;
transform: translate(-50%, -20%);
padding: 0;
box-sizing: border-box;
text-align: left;
box-shadow: var(--shadow_color) 0px 0px 50px;
}
.modal_header {
flex-grow: 0;
flex-shrink: 0;
display: flex;
flex-direction: row;
padding: 4px;
}
.modal_title {
flex-grow: 1;
flex-shrink: 0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 1.2em;
}
.modal_btn_close {
flex-grow: 0;
flex-shrink: 0;
}
.modal_body {
flex-grow: 1;
flex-shrink: 1;
overflow: auto;
padding: 10px;
}

View File

@@ -253,45 +253,7 @@
|| MISC COMPONENTS ||
===================== */
.popup {
position: absolute;
visibility: hidden;
display: flex;
flex-direction: column;
opacity: 0;
transition: visibility .5s, opacity .5s;
background-color: var(--layer_2_color);
border-color: var(--layer_2_color_border);
max-height: 100%;
max-width: 100%;
top: 20%;
left: 50%;
transform: translate(-50%, -20%);
padding: 0;
box-sizing: border-box;
text-align: left;
box-shadow: var(--shadow_color) 0px 0px 50px;
}
.popup > .highlight_1 {
font-size: 1.2em;
}
.popup > .content_area {
flex: 1;
overflow: auto;
padding: 10px;
}
.details_popup{
width: 1500px;
height: 800px;
z-index: 200;
}
.captcha_popup{
height: auto;
width: 450px;
z-index: 201;
}
#captcha_popup_captcha > div {
.captcha_popup_captcha > div {
display: inline-block;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -50,10 +50,10 @@
let breadcrumbs = document.querySelector("#nav_bar > .breadcrumbs")
if (window.location.href.endsWith("?files")) {
breadcrumbs.value += "/Files"
breadcrumbs.value = "/{{.Username}}/Files"
fm.getUserFiles()
} else if (window.location.href.endsWith("?lists")) {
breadcrumbs.value += "/Lists"
breadcrumbs.value = "/{{.Username}}/Lists"
fm.getUserLists()
} else {
alert("invalid file manager type")

View File

@@ -13,7 +13,7 @@
<style>
{{template `viewer.css`}}
{{template `layout.css`}}
{{template `modal.css`}}
</style>
{{.OGData}}
@@ -22,7 +22,7 @@
<body>
<div id="file_viewer" class="file_viewer">
<div id="file_viewer_headerbar" class="highlight_1 file_viewer_headerbar">
<button id="btn_toggle_toolbar" class="button_toggle_toolbar"></button>
<button id="btn_toggle_toolbar" class="button_toggle_toolbar"><i class="icon small">menu</i></button>
<a href="/" id="button_home" class="button button_home">
{{template `pixeldrain.svg` .}}
</a>
@@ -30,7 +30,9 @@
<div id="file_viewer_list_title"></div>
<div id="file_viewer_file_title">{{.Title}}</div>
</div>
<button id="button_close_file_viewer" class="button_close_file_viewer button_red" onclick="window.close();">{{template `close.svg` .}}</button>
<button id="button_close_file_viewer" class="button_close_file_viewer button_red" onclick="window.close();">
<i class="icon small">close</i>
</button>
</div>
<div id="list_navigator" class="list_navigator"></div>
<div id="file_viewer_window" class="file_viewer_window">
@@ -43,29 +45,33 @@
<div id="stat_size" style="text-align: center;">N/A</div>
<button id="btn_download" class="toolbar_button button_full_width">
{{template `save.svg` .}}
<i class="icon">save</i>
<span>Download</span>
</button>
<button id="btn_download_list" class="toolbar_button button_full_width" style="display: none">
{{template `save.svg` .}}
<button id="btn_download_list" class="toolbar_button button_full_width" style="display: none;">
<i class="icon">save</i>
<span>DL all files</span>
</button>
<button id="btn_copy" class="toolbar_button button_full_width">
{{template `copy.svg` .}}
<i class="icon">content_copy</i>
<span><u>C</u>opy Link</span>
</button>
<button id="btn_share" class="toolbar_button button_full_width">
{{template `share.svg` .}}
<i class="icon">share</i>
<span>Share</span>
</button>
<button id="btn_shuffle" class="toolbar_button button_full_width" style="display: none">
{{template `shuffle.svg` .}}
<button id="btn_shuffle" class="toolbar_button button_full_width" style="display: none;">
<i class="icon">shuffle</i>
<span>Shuffle &#x2610;</span>
</button>
<button id="btn_details" class="toolbar_button button_full_width">
{{template `help.svg` .}}
<i class="icon">help</i>
<span>Deta<u>i</u>ls</span>
</button>
<button id="btn_edit" class="toolbar_button button_full_width" style="display: none;">
<i class="icon">edit</i>
<span><u>E</u>dit</span>
</button>
{{template "advertisement" .}}
<a href="https://twitter.com/SiaTechHQ/status/1228050383685201921">
@@ -99,52 +105,61 @@
<div class="center" style="width: 100px; height: 100px;">{{template "spinner.svg" .}}</div>
</div>
</div>
<!-- Popup windows, hidden by default -->
<div id="details_popup" class="popup details_popup">
<div id="details_popup_title" class="highlight_1">
File Info
<button id="btn_close_details" style="position: absolute; top: 3px; right: 3px;" class="button_red">{{template `close.svg` .}}</button>
</div>
<div class="content_area">
<div id="info_file_details"></div>
<div id="info_about">
<h3>Downloads and views</h3>
<div id="chart_container" class="chart-container" style="position: relative; width: 100%; height: auto;">
<canvas id="bandwidth_chart"></canvas>
</div>
<p style="text-align: center">
Chart rendered by the amazing <a href="https://www.chartjs.org/" target="_blank">Chart.js</a>.
</p>
<h3>About</h3>
Pixeldrain is a file sharing platform.
<a href="/" target="_blank">Visit the home page for more information.</a>
<h3>Keyboard Controls</h3>
<table>
<tr><td colspan="2">File Shortcuts</td></tr>
<tr><td>c</td><td> = Copy URL of this page</td></tr>
<tr><td>i</td><td> = Toggle details window (this window) (<b><u>i</u></b>nfo)</td></tr>
<tr><td>s</td><td> = Download the file you are currently viewing (<b><u>s</u></b>ave)</td></tr>
<tr><td>q</td><td> = Close the window (<b><u>q</u></b>uit)</td></tr>
<tr><td colspan="2">List Shortcuts</td></tr>
<tr><td>a or &#8592;</td><td> = View previous item in list</td></tr>
<tr><td>d or &#8594;</td><td> = View next item in list</td></tr>
<tr><td>r</td><td> = Toggle shuffle (<b><u>r</u></b>andom)</td></tr>
<tr><td>SHIFT + s</td><td> = Download all the files in the list as a zip archive</td></tr>
</table>
</div>
</div>
</div>
<div id="captcha_popup" class="popup captcha_popup">
<div id="captcha_popup_title" class="highlight_1"></div>
<div id="captcha_popup_content" class="content_area"></div>
<br/>
<div id="captcha_popup_captcha" style="text-align: center;"></div>
</div>
</div>
<template id="tpl_details_popup">
<table class="info_file_details" style="min-width: 100%;"></table>
<div class="info_about">
<h3>Downloads and views</h3>
<div class="chart-container" style="position: relative; width: 100%; height: auto;">
<canvas id="bandwidth_chart"></canvas>
</div>
<p style="text-align: center">
Chart rendered by the amazing <a href="https://www.chartjs.org/" target="_blank">Chart.js</a>.
</p>
<h3>About</h3>
Pixeldrain is a file sharing platform.
<a href="/" target="_blank">Visit the home page for more information.</a>
<h3>Keyboard Controls</h3>
<table style="max-width: 100%;">
<tr><td colspan="2">File Shortcuts</td></tr>
<tr><td>c</td><td> = Copy URL of this page</td></tr>
<tr><td>i</td><td> = Toggle details window (this window) (<b><u>i</u></b>nfo)</td></tr>
<tr><td>s</td><td> = Download the file you are currently viewing (<b><u>s</u></b>ave)</td></tr>
<tr><td>q</td><td> = Close the window (<b><u>q</u></b>uit)</td></tr>
<tr><td colspan="2">List Shortcuts</td></tr>
<tr><td>a or &#8592;</td><td> = View previous item in list</td></tr>
<tr><td>d or &#8594;</td><td> = View next item in list</td></tr>
<tr><td>r</td><td> = Toggle shuffle (<b><u>r</u></b>andom)</td></tr>
<tr><td>SHIFT + s</td><td> = Download all the files in the list as a zip archive</td></tr>
</table>
</div>
</template>
<template id="tpl_edit_file">
<h3>Delete file</h3>
<p>
When you delete a file it cannot be recovered.
Nobody will be able to download it and the link will
stop working. The file will also disappear from any
lists it's contained in.
</p>
<div style="text-align: center;">
<button class="button_red btn_delete_file">
<i class="icon small">delete</i> Delete this file
</button>
</div>
</template>
<template id="tpl_captcha_popup">
<div class="captcha_text"></div>
<br/>
<div class="captcha_popup_captcha" style="text-align: center;"></div>
</template>
<script src="/res/script/Chart.min.js"></script>
<script>
'use strict';
@@ -152,7 +167,9 @@
let captchaKey = '{{.Other.CaptchaKey}}';
{{template `util.js`}}
{{template `Modal.js`}}
{{template `Toolbar.js`}}
{{template `EditWindow.js`}}
{{template `DetailsWindow.js`}}
{{template `ListNavigator.js`}}
{{template `Viewer.js`}}

View File

@@ -165,7 +165,7 @@ func (wc *WebController) serveSkynetViewer(w http.ResponseWriter, r *http.Reques
// Get the first few bytes from the file to probe the content type and
// length
rq, err := http.NewRequest("GET", "https://sky.pixeldrain.com/file/"+p.ByName("id"), nil)
rq, err := http.NewRequest("GET", "https://skydrain.net/file/"+p.ByName("id"), nil)
if err != nil {
log.Warn("Failed to make request to sia portal: %s", err)
w.WriteHeader(http.StatusInternalServerError)

View File

@@ -181,7 +181,6 @@ func (wc *WebController) loginForm(td *TemplateData, r *http.Request) (f Form) {
Expires: time.Now().AddDate(50, 0, 0),
Domain: wc.sessionCookieDomain,
SameSite: http.SameSiteStrictMode,
Secure: true,
}
f.Extra.RedirectTo = "/user"
}