mirror of
https://github.com/RetroShare/RSNewWebUI.git
synced 2026-09-12 19:50:04 +05:00
fix search events, fonts and login; improve Files labels and search results
This commit is contained in:
parent
15b0d169fe
commit
e007e90588
@ -8,14 +8,21 @@ const fileProxyObj = futil.createProxy({}, () => {
|
||||
|
||||
rs.events[rs.RsEventsType.FILE_TRANSFER] = {
|
||||
handler: (event) => {
|
||||
console.log('search results : ', event);
|
||||
console.warn('[RS-DEBUG] FILE_TRANSFER event received:', event);
|
||||
|
||||
// if request item doesn't already exists in Object then create new item
|
||||
if (!Object.prototype.hasOwnProperty.call(fileProxyObj, event.mRequestId)) {
|
||||
fileProxyObj[event.mRequestId] = [];
|
||||
}
|
||||
|
||||
fileProxyObj[event.mRequestId].push(...event.mResults);
|
||||
event.mResults.forEach((newRes) => {
|
||||
const isAlt = fileProxyObj[event.mRequestId].some(
|
||||
(oldRes) => oldRes.fHash === newRes.fHash && oldRes.fName === newRes.fName
|
||||
);
|
||||
if (!isAlt) {
|
||||
fileProxyObj[event.mRequestId].push(newRes);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -11,11 +11,12 @@ const reqObj = {};
|
||||
function handleSubmit() {
|
||||
rs.rsJsonApiRequest('/rsFiles/turtleSearch', { matchString })
|
||||
.then((res) => {
|
||||
console.warn('[RS-DEBUG] turtleSearch response:', res.body);
|
||||
// Add prefix to obj keys so that javascript doesn't sort them
|
||||
reqObj['_' + res.body.retval] = matchString;
|
||||
currentItem = '_' + res.body.retval;
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
.catch((error) => console.error('[RS-DEBUG] turtleSearch error:', error));
|
||||
}
|
||||
|
||||
const SearchBar = () => {
|
||||
@ -63,58 +64,72 @@ const Layout = () => {
|
||||
m('.widget__body', [
|
||||
m('div.file-search-container', [
|
||||
m('div.file-search-container__keywords', [
|
||||
m('h5.bold', 'Keywords'),
|
||||
Object.keys(reqObj).length !== 0 &&
|
||||
m('.keywords-header', [
|
||||
m('h5.bold', 'Keywords'),
|
||||
m(
|
||||
'div.keywords-container',
|
||||
Object.keys(reqObj)
|
||||
.reverse()
|
||||
.map((item, index) => {
|
||||
return m(
|
||||
m.route.Link,
|
||||
{
|
||||
class: active === index ? 'selected' : '',
|
||||
onclick: () => {
|
||||
active = index;
|
||||
currentItem = item;
|
||||
},
|
||||
href: `/files/search/${item}`,
|
||||
},
|
||||
reqObj[item]
|
||||
);
|
||||
})
|
||||
'button.red.clear-btn',
|
||||
{
|
||||
onclick: () => {
|
||||
Object.keys(reqObj).forEach((key) => delete reqObj[key]);
|
||||
Object.keys(fproxy.fileProxyObj).forEach((key) => delete fproxy.fileProxyObj[key]);
|
||||
currentItem = 0;
|
||||
active = 0;
|
||||
},
|
||||
},
|
||||
'Clear'
|
||||
),
|
||||
]),
|
||||
Object.keys(reqObj).length !== 0 &&
|
||||
m(
|
||||
'div.keywords-container',
|
||||
Object.keys(reqObj)
|
||||
.reverse()
|
||||
.map((item, index) => {
|
||||
return m(
|
||||
m.route.Link,
|
||||
{
|
||||
class: active === index ? 'selected' : '',
|
||||
onclick: () => {
|
||||
active = index;
|
||||
currentItem = item;
|
||||
},
|
||||
href: `/files/search/${item}`,
|
||||
},
|
||||
reqObj[item]
|
||||
);
|
||||
})
|
||||
),
|
||||
]),
|
||||
m('div.file-search-container__results', [
|
||||
Object.keys(fproxy.fileProxyObj).length === 0
|
||||
Object.keys(fproxy.fileProxyObj).length === 0 || currentItem === 0
|
||||
? m('h5.bold', 'Results')
|
||||
: m('table.results-container', [
|
||||
m(
|
||||
'thead.results-header',
|
||||
m('tr', [
|
||||
m('th', 'Name'),
|
||||
m('th', 'Size'),
|
||||
m('th', 'Hash'),
|
||||
m('th', 'Download'),
|
||||
])
|
||||
),
|
||||
m(
|
||||
'tbody.results',
|
||||
fproxy.fileProxyObj[currentItem.slice(1)]
|
||||
? fproxy.fileProxyObj[currentItem.slice(1)].map((item) =>
|
||||
m('tr', [
|
||||
m('td.results__name', [m('i.fas.fa-file'), m('span', item.fName)]),
|
||||
m('td.results__size', rs.formatBytes(item.fSize.xint64)),
|
||||
m('td.results__hash', item.fHash),
|
||||
m(
|
||||
'td.results__download',
|
||||
m('button', { onclick: () => handleFileDownload(item) }, 'Download')
|
||||
),
|
||||
])
|
||||
)
|
||||
: 'No Results.'
|
||||
),
|
||||
]),
|
||||
: m('div.results-container', [
|
||||
m(
|
||||
'div.results-header',
|
||||
m('.results-row', [
|
||||
m('.results-cell.name-col', 'Name'),
|
||||
m('.results-cell.size-col', 'Size'),
|
||||
m('.results-cell.hash-col', 'Hash'),
|
||||
m('.results-cell.action-col', 'Download'),
|
||||
])
|
||||
),
|
||||
m(
|
||||
'div.results-list',
|
||||
fproxy.fileProxyObj[currentItem.slice(1)]
|
||||
? fproxy.fileProxyObj[currentItem.slice(1)].map((item) =>
|
||||
m('div.results-row.file-item', [
|
||||
m('.results-cell.name-col', [m('i.fas.fa-file'), m('span', item.fName)]),
|
||||
m('.results-cell.size-col', rs.formatBytes((item.fSize && (item.fSize.xint64 || item.fSize.xstr64)) || 0)),
|
||||
m('.results-cell.hash-col', item.fHash),
|
||||
m(
|
||||
'.results-cell.action-col',
|
||||
m('button', { onclick: () => handleFileDownload(item) }, 'Download')
|
||||
),
|
||||
])
|
||||
)
|
||||
: 'No Results.'
|
||||
),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
|
||||
@ -3,6 +3,14 @@ const rs = require('rswebui');
|
||||
const util = require('files/files_util');
|
||||
const manager = require('files/files_manager');
|
||||
|
||||
const translateName = (name) => {
|
||||
const n = name.toLowerCase().trim();
|
||||
if (n === 'extra list' || n === '[extra list]') return 'Temporary shared files';
|
||||
// Match hex strings (IDs) or pure numeric strings
|
||||
if (/^[0-9a-fA-F]{16,}$/.test(name) || /^\d+$/.test(name)) return 'My Files';
|
||||
return name;
|
||||
};
|
||||
|
||||
const DisplayFiles = () => {
|
||||
const childrenList = []; // stores children details
|
||||
let loaded = false; // checks whether we have loaded the children details or not.
|
||||
@ -15,28 +23,34 @@ const DisplayFiles = () => {
|
||||
},
|
||||
view: (v) => [
|
||||
m('tr', [
|
||||
parStruct && Object.keys(parStruct.details.children).length
|
||||
parStruct && parStruct.details.children && parStruct.details.children.length
|
||||
? m(
|
||||
'td',
|
||||
m('i.fas.fa-angle-right', {
|
||||
class: `fa-rotate-${parStruct.showChild ? '90' : '0'}`,
|
||||
style: 'margin-top: 0.5rem',
|
||||
onclick: () => {
|
||||
if (!loaded) {
|
||||
// if it is not already retrieved
|
||||
parStruct.details.children.map(async (child) => {
|
||||
const res = await rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {
|
||||
'td',
|
||||
m('i.fas.fa-angle-right', {
|
||||
class: `fa-rotate-${parStruct.showChild ? '90' : '0'}`,
|
||||
style: 'margin-top: 0.5rem',
|
||||
onclick: async () => {
|
||||
if (!loaded) {
|
||||
// if it is not already retrieved
|
||||
const results = await Promise.all(
|
||||
parStruct.details.children.map((child) =>
|
||||
rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {
|
||||
handle: child.handle.xint64,
|
||||
flags: util.RS_FILE_HINTS_LOCAL,
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
results.forEach((res) => {
|
||||
if (res && res.body && res.body.details) {
|
||||
childrenList.push(res.body.details);
|
||||
loaded = true;
|
||||
});
|
||||
}
|
||||
parStruct.showChild = !parStruct.showChild;
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
});
|
||||
loaded = true;
|
||||
}
|
||||
parStruct.showChild = !parStruct.showChild;
|
||||
},
|
||||
})
|
||||
)
|
||||
: m('td', ''),
|
||||
m(
|
||||
'td',
|
||||
@ -47,29 +61,49 @@ const DisplayFiles = () => {
|
||||
left: `calc(1.5rem*${v.attrs.replyDepth})`,
|
||||
},
|
||||
},
|
||||
parStruct.details.name
|
||||
translateName(parStruct.details.name || '')
|
||||
),
|
||||
m('td', rs.formatBytes(parStruct.details.size.xint64)),
|
||||
m('td', rs.formatBytes((parStruct.details.size && parStruct.details.size.xint64) || 0)),
|
||||
]),
|
||||
parStruct.showChild &&
|
||||
childrenList.map((child) =>
|
||||
m(DisplayFiles, {
|
||||
// recursive call
|
||||
par_directory: { details: child, showChild: false },
|
||||
replyDepth: v.attrs.replyDepth + 1,
|
||||
})
|
||||
),
|
||||
childrenList.map((child) =>
|
||||
m(DisplayFiles, {
|
||||
// recursive call
|
||||
par_directory: { details: child, showChild: false },
|
||||
replyDepth: v.attrs.replyDepth + 1,
|
||||
})
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const Layout = () => {
|
||||
// let root_handle;
|
||||
let parent;
|
||||
let showShareManager = false;
|
||||
let displayList = [];
|
||||
let isLoading = true;
|
||||
let showShareManager = false; // Retain original declaration
|
||||
|
||||
return {
|
||||
oninit: () => {
|
||||
rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {}).then((res) => (parent = res));
|
||||
rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {}).then(async (res) => {
|
||||
if (res && res.body && res.body.details) {
|
||||
if (res.body.details.name === 'root') {
|
||||
// Skip root and fetch full details for each child (Location ID, Extra list, etc)
|
||||
const results = await Promise.all(
|
||||
res.body.details.children.map((child) =>
|
||||
rs.rsJsonApiRequest('/rsfiles/requestDirDetails', {
|
||||
handle: child.handle.xint64,
|
||||
flags: util.RS_FILE_HINTS_LOCAL,
|
||||
})
|
||||
)
|
||||
);
|
||||
displayList = results.map((r) => r.body.details);
|
||||
} else {
|
||||
displayList = [res.body.details];
|
||||
}
|
||||
}
|
||||
isLoading = false;
|
||||
m.redraw();
|
||||
});
|
||||
},
|
||||
view: () => [
|
||||
m('.widget__heading', [
|
||||
@ -81,11 +115,14 @@ const Layout = () => {
|
||||
util.MyFilesTable,
|
||||
m(
|
||||
'tbody',
|
||||
parent &&
|
||||
m(DisplayFiles, {
|
||||
par_directory: { details: parent.body.details, showChild: false },
|
||||
replyDepth: 0,
|
||||
})
|
||||
isLoading
|
||||
? m('tr', m('td[colspan=3]', 'Loading...'))
|
||||
: displayList.map((details) =>
|
||||
m(DisplayFiles, {
|
||||
par_directory: { details, showChild: false },
|
||||
replyDepth: 0,
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
m(
|
||||
|
||||
@ -44,7 +44,7 @@ const navbar = () => {
|
||||
m('.nav-menu__logo-text', [
|
||||
m('h5', 'RetroShare'),
|
||||
m('.webui-version-box', [
|
||||
m('span.webui-version', 'v44'),
|
||||
m('span.webui-version', 'v61'),
|
||||
m('i.fas.fa-sync-alt.refresh-icon', {
|
||||
onclick: () => window.location.reload(true),
|
||||
title: 'Force reload application',
|
||||
@ -181,3 +181,16 @@ m.route(document.getElementById('main'), '/', {
|
||||
render: (v) => m(Layout, m(config, v.attrs)),
|
||||
},
|
||||
});
|
||||
|
||||
// v51 architectural fix: ensure event queue starts on direct route refresh
|
||||
if (rs.loginKey.isVerified && rs.loginKey.username && rs.loginKey.passwd) {
|
||||
console.info('[RS-DEBUG] main.js: Initiating global event queue for verified session...');
|
||||
rs.logon(
|
||||
{ Authorization: `Basic ${btoa(`${rs.loginKey.username}:${rs.loginKey.passwd}`)}` },
|
||||
() => { }, // displayAuthError
|
||||
() => { }, // displayErrorMessage
|
||||
() => {
|
||||
console.info('[RS-DEBUG] Global event queue successfully started.');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -106,6 +106,7 @@ function rsJsonApiRequest(
|
||||
handleSerialize = JSON.stringify,
|
||||
config = null
|
||||
) {
|
||||
console.warn('[RS-DEBUG] rsJsonApiRequest called for path:', path, 'with config:', !!config);
|
||||
headers['Accept'] = 'application/json';
|
||||
if (loginKey.isVerified) {
|
||||
if (loginKey.username && loginKey.passwd) {
|
||||
@ -135,7 +136,7 @@ function rsJsonApiRequest(
|
||||
headers,
|
||||
body: data,
|
||||
|
||||
config,
|
||||
xhr: config,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.status === 200) {
|
||||
@ -254,8 +255,9 @@ const eventQueue = {
|
||||
},
|
||||
},
|
||||
handler: (event) => {
|
||||
console.warn('[RS-DEBUG] Event queue handler received type:', event.mType);
|
||||
if (!deeperIfExist(eventQueue.events, event.mType, (owner) => owner.handler(event, owner))) {
|
||||
// console.info('[RS-DEBUG] unhandled event', event);
|
||||
console.info('[RS-DEBUG] unhandled event', event);
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -340,42 +342,57 @@ function startEventQueue(
|
||||
displayErrorMessage = () => { },
|
||||
successful = () => { }
|
||||
) {
|
||||
return rsJsonApiRequest(
|
||||
'/rsEvents/registerEventsHandler',
|
||||
{},
|
||||
(data, success) => {
|
||||
if (success) {
|
||||
// unused
|
||||
} else if (data.status === 401) {
|
||||
console.warn('[RS-DEBUG] startEventQueue starting raw XHR for:', info);
|
||||
const xhr = new window.XMLHttpRequest();
|
||||
let lastIndex = 0;
|
||||
xhr.open('POST', loginKey.url + '/rsEvents/registerEventsHandler', true);
|
||||
|
||||
// Set headers for authentication
|
||||
const headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...loginHeader,
|
||||
};
|
||||
|
||||
if (loginKey.isVerified && !headers['Authorization']) {
|
||||
if (loginKey.username && loginKey.passwd) {
|
||||
console.warn('[RS-DEBUG] Setting persistent Auth header for event queue');
|
||||
headers['Authorization'] = 'Basic ' + btoa(loginKey.username + ':' + loginKey.passwd);
|
||||
} else {
|
||||
console.warn('[RS-DEBUG] Missing credentials for event queue auth');
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(headers).forEach((key) => {
|
||||
xhr.setRequestHeader(key, headers[key]);
|
||||
});
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
console.warn('[RS-DEBUG] Event Queue XHR state changed:', xhr.readyState, 'status:', xhr.status);
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 401) {
|
||||
displayAuthError('Incorrect login/password.');
|
||||
} else if (data.status === 0) {
|
||||
displayErrorMessage([
|
||||
'Retroshare-jsonapi not available.',
|
||||
m('br'),
|
||||
'Please fix host and/or port.',
|
||||
]);
|
||||
} else {
|
||||
displayErrorMessage('Login failed: HTTP ' + data.status + ' ' + data.statusText);
|
||||
} else if (xhr.status === 0) {
|
||||
console.error('[RS-DEBUG] Event Queue connection failed (status 0)');
|
||||
}
|
||||
},
|
||||
true,
|
||||
loginHeader,
|
||||
JSON.parse,
|
||||
JSON.stringify,
|
||||
(xhr, args, url) => {
|
||||
let lastIndex = 0;
|
||||
xhr.onprogress = (ev) => {
|
||||
const currIndex = xhr.responseText.length;
|
||||
if (currIndex > lastIndex) {
|
||||
const parts = xhr.responseText.substring(lastIndex, currIndex);
|
||||
lastIndex = currIndex;
|
||||
parts
|
||||
.trim()
|
||||
.split('\n\n')
|
||||
.filter((e) => e.startsWith('data: {'))
|
||||
.map((e) => e.substr(6))
|
||||
.map(JSON.parse)
|
||||
.forEach((data) => {
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onprogress = (ev) => {
|
||||
const currIndex = xhr.responseText.length;
|
||||
if (currIndex > lastIndex) {
|
||||
const parts = xhr.responseText.substring(lastIndex, currIndex);
|
||||
lastIndex = currIndex;
|
||||
console.warn('[RS-DEBUG] RAW DATA RECEIVED:', parts);
|
||||
parts
|
||||
.trim()
|
||||
.split('\n\n')
|
||||
.filter((e) => e.trim().length > 0)
|
||||
.forEach((e) => {
|
||||
if (e.startsWith('data: {')) {
|
||||
try {
|
||||
const data = JSON.parse(e.substr(6));
|
||||
console.warn('[RS-DEBUG] PARSED EVENT:', data);
|
||||
if (Object.prototype.hasOwnProperty.call(data, 'retval')) {
|
||||
console.info(
|
||||
'[RS-DEBUG] ' + info + ' [' + data.retval.errorCategory + '] ' + data.retval.errorMessage
|
||||
@ -389,21 +406,38 @@ function startEventQueue(
|
||||
data.event.queueSize = currIndex;
|
||||
try {
|
||||
eventQueue.handler(data.event);
|
||||
} catch (e) {
|
||||
console.error('[RS-DEBUG] Error in event handler:', e, data.event);
|
||||
} catch (err) {
|
||||
console.error('[RS-DEBUG] Error in event handler:', err, data.event);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (currIndex > 1e6) {
|
||||
// max 1 MB eventQueue
|
||||
startEventQueue('restart queue');
|
||||
xhr.abort();
|
||||
} catch (err) {
|
||||
console.error('[RS-DEBUG] JSON parse error for part:', e, err);
|
||||
}
|
||||
} else {
|
||||
console.info('[RS-DEBUG] Ignored non-data part:', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
return xhr;
|
||||
});
|
||||
if (currIndex > 1e6) {
|
||||
// max 1 MB eventQueue
|
||||
console.warn('[RS-DEBUG] Restarting event queue (size > 1MB)');
|
||||
startEventQueue('restart queue');
|
||||
xhr.abort();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
xhr.onload = () => {
|
||||
console.warn('[RS-DEBUG] Event Queue XHR load finished. Status:', xhr.status);
|
||||
};
|
||||
|
||||
xhr.onerror = (err) => {
|
||||
console.error('[RS-DEBUG] Event Queue XHR error occurred:', err);
|
||||
};
|
||||
|
||||
// We need to send an eventType to registerEventsHandler
|
||||
// 0 means all events
|
||||
xhr.send(JSON.stringify({ eventType: 0 }));
|
||||
return xhr;
|
||||
}
|
||||
|
||||
function logon(loginHeader, displayAuthError, displayErrorMessage, successful) {
|
||||
|
||||
@ -46,84 +46,84 @@ p {
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Bold.woff2") format("woff2"), url("./webfonts/Roboto-Bold.woff") format("woff"), url("./webfonts/Roboto-Bold.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Bold.woff") format("woff"), url("./webfonts/Roboto-Bold.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Bold.woff2") format("woff2"), url("./webfonts/Roboto-Bold.woff") format("woff"), url("./webfonts/Roboto-Bold.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Bold.woff") format("woff"), url("./webfonts/Roboto-Bold.ttf") format("truetype");
|
||||
font-weight: bold;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-BoldItalic.woff2") format("woff2"), url("./webfonts/Roboto-BoldItalic.woff") format("woff"), url("./webfonts/Roboto-BoldItalic.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-BoldItalic.woff") format("woff"), url("./webfonts/Roboto-BoldItalic.ttf") format("truetype");
|
||||
font-weight: 700;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-BoldItalic.woff2") format("woff2"), url("./webfonts/Roboto-BoldItalic.woff") format("woff"), url("./webfonts/Roboto-BoldItalic.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-BoldItalic.woff") format("woff"), url("./webfonts/Roboto-BoldItalic.ttf") format("truetype");
|
||||
font-weight: bold;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Medium.woff2") format("woff2"), url("./webfonts/Roboto-Medium.woff") format("woff"), url("./webfonts/Roboto-Medium.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Medium.woff") format("woff"), url("./webfonts/Roboto-Medium.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-MediumItalic.woff2") format("woff2"), url("./webfonts/Roboto-MediumItalic.woff") format("woff"), url("./webfonts/Roboto-MediumItalic.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-MediumItalic.woff") format("woff"), url("./webfonts/Roboto-MediumItalic.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Regular.woff2") format("woff2"), url("./webfonts/Roboto-Regular.woff") format("woff"), url("./webfonts/Roboto-Regular.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Regular.woff") format("woff"), url("./webfonts/Roboto-Regular.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Regular.woff2") format("woff2"), url("./webfonts/Roboto-Regular.woff") format("woff"), url("./webfonts/Roboto-Regular.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Regular.woff") format("woff"), url("./webfonts/Roboto-Regular.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Italic.woff2") format("woff2"), url("./webfonts/Roboto-Italic.woff") format("woff"), url("./webfonts/Roboto-Italic.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Italic.woff") format("woff"), url("./webfonts/Roboto-Italic.ttf") format("truetype");
|
||||
font-weight: 400;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Italic.woff2") format("woff2"), url("./webfonts/Roboto-Italic.woff") format("woff"), url("./webfonts/Roboto-Italic.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Italic.woff") format("woff"), url("./webfonts/Roboto-Italic.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: italic
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-Light.woff2") format("woff2"), url("./webfonts/Roboto-Light.woff") format("woff"), url("./webfonts/Roboto-Light.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-Light.woff") format("woff"), url("./webfonts/Roboto-Light.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
src: url("./webfonts/Roboto-LightItalic.woff2") format("woff2"), url("./webfonts/Roboto-LightItalic.woff") format("woff"), url("./webfonts/Roboto-LightItalic.ttf") format("truetype");
|
||||
src: url("./webfonts/Roboto-LightItalic.woff") format("woff"), url("./webfonts/Roboto-LightItalic.ttf") format("truetype");
|
||||
font-weight: 300;
|
||||
font-style: italic
|
||||
}
|
||||
@ -8565,4 +8565,242 @@ table.boards tr.hidden {
|
||||
height: auto !important;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* FILES MODULE RESPONSIVENESS (v45) */
|
||||
@media (max-width: 700px) {
|
||||
|
||||
/* General Files Layout */
|
||||
.file-view__body-details {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* File Detail Grid (Downloads/Uploads) */
|
||||
.file-view__body-details-stat {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.file-view__body-details-stat span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Share Manager Table -> Cards */
|
||||
.share-manager__table,
|
||||
.share-manager__table thead,
|
||||
.share-manager__table tbody,
|
||||
.share-manager__table tr,
|
||||
.share-manager__table td {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.share-manager__table thead {
|
||||
display: none;
|
||||
/* Hide header on mobile */
|
||||
}
|
||||
|
||||
.share-manager__table tr {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.share-manager__table td {
|
||||
margin-bottom: 0.5rem;
|
||||
border: none !important;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
|
||||
/* My Files / Friends Files Tables -> Cards */
|
||||
table.myfiles,
|
||||
table.myfiles tr,
|
||||
table.myfiles td,
|
||||
table.friendsfiles,
|
||||
table.friendsfiles tr,
|
||||
table.friendsfiles td {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table.myfiles th,
|
||||
table.friendsfiles th {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.myfiles tr,
|
||||
table.friendsfiles tr {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.5rem;
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Search Container Layout */
|
||||
.file-search-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.file-search-container__keywords {
|
||||
flex-basis: auto;
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid rgba(20, 20, 27, 0.1);
|
||||
padding-bottom: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Search results table -> cards */
|
||||
.results-container,
|
||||
.results-container thead,
|
||||
.results-container tbody,
|
||||
.results-container tr,
|
||||
.results-container td {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.results-container thead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.results-container tr {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.results-container td {
|
||||
margin-bottom: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* GLOBAL SIDEBAR TO TABS (v46) */
|
||||
.tab-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid rgba(20, 20, 27, 0.1);
|
||||
background: white;
|
||||
z-index: 50;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
display: inline-block;
|
||||
padding: 0.8rem 1.2rem;
|
||||
border-bottom: 3px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar .selected-sidebar-link {
|
||||
border-left: none;
|
||||
border-bottom: 3px solid #3ba4d7;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
/* Hide quickview headings on mobile to save space if needed */
|
||||
.sidebarquickview>h4 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* Search Results Responsive Layout (v60) */
|
||||
.results-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: var(--main-bg-color);
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.results-header {
|
||||
background: var(--secondary-bg-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.results-row {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 0.8rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.results-cell {
|
||||
padding: 0 0.5rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.name-col { flex: 3; }
|
||||
.size-col { flex: 1; text-align: center; }
|
||||
.hash-col { flex: 4; font-family: monospace; font-size: 0.8rem; opacity: 0.7; }
|
||||
.action-col { flex: 1; text-align: right; }
|
||||
|
||||
.file-item:hover {
|
||||
background: rgba(var(--primary-color-rgb), 0.05);
|
||||
}
|
||||
|
||||
/* Mobile Adjustments */
|
||||
@media screen and (max-width: 768px) {
|
||||
.results-header {
|
||||
display: none; /* Hide header on mobile */
|
||||
}
|
||||
|
||||
.results-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding: 1rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.results-cell {
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.name-col {
|
||||
font-weight: bold;
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.size-col {
|
||||
text-align: left;
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.hash-col {
|
||||
display: none; /* Hide hash on mobile as requested */
|
||||
}
|
||||
|
||||
.action-col {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.action-col button {
|
||||
width: 100%;
|
||||
padding: 0.8rem;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user