@@ -1,3 +1,3 @@
|
||||
<tr class="compact-list-item color-highlight-dark">
|
||||
<tr class=["compact-list-item", input.highlight ? "color-highlight-primary" : "color-highlight-dark"]>
|
||||
<include(input.renderBody)/>
|
||||
</tr>
|
||||
@@ -2,4 +2,22 @@ tr.compact-list-item {
|
||||
user-select: none;
|
||||
outline: 0.05px dotted;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tr.compact-list-item.color-highlight-primary {
|
||||
a {
|
||||
color: #FFF !important;
|
||||
}
|
||||
|
||||
label {
|
||||
color: #CCC !important;
|
||||
}
|
||||
|
||||
div.label {
|
||||
color: #AA8 !important;
|
||||
}
|
||||
|
||||
div.value.color-text {
|
||||
color: #FFF !important;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
table.compact-list {
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 3em;
|
||||
|
||||
width: 90%;
|
||||
max-width: 100ch;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {fileApi} = require("../../../../../rpdata/api/File")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
file: null,
|
||||
name: "",
|
||||
public: false,
|
||||
|
||||
realFile: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
if (key === "file") {
|
||||
this.state.values = Object.assign({}, this.state.values, {
|
||||
[key]: ev.target.value,
|
||||
realFile: ev.target.files[0],
|
||||
name: ev.target.files[0].name,
|
||||
})
|
||||
} else {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.state.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = Object.assign({}, this.state.values)
|
||||
input.file = input.realFile
|
||||
delete input.realFile
|
||||
|
||||
this.state.loading = true
|
||||
fileApi.uploadFile(input).then((res) => {
|
||||
this.emit("added", res)
|
||||
this.emit("close")
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add:", errs)
|
||||
|
||||
this.state.error = "Failed to add: " + JSON.stringify(errs)
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Upload File</h1>
|
||||
|
||||
<p class="color-error">${state.error}</p>
|
||||
|
||||
<label>File</label>
|
||||
<input key="eventName" type="file" placeholder="(None)" on-change("change", "file") value=state.values.file />
|
||||
|
||||
<label>Name</label>
|
||||
<input key="name" autofocus placeholder="(Required)" class="big" on-change("change", "name") value=state.values.name />
|
||||
|
||||
<label>Public</label>
|
||||
<toggle value=state.values.public on="On" off="Off"
|
||||
onDesc="Other members may see this file on this page."
|
||||
offDesc="Only you may see this file on this page."
|
||||
on-change("change", "public") />
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -99,6 +99,19 @@ module.exports = class {
|
||||
break
|
||||
}
|
||||
|
||||
case "File": {
|
||||
const key = data.keys.find(k => k.id !== "*")
|
||||
const fileObj = data.objects.find(o => o.type === "File") || {name: key.id}
|
||||
|
||||
this.state.operation += " file"
|
||||
this.state.links = [{
|
||||
text: fileObj.name,
|
||||
href: `/data/files#${key.id}`,
|
||||
}]
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case "Post": {
|
||||
const logKey = data.keys.find(k => k.model === "Log")
|
||||
const postKeys = data.keys.filter(k => k.model === "Post")
|
||||
@@ -124,6 +137,7 @@ module.exports = class {
|
||||
const opText = {
|
||||
add: "added",
|
||||
remove: "removed",
|
||||
upload: "uploaded",
|
||||
move: "moved",
|
||||
edit: "edited",
|
||||
import: "imported",
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
</main>
|
||||
<add-channel-modal enabled=(state.modal === "channel.add") user=input.user on-added("channelAdded") on-close("close") />
|
||||
<add-character-modal enabled=(state.modal === "character.add") user=input.user on-close("close") />
|
||||
<add-file-modal enabled=(state.modal === "file.add") user=input.user on-close("close") />
|
||||
|
||||
@@ -31,6 +31,15 @@ module.exports = class {
|
||||
}
|
||||
|
||||
characterAdded(character) {
|
||||
const index = this.state.characters.findIndex(c => c.id === character.id)
|
||||
if (index !== -1) {
|
||||
const characters = this.state.characters.slice()
|
||||
characters[index] = character
|
||||
this.state.characters = characters
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.state.characters = this.state.characters.concat(character)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
</main>
|
||||
<add-character-modal enabled=(state.modal === "character.add") user=input.user on-added("characterAdded") on-close("close") />
|
||||
<add-channel-modal enabled=(state.modal === "channel.add") user=input.user on-close("close") />
|
||||
<add-file-modal enabled=(state.modal === "file.add") user=input.user on-close("close") />
|
||||
|
||||
@@ -3,14 +3,18 @@
|
||||
<menu-link key="characters" selected=input.selected.characters icon="C" href="/data/characters/">Characters</menu-link>
|
||||
<menu-link key="channels" selected=input.selected.channels icon="#" href="/data/channels/">Channels</menu-link>
|
||||
<menu-link key="history" selected=input.selected.changes icon="H" href="/data/changes/">History</menu-link>
|
||||
<if-permitted user=input.user permission=["member", "character.add", "channel.add"]>
|
||||
<menu-link key="files" selected=input.selected.files icon="F" href="/data/files/">Files</menu-link>
|
||||
<if-permitted user=input.user permission=["member", "character.add", "channel.add", "file.add"]>
|
||||
<menu-gap />
|
||||
<menu-header>Add</menu-header>
|
||||
<if-permitted user=input.user permission=["character.add", "member"]>
|
||||
<menu-link dark key="characters_add" icon="+" on-click("emit", "open", "character.add")>Character</menu-link>
|
||||
<if-permitted key="characters_add" user=input.user permission=["character.add", "member"]>
|
||||
<menu-link dark icon="+" on-click("emit", "open", "character.add")>Character</menu-link>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user permission="channel.add">
|
||||
<menu-link dark key="channels_add" icon="+" on-click("emit", "open", "channel.add")>Channel</menu-link>
|
||||
<if-permitted key="channels_add" user=input.user permission="channel.add">
|
||||
<menu-link dark icon="+" on-click("emit", "open", "channel.add")>Channel</menu-link>
|
||||
</if-permitted>
|
||||
<if-permitted key="files_add" user=input.user permission=["file.add", "member"]>
|
||||
<menu-link dark icon="+" on-click("emit", "open", "file.add")>File</menu-link>
|
||||
</if-permitted>
|
||||
</if-permitted>
|
||||
</menu>
|
||||
@@ -0,0 +1,59 @@
|
||||
const moment = require("moment")
|
||||
|
||||
const {fileApi} = require("../../../../../rpdata/api/File")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
id: "",
|
||||
name: "",
|
||||
public: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
this.state.values = {
|
||||
id: input.file.id,
|
||||
name: input.file.name,
|
||||
public: input.file.public,
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
save() {
|
||||
if (this.state.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = Object.assign({}, this.state.values)
|
||||
input.file = input.realFile
|
||||
delete input.realFile
|
||||
|
||||
this.state.loading = true
|
||||
fileApi.editFile(input).then((res) => {
|
||||
this.emit("edited", res)
|
||||
this.emit("close")
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add:", errs)
|
||||
|
||||
this.state.error = "Failed to add: " + JSON.stringify(errs)
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Upload File</h1>
|
||||
|
||||
<p class="color-error">${state.error}</p>
|
||||
|
||||
<label>Name</label>
|
||||
<input key="name" autofocus placeholder="(Required)" class="big" on-change("change", "name") value=state.values.name />
|
||||
|
||||
<label>Public</label>
|
||||
<toggle value=state.values.public on="On" off="Off"
|
||||
onDesc="Other members may see this file on this page."
|
||||
offDesc="Only you may see this file on this page."
|
||||
on-change("change", "public") />
|
||||
|
||||
<button disabled=state.loading on-click("save")>Save</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = class {
|
||||
isHighlighted(id) {
|
||||
if (typeof(window) !== "undefined") {
|
||||
return window.location.hash.endsWith(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<compact-list>
|
||||
<compact-list-item for(file in input.files) key=file.id id=file.id highlight=component.isHighlighted(file.id)>
|
||||
<file data=file
|
||||
user=input.user
|
||||
on-removed("emit", "removed", file)
|
||||
on-edited("emit", "edited", file)
|
||||
/>
|
||||
</compact-list-item>
|
||||
</compact-list>
|
||||
@@ -0,0 +1,38 @@
|
||||
const moment = require("moment")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
modal: null,
|
||||
deleted: false,
|
||||
date: moment(input.data.time).format("MMM D, YYYY"),
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
if (input && input.data) {
|
||||
this.state.date = moment(input.data.time).format("MMM D, YYYY")
|
||||
}
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.state.modal = modal
|
||||
}
|
||||
|
||||
close() {
|
||||
this.state.modal = null
|
||||
}
|
||||
|
||||
removed() {
|
||||
this.state.deleted = true
|
||||
this.emit("removed")
|
||||
}
|
||||
|
||||
sizeString(bytes) {
|
||||
const order = Math.floor(Math.log2(bytes) / 10)
|
||||
const unit = ["B", "KB", "MB", "GB", "TB", "PB"][order]
|
||||
const number = bytes / Math.pow(2, (order)*10)
|
||||
|
||||
return `${number.toFixed(2)} ${unit}`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<if (!state.deleted)>
|
||||
<a class="anchor" id=input.data.id />
|
||||
<compact-list-property primary long label="Name" value=input.data.name />
|
||||
<compact-list-property short label="Date" value=state.date />
|
||||
<compact-list-property short label="Type" value=input.data.mimeType />
|
||||
<compact-list-property short label="Author" value=input.data.author />
|
||||
<compact-list-property short label="Size" value=component.sizeString(input.data.size) />
|
||||
<compact-list-options long>
|
||||
<a key="link" class="color-menu" on-click("open", "link")>Link</a>
|
||||
<if(input.data.kind === "upload")>
|
||||
<if-permitted user=input.user author=input.data.author permission="file.edit">
|
||||
<a key="edit" on-click("open", "edit", input.data)>Edit</a>
|
||||
</if-permitted>
|
||||
<if-permitted user=input.user author=input.data.author permission="file.remove">
|
||||
<a key="remove" on-click("open", "remove", input.data)>Remove</a>
|
||||
</if-permitted>
|
||||
</if>
|
||||
</compact-list-options>
|
||||
<link-file-modal enabled=(state.modal === "link") file=input.data on-close("close") />
|
||||
<edit-file-modal enabled=(state.modal === "edit") file=input.data on-edited("emit", "edited") on-close("close") />
|
||||
<remove-file-modal enabled=(state.modal === "remove") file=input.data on-removed("removed") on-close("close") />
|
||||
</if>
|
||||
@@ -0,0 +1,47 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
publics: input.publics.sort(byDate),
|
||||
privates: input.privates.sort(byDate),
|
||||
modal: null,
|
||||
}
|
||||
}
|
||||
|
||||
onMount() {
|
||||
}
|
||||
|
||||
open(modal) {
|
||||
this.state.modal = modal
|
||||
}
|
||||
|
||||
close() {
|
||||
this.state.modal = null
|
||||
}
|
||||
|
||||
fileRemoved({id}) {
|
||||
this.state.publics = this.state.publics.filter(f => f.id !== id)
|
||||
this.state.privates = this.state.privates.filter(f => f.id !== id)
|
||||
}
|
||||
|
||||
fileEdited(file, patch) {
|
||||
this.fileRemoved({id: file.id})
|
||||
this.fileAdded(Object.assign({}, file, patch))
|
||||
|
||||
console.log(Object.assign({}, file, patch))
|
||||
}
|
||||
|
||||
fileAdded(file) {
|
||||
if (file.public) {
|
||||
this.state.publics = [].concat(...this.state.publics).concat(file).sort(byDate)
|
||||
} else {
|
||||
this.state.privates = [].concat(...this.state.privates).concat(file).sort(byDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function byDate(a, b) {
|
||||
const ad = new Date(a.time)
|
||||
const bd = new Date(b.time)
|
||||
|
||||
return ad.getTime() - bd.getTime()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<data-menu categories=input.categories selected=(input.selected || {}) user=input.user on-open("open") />
|
||||
<main class="data-file-page">
|
||||
<h1 class="file-list-headers color-primary">Your Private Files</h1>
|
||||
<file-list user=input.user files=state.privates on-removed("fileRemoved") on-edited("fileEdited") on-nicks("fileNicksChanged") />
|
||||
|
||||
<h1 class="file-list-headers color-primary">Public Files</h1>
|
||||
<file-list user=input.user files=state.publics on-removed("fileRemoved") on-edited("fileEdited") on-nicks("fileNicksChanged") />
|
||||
</main>
|
||||
<add-channel-modal enabled=(state.modal === "channel.add") user=input.user on-close("close") />
|
||||
<add-character-modal enabled=(state.modal === "character.add") user=input.user on-close("close") />
|
||||
<add-file-modal enabled=(state.modal === "file.add") user=input.user on-added("fileAdded") on-close("close") />
|
||||
@@ -0,0 +1,10 @@
|
||||
h1.file-list-headers {
|
||||
margin: 0;
|
||||
font-size: 1.5em;
|
||||
margin-top: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
main.data-file-page {
|
||||
margin-bottom: 4em;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
isImage: (input.file && input.file.mimeType.startsWith("image/"))
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
this.state.isImage = (input.file && input.file.mimeType.startsWith("image/"))
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<modal class="modal color-text nolabel link-file-modal" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Links for ${input.file.name}</h1>
|
||||
|
||||
<p>
|
||||
Below are links and copy-pastable syntax for sharing the file. They can be used for linking to it
|
||||
in your stories and wiki pages.
|
||||
</p>
|
||||
|
||||
<p class="color-danger">
|
||||
This is the link to the actual file on the CDN, not a share of it. You must delete the file if you want
|
||||
to un-share it.
|
||||
</p>
|
||||
|
||||
<if(state.isImage)>
|
||||
<p class="color-danger">
|
||||
Images uploaded here cannot be inlined on the wiki. For that you have to upload it there.
|
||||
</p>
|
||||
</if>
|
||||
|
||||
<label>URL</label>
|
||||
<input key="url" disabled placeholder="(None)" on-change("change", "eventName") value=input.file.url />
|
||||
|
||||
<label>Markdown link</label>
|
||||
<input key="mdLink" disabled placeholder="(None)" on-change("change", "locationName") value=`[Link text](${input.file.url})` />
|
||||
|
||||
<label>Wiki link</label>
|
||||
<input key="wikiLink" disabled placeholder="(None)" on-change("change", "locationName") value=`[${input.file.url} Link text]` />
|
||||
|
||||
<if(state.isImage)>
|
||||
<label>Markdown image</label>
|
||||
<input key="mdImage" disabled placeholder="(None)" on-change("change", "locationName") value=`` />
|
||||
|
||||
<label>Image Preview</label>
|
||||
<img src=input.file.url alt="File preview"/>
|
||||
</if>
|
||||
</modal>
|
||||
@@ -0,0 +1,19 @@
|
||||
div.link-file-modal {
|
||||
user-select: text;
|
||||
|
||||
p {
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: 0.25em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input {
|
||||
user-select: all !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
const {fileApi} = require("../../../../../rpdata/api/File")
|
||||
|
||||
module.exports = class {
|
||||
onCreate(input) {
|
||||
this.state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
values: {
|
||||
id: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onInput(input) {
|
||||
this.state.values = {
|
||||
id: input.file.id,
|
||||
}
|
||||
}
|
||||
|
||||
change(key, ev) {
|
||||
this.state.values = Object.assign({}, this.state.values, {[key]: ev.target.value})
|
||||
}
|
||||
|
||||
open() {
|
||||
|
||||
}
|
||||
|
||||
close() {
|
||||
this.emit("close")
|
||||
}
|
||||
|
||||
doIt() {
|
||||
if (this.state.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const input = Object.assign({}, this.state.values)
|
||||
|
||||
this.state.loading = true
|
||||
fileApi.removeFile(input).then((res) => {
|
||||
this.emit("removed", res)
|
||||
this.emit("close")
|
||||
}).catch(errs => {
|
||||
console.warn("Failed to add:", errs)
|
||||
|
||||
this.state.error = "Failed to add: " + JSON.stringify(errs)
|
||||
}).then(() => {
|
||||
this.state.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<modal class="modal color-text nolabel" key="modal" enabled=(input.enabled) closable on-close("close") on-open("open") >
|
||||
<h1>Delete File ${input.file.name}</h1>
|
||||
|
||||
<p class="color-error">${state.error}</p>
|
||||
|
||||
<p class="color-danger">
|
||||
It might take up to an hour for it to be removed from CDN caches if it's been recently loaded.
|
||||
</p>
|
||||
|
||||
<button disabled=state.loading on-click("doIt")>Remove</button>
|
||||
</modal>
|
||||
@@ -0,0 +1,6 @@
|
||||
<include("../layout", {title: "Data", site: "data"})>
|
||||
<@body>
|
||||
<background src="/assets/images/bg.png" opacity=0.25 />
|
||||
<files-page user=input.user privates=input.privates publics=input.publics selected=input.selected />
|
||||
</@body>
|
||||
</include>
|
||||
Reference in New Issue
Block a user