This commit is contained in:
retoor 2025-06-12 01:38:45 +02:00
parent 803ad3dfc6
commit 0c0742fce7
6 changed files with 78 additions and 28 deletions

View File

@ -44,6 +44,7 @@ from snek.view.avatar import AvatarView
from snek.view.channel import ChannelAttachmentView,ChannelAttachmentUploadView, ChannelView
from snek.view.docs import DocsHTMLView, DocsMDView
from snek.view.drive import DriveApiView, DriveView
from snek.view.channel import ChannelDriveApiView
from snek.view.index import IndexView
from snek.view.login import LoginView
from snek.view.logout import LogoutView
@ -286,6 +287,9 @@ class Application(BaseApplication):
self.router.add_view(
"/channel/{channel_uid}/attachment.bin", ChannelAttachmentView
)
self.router.add_view(
"/channel/{channel_uid}/drive.json", ChannelDriveApiView
)
self.router.add_view(
"/channel/{channel_uid}/attachment.sock", ChannelAttachmentUploadView
)

View File

@ -1,4 +1,6 @@
/* A <file-browser> custom element that talks to /api/files */
import { NjetComponent } from "/njet.js";
class FileBrowser extends HTMLElement {
constructor() {
super();
@ -6,9 +8,11 @@ class FileBrowser extends HTMLElement {
this.path = ""; // current virtual path ("" = ROOT)
this.offset = 0; // pagination offset
this.limit = 40; // items per request
this.url = '/drive.json'
}
connectedCallback() {
this.url = this.getAttribute("url") || this.url;
this.path = this.getAttribute("path") || "";
this.renderShell();
this.load();
@ -58,7 +62,7 @@ class FileBrowser extends HTMLElement {
// ---------- Networking ----------------------------------------------
async load() {
const r = await fetch(
`/drive.json?path=${encodeURIComponent(this.path)}&offset=${this.offset}&limit=${this.limit}`,
this.url + `?path=${encodeURIComponent(this.path)}&offset=${this.offset}&limit=${this.limit}`
);
if (!r.ok) {
console.error(await r.text());

View File

@ -134,11 +134,26 @@ class Njet extends HTMLElement {
get rest() {
return Njet._root._rest
}
attach(element) {
this._attachedTo = element
this._attachedTo.addEventListener("resize", () => {
this.updatePosition()
})
}
updatePosition(){
if(this._attachedTo)
{
this.style.width = `${this._attachedTo.offsetWidth}`
this.style.height = `${this._attachedTo.offsetHeight}`
this.style.left = `${this._attachedTo.offsetLeft}`
this.style.top = `${this._attachedTo.offsetTop}`
this.style.position = 'fixed'
}
}
_subscriptions = {}
_elements = []
_rest = null
_attachedTo = null
match(args) {
return Object.entries(args).every(([key, value]) => this[key] === value);
}
@ -312,15 +327,6 @@ class NjetDialog extends Component {
this.innerHTML = '';
const { title, content, primaryButton, secondaryButton } = this.config;
this.classList.add('njet-dialog');
this.style.position = 'fixed';
this.style.top = '50%';
this.style.left = '50%';
this.style.transform = 'translate(-50%, -50%)';
this.style.padding = '20px';
this.style.border = '1px solid #444';
this.style.backgroundColor = '#fff';
this.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
this.style.minWidth = '300px';
if (title) {
const header = document.createElement('h2');
header.textContent = title;
@ -357,22 +363,14 @@ class NjetWindow extends Component {
render() {
this.innerHTML = '';
const { title, content, primaryButton, secondaryButton } = this.config;
this.classList.add('njet-dialog');
this.style.position = 'fixed';
this.style.top = '50%';
this.style.left = '50%';
this.style.transform = 'translate(-50%, -50%)';
this.style.padding = '20px';
this.style.border = '1px solid #444';
this.style.backgroundColor = '#fff';
this.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
this.style.minWidth = '300px';
this.classList.add('njet-window');
if (title) {
const header = document.createElement('h2');
header.textContent = title;
this.appendChild(header);
}
this.config.items.forEach(item => this.appendChild(item));
}

View File

@ -2,7 +2,10 @@
<script type="module">
import { njet } from "/njet.js"
function deleteChannel(channelUid){
app.channel = {}
app.channel.remove = function(channelUid){
const dialog = new njet.showDialog({
title: 'Upload in progress',
@ -14,8 +17,24 @@
}
}
})
}
app.channel._fileManager = null
app.channel.toggleDrive = function(channelUid){
if(app._fileManager){
app._fileManager.remove()
app._fileManager = null
document.querySelector('message-list').style.display = 'block'
return
}
app.channel._fileManager = document.createElement("file-manager")
app.channel._fileManager.style.padding = '10px'
app.channel._fileManager.setAttribute("url",`/channel/${app.channelUid}/drive.json`)
document.querySelector(".chat-area").insertBefore(app.channel._fileManager,document.querySelector(".chat-area").firstChild)
document.querySelector("message-list").style.display = 'none'
}
</script>

View File

@ -13,6 +13,17 @@ from snek.system.view import BaseView
register_heif_opener()
from snek.view.drive import DriveApiView
class ChannelDriveApiView(DriveApiView):
async def get_target(self):
target = await self.services.channel.get_home_folder(self.request.match_info.get("channel_uid"))
target.mkdir(parents=True, exist_ok=True)
return target
async def get_download_url(self, rel):
return f"/channel/{self.request.match_info.get('channel_uid')}/drive/{urllib.parse.quote(rel)}"
class ChannelAttachmentView(BaseView):
async def get(self):
relative_path = self.request.match_info.get("relative_url")

View File

@ -28,8 +28,19 @@ class DriveView(BaseView):
class DriveApiView(BaseView):
async def get(self):
login_required = True
async def get_target(self):
target = await self.services.user.get_home_folder(self.session.get("uid"))
return target
async def get_download_url(self, rel):
return f"/drive/{urllib.parse.quote(rel)}"
async def get(self):
target = await self.get_target()
original_target = target
rel = self.request.query.get("path", "")
offset = int(self.request.query.get("offset", 0))
limit = int(self.request.query.get("limit", 20))
@ -40,6 +51,9 @@ class DriveApiView(BaseView):
if not target.exists():
return web.json_response({"error": "Not found"}, status=404)
if not target.relative_to(original_target):
return web.json_response({"error": "Not found"}, status=404)
if target.is_dir():
entries = []
for p in sorted(
@ -79,7 +93,7 @@ class DriveApiView(BaseView):
}
)
url = self.request.url.with_path(f"/drive/{urllib.parse.quote(rel)}")
url = self.request.url.with_path(await self.get_download_url(rel))
return web.json_response(
{
"name": target.name,