Merge pull request #122 from zelfroster/fix/webui-build-sources

This commit is contained in:
Sumit Kumar Soni 2026-08-07 06:28:01 +05:30 committed by GitHub
commit fb80060595
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 3140 additions and 1997 deletions

356
README.md
View File

@ -1,212 +1,206 @@
# Web Interface for Retroshare
# RetroShare Web Interface
A web-based frontend for [Retroshare](https://github.com/Retroshare/Retroshare)
which communicates with the client through the JSON API.
This project is the browser interface for
[RetroShare](https://github.com/RetroShare/RetroShare). It talks to RetroShare
through its JSON API.
## Requirements
Recent RetroShare releases already include this interface. Follow this guide if
you want to build it yourself, change it, or contribute to it.
- Retroshare v0.6.5+ with JSON API enabled(see instructions below)
- A modern JavaScript-enabled web browser
- [`qmake`](https://doc.qt.io/qt-5/qmake-manual.html)(optional)
## What you need
## Installation
- RetroShare 0.6.5 or newer, with the JSON API enabled
- A modern web browser
> **Note:** The Web Interface is shipped by default in the latest release of
> Retroshare. If you want to customise it or [contribute](#contributing) to it
> then proceed with the following steps.
## Set up RetroShare
### Install WebUI
### Enable the JSON API
First, you need to download and install the web interface javascript code
itself:
1. Open RetroShare.
2. Go to **Preferences > JSON API**.
3. Enable **RetroShare JSON API Server**.
1. **Clone the repo**:
You can clone using git, or download the zip file and extract it
### Enable the Web Interface
```bash
git clone https://github.com/Retroshare/RSNewWebUI
cd RSNewWebUI
```
1. Go to **Preferences > Web Interface**.
2. Enable the Web Interface.
3. Choose a password.
4. Set **Web interface directory** to this repository's `webui/` directory if
RetroShare does not find it automatically.
5. Click **Apply**.
2. **Build the files**:
If you have `qmake` installed, you just need to run it in the base directory:
The JSON API page should now show an authenticated token named
`webui:<your password>`.
```bash
qmake .
```
## Open the interface
If you do not have `qmake`, go to `webui-src/make-src/` and run the build
script. Note that qmake is enough to do the build.
Open [https://localhost:9092/index.html](https://localhost:9092/index.html) in
your browser. If you changed the JSON API port, replace `9092` with that port.
##### On Linux/MacOS:
### Connect to a remote or headless server
```bash
cd webui-src/make-src
sh build.sh
```
The Web Interface listens on localhost by default. To reach RetroShare on a
remote server, run this command on your local computer:
##### On Windows:
```sh
ssh -L 9092:localhost:9092 -N login@server
```
```bash
cd webui-src\make-src\
./build.bat
```
Keep that command running, then open
[https://localhost:9092/index.html](https://localhost:9092/index.html).
### Compile Retroshare with JSON API
The Web Interface cannot create a new RetroShare node. Create the node with the
desktop interface first, then copy its RetroShare data directory to the server.
On Linux this directory is usually `.retroshare/`.
If you are on older versions of Retroshare then it needs to be compiled with
non-default options as follows:
Start the service on the server with:
```bash
```sh
./retroshare-service/src/retroshare-service -U list -W
```
Follow the prompts to select the profile and enter its passwords.
## Contributing
To work on the source, you need:
- Node.js 24 or newer and npm
- `sh` on macOS or Linux; Windows uses the included batch file
- `qmake` only if you want to use RetroShare's qmake build integration
### First-time setup
Fork the repository, clone your fork, and install the locked dependencies:
```sh
git clone https://github.com/YOUR-USERNAME/RSNewWebUI.git
cd RSNewWebUI/webui-src
npm ci
```
### Where to make changes
- JavaScript source is in `webui-src/app/`.
- SCSS source is in `webui-src/app/scss/`.
- `webui-src/styles.css` is generated from `app/scss/main.scss`. Do not edit it
by hand, but commit it with the SCSS changes that generated it.
- Everything in `webui/` is build output. Do not edit those files by hand.
- `webui-src/app/mithril.js` contains Mithril 2.3.8. Change it only when
intentionally upgrading Mithril.
### Normal development commands
Run these commands from `webui-src/`:
```sh
npm run build
npm run lint
```
`npm run build` compiles the SCSS and creates the complete `webui/` directory.
It automatically uses `build.bat` on Windows and `build.sh` on macOS or Linux.
`npm run lint` checks the source files and does not require `webui/` to exist.
To rebuild CSS whenever an SCSS file changes, run:
```sh
npm run watch
```
Watch mode updates `webui-src/styles.css` only. Run `npm run build` when you
also need to update the files in `webui/`.
### Using qmake during development
Run qmake from the repository root:
```sh
qmake .
```
qmake copies the committed `webui-src/styles.css`; it does not compile SCSS.
After changing SCSS, run `npm run build` first. Rerun `qmake .` when you need
qmake to package newer WebUI files; `make` alone does not do that for this
project.
A standalone checkout may show a warning about a missing `../retroshare.pri`.
The WebUI files are still generated.
### Building without npm or qmake
These commands create `webui/` from the files already stored in the repository.
They do not compile SCSS.
From the repository root on macOS or Linux:
```sh
sh webui-src/make-src/build.sh
```
From the repository root on Windows:
```bat
webui-src\make-src\build.bat
```
### Optional focused builds
The normal `npm run build` command is the easiest choice. On macOS or Linux,
you can rebuild only one generated file from `webui-src/`:
```sh
# JavaScript only
sh make-src/build.sh "" app.js
# HTML only
sh make-src/build.sh "" index.html
# CSS only
./node_modules/.bin/sass --no-source-map --style=compressed app/scss/main.scss styles.css
sh make-src/build.sh "" styles.css
```
### Before opening a pull request
1. Run `npm run build`.
2. Run `npm run lint`.
3. Point RetroShare's **Web interface directory** to this repository's
`webui/` directory.
4. Manually test the screens you changed.
5. If you changed SCSS, include the regenerated `webui-src/styles.css`.
There is no separate formatting command.
### Building older RetroShare versions
If you are testing with an older RetroShare version, you may need to build it
with the JSON API and WebUI enabled:
```sh
qmake CONFIG+="debug rs_jsonapi rs_webui"
make
```
See the [RetroShare repo](https://github.com/Retroshare/Retroshare) for more
detailed instructions on compiling RetroShare. You should afterwards see a tab
'JSON API' and a tab 'Web Interface' in the **Preferences**.
See the [RetroShare repository](https://github.com/RetroShare/RetroShare) for
complete build instructions. After building, RetroShare preferences should
contain both **JSON API** and **Web Interface** pages.
### Enable JSON API
### Useful references
You need to enable the JSON API, through which the web interface communicates
with the client:
- [Mithril documentation](https://mithril.js.org/)
- [RetroShare source](https://github.com/RetroShare/RetroShare)
1. Open Retroshare, go to `Preferences > JSON API`.
2. Make sure the **Enable Retroshare JSON API Server** box is checked.
### Enable Web Interface
1. Go to `Preferences > Webinterface`.
2. Make sure the **Enable Retroshare WEB Interface** box is checked.
3. Enter a password to protect access to the web interface.
If necessary, point the **Web interface directory** to the place where the webui
files are compiled. This is usually `RSNewWebUI/webui/`.
In any case, click on "Apply settings" after making the changes. If everything
goes ok, you should see a new token `webui:[your password]` under the
**Authenticated Tokens** section in the **JSON API** preferences page.
## Usage
### Basic Usage
This is the default link to access the WebInterface.
<br>
Open this link your browser ->
[https://localhost:9092/index.html](https://localhost:9092/index.html).
> Note: If you changed the port in the JSON API preferences pages, the port
> in the above line needs to be changed accordingly.
### Advanced Usage
The Web interface is only accessible from localhost (127.0.0.1). If you want to
access the web interface of a headless retroshare server, then you need to
create a SSH tunnel as follows:
```
ssh login@server -L 9092:localhost:9092 -N
```
After that, the Web interface of the Retroshare running on 'server' is tunneled
to your local machine and accessible through localhost:9092.
Running a headless retroshare server is one possibility. The Webinterface
however does not allow you to create new nodes. Therefore the steps are:
1. Create a node using the standard Qt UI. That can be done in another machine.
2. Copy the retroshare data directory (.retroshare/ on linux) on the server.
3. On the server, launch a headless retroshare using that node:
```
./retroshare-service/src/retroshare-service -U list -W
```
After that follow instructions to launch your profile (you need to choose a
webui password and enter the ID and login password of your node).
## Contributing
For contributing, It is recommended that you read this entire section to have a
better idea.
### Setup WebUI for contributing
Follow these steps to setup the project and make it ready for
contribution/customisation :
- Fork and Clone this repository to your local machine.
- `cd` into the cloned repo:
```
cd RSNewWebUI
```
- Run this command to install the dependecies for the project.
```
pnpm install
```
### Run the WebUI
- Run this command to watch for any changes in the `scss` files and compile them
to css.
```
pnpm watch
```
- You can now start to edit the source code. But you must run the below command
to see the changes reflected in the browser everytime you edit the code for
webui which is in the `webui-src/app/` directory.
```
qmake .
```
### Linting and Formatting
Linting and formatting can be done with editor/IDE plugins.
Or to do it manually,
Install `prettier` and `eslint` (required for linting & formatting code):
To find RetroShare headers that expose JSON API methods, run this from
`libretroshare/src/retroshare/` in the RetroShare source tree:
```sh
npm install -g prettier eslint
grep -c "@jsonapi" *.h | grep -v ":0"
```
Next, run the following in the `webui-src` directory:
## Get help or report a problem
To run the linter: `eslint app`
To run the formatter: `prettier -c app`
### References
Now, While contributing you can checkout these resources as you might need to
look up for these often.
- [mithril](https://mithril.js.org/hyperscript.html)
- You can list files with @jsonapi in libretroshare/src/retroshare of
[retroshare](https://github.com/RetroShare/RetroShare):
```
grep -c "@jsonapi" *.h|grep -v ":0"
```
<hr>
And, that's it. You are more than welcome to contribute to this project. If you
have any questions/difficulties in setting up or running the project, you can
raise an issue and we will be more than willing to help you out.
### Bug Reports & Feature requests
Please create an [issue](https://github.com/Retroshare/RsNewWebUI/issues)
concisely describing the bug you faced, or the feature you would like to see
implemented.
### Development
Whether you are a JavaScript developer or a Web designer, you can help make the
web interface better. Get in touch with us on the Developer forums in
Retroshare.
Open a [GitHub issue](https://github.com/RetroShare/RSNewWebUI/issues) and
briefly explain what happened, what you expected, and how someone can reproduce
it. You can also join the RetroShare developer forums to discuss development.

View File

@ -1,57 +0,0 @@
{
"root": true,
"env": {
"browser": true,
"commonjs": true,
"es6": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly",
"window": "readonly",
"document": "readonly"
},
"parserOptions": {
"ecmaVersion": 2020
},
"ignorePatterns": ["mithril.js", "assets/*", "make-src/*"],
"rules": {
"linebreak-style": ["off", "unix"],
"quotes": ["error", "single"],
"semi": ["error", "always"],
"arrow-spacing": ["error", { "before": true, "after": true }],
"arrow-parens": ["error", "always"],
"comma-style": ["error", "last"],
"comma-spacing": ["error", { "before": false, "after": true }],
"camelcase": [
"error",
{
"allow": ["^UNSAFE_"],
"properties": "never",
"ignoreGlobals": true
}
],
"eol-last": "error",
"eqeqeq": ["error", "always", { "null": "ignore" }],
"no-irregular-whitespace": ["error"],
"no-trailing-spaces": ["error"],
"no-unexpected-multiline": ["error"],
"no-unreachable": ["error"],
"no-var": "warn",
"no-unused-vars": [
"error",
{
"args": "none",
"caughtErrors": "none",
"ignoreRestSiblings": true,
"vars": "all"
}
],
"no-use-before-define": ["warn", { "functions": true, "classes": false }],
"object-shorthand": "error",
"prefer-const": ["error", { "destructuring": "all" }],
"semi-style": ["warn", "last"],
"spaced-comment": ["warn", "always"]
}
}

1
webui-src/.nvmrc Normal file
View File

@ -0,0 +1 @@
v24.15.0

View File

@ -39,7 +39,7 @@ function createboard() {
m('label[for=thumbnail]', 'Thumbnail: '),
m('input[type=file][name=files][id=thumbnail][accept=image/*]', {
onchange: async (e) => {
let reader = new FileReader();
const reader = new FileReader();
reader.onloadend = function () {
thumbnail = reader.result.substring(reader.result.indexOf(',') + 1);
};

View File

@ -1,17 +1,17 @@
const m = require('mithril');
const rs = require('rswebui');
const GROUP_SUBSCRIBE_ADMIN = 0x01;// means: you have the admin key for this group
const GROUP_SUBSCRIBE_PUBLISH = 0x02;// means: you have the publish key for thiss group. Typical use: publish key in channels are shared with specific friends.
const GROUP_SUBSCRIBE_SUBSCRIBED = 0x04;// means: you are subscribed to a group, which makes you a source for this group to your friend nodes.
const GROUP_SUBSCRIBE_ADMIN = 0x01; // means: you have the admin key for this group
const GROUP_SUBSCRIBE_PUBLISH = 0x02; // means: you have the publish key for thiss group. Typical use: publish key in channels are shared with specific friends.
const GROUP_SUBSCRIBE_SUBSCRIBED = 0x04; // means: you are subscribed to a group, which makes you a source for this group to your friend nodes.
const GROUP_SUBSCRIBE_NOT_SUBSCRIBED = 0x08;
const GROUP_MY_BOARD = GROUP_SUBSCRIBE_ADMIN + GROUP_SUBSCRIBE_SUBSCRIBED + GROUP_SUBSCRIBE_PUBLISH;
const GXS_VOTE_DOWN = 0x0001;
const GXS_VOTE_UP = 0x0002;
//rsgxscircles.h:50
const PUBLIC = 1; /// Public distribution
const EXTERNAL = 2; /// Restricted to an external circle, based on GxsIds
// rsgxscircles.h:50
const PUBLIC = 1; // Public distribution
const EXTERNAL = 2; // Restricted to an external circle, based on GxsIds
const NODES_GROUP = 3;
const Data = {
@ -20,34 +20,6 @@ const Data = {
Comments: {}, // threadID, msgID -> {Comment, showReplies}
};
async function updateContent(content, boardid) {
const res = await rs.rsJsonApiRequest('/rsPosted/getBoardContent', {
boardId: boardid,
contentsIds: [content.mMsgId],
});
if (res.body.retval && res.body.posts.length > 0) {
Data.Posts[boardid][content.mMsgId] = { post: res.body.posts[0], isSearched: true };
} else if (res.body.retval && res.body.comments.length > 0) {
if (Data.Comments[content.mThreadId] === undefined) {
Data.Comments[content.mThreadId] = {};
}
Data.Comments[content.mThreadId][content.mMsgId] = res.body.comments[0];
} else if (res.body.retval && res.body.votes.length > 0) {
const vote = res.body.votes[0];
if (
Data.Comments[vote.mMeta.mThreadId] &&
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId]
) {
if (vote.mVoteType === GXS_VOTE_UP) {
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].mUpVotes += 1;
}
if (vote.mVoteType === GXS_VOTE_DOWN) {
Data.Comments[vote.mMeta.mThreadId][vote.mMeta.mParentId].mDownVotes += 1;
}
}
}
}
async function updateDisplayBoards(keyid, details) {
const res1 = await rs.rsJsonApiRequest('/rsPosted/getBoardsInfo', {
boardsIds: [keyid],
@ -72,7 +44,7 @@ async function updateDisplayBoards(keyid, details) {
Data.Posts[keyid] = {};
}
/*const res2 = await rs.rsJsonApiRequest('/rsPosted/getContentSummaries', {
/* const res2 = await rs.rsJsonApiRequest('/rsPosted/getContentSummaries', {
boardId: keyid,
});
@ -81,7 +53,6 @@ async function updateDisplayBoards(keyid, details) {
updateContent(content, keyid);
});
}*/
}
const DisplayBoardsFromList = () => {

View File

@ -109,7 +109,7 @@ function createchannel() {
m('label[for=thumbnail]', 'Thumbnail: '),
m('input[type=file][name=files][id=thumbnail][accept=image/*]', {
onchange: async (e) => {
let reader = new FileReader();
const reader = new FileReader();
reader.onloadend = function () {
thumbnail = reader.result.substring(reader.result.indexOf(',') + 1);
};
@ -238,7 +238,7 @@ const AddPost = () => {
m('label[for=thumbnail]', 'Thumbnail: '),
m('input[type=file][name=files][id=thumbnail][accept=image/*]', {
onchange: async (e) => {
let reader = new FileReader();
const reader = new FileReader();
reader.onloadend = function () {
pthumbnail = reader.result.substring(reader.result.indexOf(',') + 1);
};

View File

@ -7,18 +7,11 @@ const chatEmoji = require('chat/chat_emoji');
const HistoryBrowserModal = require('people/people_history');
const {
get64Num,
loadLobbyDetails,
loadDistantChatDetails,
sortLobbies,
getNicknameColor,
getStatusColor,
getStatusTooltip,
renderTextWithEmoji,
getSafeAvatar,
MobileState,
ChatRoomsModel,
Message,
ChatLobbyModel,
ChatHubState,
} = chatState;
@ -123,13 +116,13 @@ function pollHashStatus(localpath) {
const info = data.info;
const sizeNum = info.size.xint64 || parseInt(info.size.xstr64) || info.size;
const fileLink = `<a href="retroshare://file?name=${encodeURIComponent(info.name)}&size=${sizeNum}&hash=${info.hash}">${info.name}</a> (${rs.formatBytes(sizeNum)})`;
const textarea = document.querySelector('.chat-hub-textarea');
if (textarea) {
const val = textarea.value;
textarea.value = val ? val + '\n' + fileLink : fileLink;
}
ChatHubState.showAttachModal = false;
ChatHubState.isHashing = false;
ChatHubState.attachPath = '';
@ -144,75 +137,6 @@ function pollHashStatus(localpath) {
// ************************* views ****************************
const Lobby = () => {
return {
view: (vnode) => {
const { info, tagname, onclick, lobbytagname = 'mainname' } = vnode.attrs;
return m(
ChatLobbyModel.selected(info, '.selected-lobby', tagname),
{
key: rs.idToHex(info.lobby_id),
onclick,
},
[
m('h5', { class: lobbytagname }, info.lobby_name === '' ? '<unnamed>' : info.lobby_name),
m('.topic', info.lobby_topic),
]
);
},
};
};
const LobbyList = {
view(vnode) {
const tagname = vnode.attrs.tagname;
const lobbytagname = vnode.attrs.lobbytagname;
const onclick = vnode.attrs.onclick || (() => null);
return [
vnode.attrs.rooms.map((info) =>
m(Lobby, {
info,
tagname,
lobbytagname,
onclick: onclick(info),
})
),
];
},
};
const SubscribedLobbies = {
view() {
return m('.widget', [
m('.widget__heading', m('h3', 'Subscribed chat rooms')),
m('.widget__body', [
m(LobbyList, {
rooms: sortLobbies(Object.values(ChatRoomsModel.subscribedRooms)),
tagname: '.lobby.subscribed',
onclick: ChatLobbyModel.switchToEvent,
}),
]),
]);
},
};
const PublicLobbies = {
view() {
return m('.widget', [
m('.widget__heading', m('h3', 'Public chat rooms')),
m('.widget__body', [
m(LobbyList, {
rooms: (ChatRoomsModel.allRooms || []).filter((info) => !ChatRoomsModel.subscribed(info)),
tagname: '.lobby.public',
onclick: ChatLobbyModel.setupEvent,
}),
]),
]);
},
};
// ************************* Chat Hub Sub-Components ****************************
const ChatRoomHeader = () => {
return {
view: (vnode) => {
@ -982,8 +906,6 @@ const ChatRoomDetailView = () => {
const room = ChatHubState.selectedRoom;
if (!room) return null;
let participantCount = 0;
let participantNames = [];
let participants = [];
if (room.gxs_ids) {
@ -994,7 +916,7 @@ const ChatRoomDetailView = () => {
}));
} else if (typeof room.gxs_ids === 'object') {
participants = Object.keys(room.gxs_ids).map((key) => ({
key: key,
key,
name: rs.userList.username(key) || key
}));
}
@ -1011,8 +933,8 @@ const ChatRoomDetailView = () => {
}
}
participantCount = participants.length;
participantNames = participants.map((p) => p.name);
const participantCount = participants.length;
const participantNames = participants.map((p) => p.name);
participantNames.sort((a, b) => a.localeCompare(b));
const lobbyHexId = rs.idToHex(room.lobby_id);
@ -1316,7 +1238,7 @@ const Layout = {
ChatHubState.showCreateRoomModal && m('.attach-modal-overlay', [
m('.attach-modal', [
m('h4', 'Create New Chat Room'),
m('.form-field', { style: 'display: flex; flex-direction: column; gap: 0.25rem;' }, [
m('label', { style: 'font-weight: bold; font-size: 0.9rem; color: #475569;' }, 'Room Name:'),
m('input[type=text]', {
@ -1326,7 +1248,7 @@ const Layout = {
style: 'padding: 0.5rem; border: 1px solid #cbd5e1; border-radius: 0.25rem; font-size: 0.9rem;'
})
]),
m('.form-field', { style: 'display: flex; flex-direction: column; gap: 0.25rem; margin-top: 0.5rem;' }, [
m('label', { style: 'font-weight: bold; font-size: 0.9rem; color: #475569;' }, 'Topic:'),
m('input[type=text]', {
@ -1344,7 +1266,7 @@ const Layout = {
onchange: (e) => { ChatHubState.newRoomIdentity = e.target.value; },
style: 'padding: 0.5rem; border: 1px solid #cbd5e1; border-radius: 0.25rem; font-size: 0.9rem; background-color: #ffffff;'
}, [
ChatHubState.ownGxsIdentities && ChatHubState.ownGxsIdentities.map(id => {
ChatHubState.ownGxsIdentities && ChatHubState.ownGxsIdentities.map((id) => {
const details = ChatHubState.gxsDetails[id];
const name = details ? (details.mNickname || details.mGroupName) : id;
return m('option', { value: id }, name);
@ -1386,7 +1308,7 @@ const Layout = {
let flags = 0;
if (isPublic) flags |= 4;
if (isSigned) flags |= 8;
rs.rsJsonApiRequest('/rsChats/createChatLobby', {
lobby_name: name,
lobby_identity: identity,
@ -1601,74 +1523,6 @@ const Layout = {
},
};
const LayoutSingle = () => {
const onResize = () => {
const element = document.querySelector('.messages');
if (element) element.scrollTop = element.scrollHeight;
};
return {
oninit: () => {
ChatLobbyModel.loadLobby(m.route.param('lobby'));
window.addEventListener('resize', onResize);
},
onremove: () => window.removeEventListener('resize', onResize),
view: (vnode) => {
const chatType = ChatLobbyModel.currentLobby.chatType;
const isPrivate = chatType === 1 || chatType === 2;
const isRoom = chatType === 3;
return m(
'.node-panel.chat-panel.chat-room',
{
class:
(MobileState.showLobbies ? 'show-lobbies ' : '') +
(MobileState.showUsers ? 'show-users ' : '') +
(isPrivate ? 'no-lobbies' : ''),
},
[
m('.chat-overlay', { onclick: () => MobileState.closeAll() }),
m(
'.messages' + (isRoom ? '.compact-container' : ''),
{ onclick: () => MobileState.closeAll() },
ChatLobbyModel.messages
),
m(
'.chatMessage',
{},
[
m('textarea.chatMsg', {
placeholder: 'Type a message...',
enterkeyhint: 'send',
onkeydown: (e) => {
if ((e.key === 'Enter' || e.keyCode === 13) && !e.shiftKey) {
const msg = e.target.value;
if (msg.trim() === '') return false;
e.target.value = ' sending ... ';
ChatLobbyModel.sendMessage(msg, () => (e.target.value = ''));
return false;
}
},
}),
m(
'button.chat-send-btn',
{
onclick: (e) => {
const textarea = e.target.closest('.chatMessage').querySelector('textarea');
const msg = textarea.value;
if (msg.trim() === '') return;
textarea.value = ' sending ... ';
ChatLobbyModel.sendMessage(msg, () => (textarea.value = ''));
},
},
m('i.fas.fa-paper-plane')
),
]
),
]
);
},
};
};
/*
/rsChats/initiateDistantChatConnexion
* @param[in] to_pid RsGxsId to start the connection

View File

@ -7,72 +7,72 @@ const EMOJI_ICONS = {
};
const EMOJI_DATA = {
Smileys: [
'😀','😁','😂','🤣','😃','😄','😅','😆','😉','😊','😋','😎','😍','😘','🥰','😗','😙','😚',
'🙂','🤗','🤩','🤔','🤨','😐','😑','😶','🙄','😏','😣','😥','😮','🤐','😯','😪','😫','🥱',
'😴','😌','😛','😜','😝','🤤','😒','😓','😔','😕','🙃','🤑','😲','☹️','🙁','😖','😞','😟',
'😤','😢','😭','😦','😧','😨','😩','🤯','😬','😰','😱','🥵','🥶','😳','🤪','😵','😡','😠',
'🤬','😷','🤒','🤕','🤢','🤮','🤧','🥴','😇','🥳','🥺','🤠','🤡','🤥','🤫','🤭','🧐','🤓',
'😈','👿','👹','👺','💀','☠️','👻','👽','👾','🤖','😺','😸','😹','😻','😼','😽','🙀','😿','😾',
'😀', '😁', '😂', '🤣', '😃', '😄', '😅', '😆', '😉', '😊', '😋', '😎', '😍', '😘', '🥰', '😗', '😙', '😚',
'🙂', '🤗', '🤩', '🤔', '🤨', '😐', '😑', '😶', '🙄', '😏', '😣', '😥', '😮', '🤐', '😯', '😪', '😫', '🥱',
'😴', '😌', '😛', '😜', '😝', '🤤', '😒', '😓', '😔', '😕', '🙃', '🤑', '😲', '☹️', '🙁', '😖', '😞', '😟',
'😤', '😢', '😭', '😦', '😧', '😨', '😩', '🤯', '😬', '😰', '😱', '🥵', '🥶', '😳', '🤪', '😵', '😡', '😠',
'🤬', '😷', '🤒', '🤕', '🤢', '🤮', '🤧', '🥴', '😇', '🥳', '🥺', '🤠', '🤡', '🤥', '🤫', '🤭', '🧐', '🤓',
'😈', '👿', '👹', '👺', '💀', '☠️', '👻', '👽', '👾', '🤖', '😺', '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾',
],
People: [
'👋','🤚','🖐️','✋','🖖','👌','🤌','🤏','✌️','🤞','🤟','🤘','🤙','👈','👉','👆','🖕','👇',
'☝️','👍','👎','✊','👊','🤛','🤜','👏','🙌','👐','🤲','🤝','🙏','✍️','💅','🤳','💪','🦾',
'🦿','🦵','🦶','👂','🦻','👃','🫀','🫁','🧠','🦷','🦴','👀','👁️','👅','👄','🫦','👶','🧒',
'👦','👧','🧑','👱','👨','🧔','👩','🧓','👴','👵','🙍','🙎','🙅','🙆','💁','🙋','🧏','🙇',
'🤦','🤷','👮','🕵️','💂','🥷','👷','🫅','🤴','👸','👲','🧕','🤵','👰','🤰','🫃','🫄','🤱',
'👼','🎅','🤶','🧑‍🎄','🦸','🦹','🧙','🧝','🧛','🧟','🧞','🧜','🧚','🧑‍🤝‍🧑','👫','👬','👭','💏','💑','👪',
'👋', '🤚', '🖐️', '✋', '🖖', '👌', '🤌', '🤏', '✌️', '🤞', '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇',
'☝️', '👍', '👎', '✊', '👊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝', '🙏', '✍️', '💅', '🤳', '💪', '🦾',
'🦿', '🦵', '🦶', '👂', '🦻', '👃', '🫀', '🫁', '🧠', '🦷', '🦴', '👀', '👁️', '👅', '👄', '🫦', '👶', '🧒',
'👦', '👧', '🧑', '👱', '👨', '🧔', '👩', '🧓', '👴', '👵', '🙍', '🙎', '🙅', '🙆', '💁', '🙋', '🧏', '🙇',
'🤦', '🤷', '👮', '🕵️', '💂', '🥷', '👷', '🫅', '🤴', '👸', '👲', '🧕', '🤵', '👰', '🤰', '🫃', '🫄', '🤱',
'👼', '🎅', '🤶', '🧑‍🎄', '🦸', '🦹', '🧙', '🧝', '🧛', '🧟', '🧞', '🧜', '🧚', '🧑‍🤝‍🧑', '👫', '👬', '👭', '💏', '💑', '👪',
],
Animals: [
'🐶','🐱','🐭','🐹','🐰','🦊','🐻','🐼','🐻‍❄️','🐨','🐯','🦁','🐮','🐷','🐸','🐵','🙈','🙉',
'🙊','🐒','🦆','🦅','🦉','🦇','🐝','🪱','🐛','🦋','🐌','🐞','🐜','🪲','🦗','🪳','🕷️','🦂',
'🐢','🐍','🦎','🦖','🦕','🐙','🦑','🦐','🦞','🦀','🐡','🐠','🐟','🐬','🐳','🐋','🦈','🦭',
'🐊','🐅','🐆','🦓','🦍','🦧','🦣','🐘','🦛','🦏','🐪','🐫','🦒','🦘','🦬','🐃','🐂','🐄',
'🐎','🐖','🐏','🐑','🦙','🐐','🦌','🐕','🐩','🦮','🐕‍🦺','🐈','🐈‍⬛','🐓','🦃','🦤','🦚','🦜',
'🦢','🦩','🕊️','🐇','🦝','🦨','🦡','🦫','🦦','🦥','🐁','🐀','🐿️','🦔','🐾','🐉','🐲','🌵',
'🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐻‍❄️', '🐨', '🐯', '🦁', '🐮', '🐷', '🐸', '🐵', '🙈', '🙉',
'🙊', '🐒', '🦆', '🦅', '🦉', '🦇', '🐝', '🪱', '🐛', '🦋', '🐌', '🐞', '🐜', '🪲', '🦗', '🪳', '🕷️', '🦂',
'🐢', '🐍', '🦎', '🦖', '🦕', '🐙', '🦑', '🦐', '🦞', '🦀', '🐡', '🐠', '🐟', '🐬', '🐳', '🐋', '🦈', '🦭',
'🐊', '🐅', '🐆', '🦓', '🦍', '🦧', '🦣', '🐘', '🦛', '🦏', '🐪', '🐫', '🦒', '🦘', '🦬', '🐃', '🐂', '🐄',
'🐎', '🐖', '🐏', '🐑', '🦙', '🐐', '🦌', '🐕', '🐩', '🦮', '🐕‍🦺', '🐈', '🐈‍⬛', '🐓', '🦃', '🦤', '🦚', '🦜',
'🦢', '🦩', '🕊️', '🐇', '🦝', '🦨', '🦡', '🦫', '🦦', '🦥', '🐁', '🐀', '🐿️', '🦔', '🐾', '🐉', '🐲', '🌵',
],
Food: [
'🍎','🍊','🍋','🍌','🍍','🥭','🍓','🍒','🍑','🥝','🍅','🥥','🥑','🍆','🥔','🥕','🌽','🌶️',
'🫑','🥒','🥬','🥦','🧄','🧅','🍄','🥜','🌰','🍞','🥐','🥖','🫓','🥨','🧀','🥚','🍳','🧈',
'🥞','🧇','🥓','🥩','🍗','🍖','🦴','🌭','🍔','🍟','🍕','🫔','🌮','🌯','🥙','🧆','🥚','🍱',
'🍘','🍙','🍚','🍛','🍜','🍝','🍠','🍢','🍣','🍤','🍥','🥮','🍡','🥟','🥠','🥡','🦪','🍦',
'🍧','🍨','🍩','🍪','🎂','🍰','🧁','🥧','🍫','🍬','🍭','🍮','🍯','🍼','🥛','☕','🫖','🍵',
'🧃','🥤','🧋','🍶','🍺','🍻','🥂','🍷','🥃','🍸','🍹','🧉','🍾','🧊','🥄','🍴','🍽️','🥢',
'🍎', '🍊', '🍋', '🍌', '🍍', '🥭', '🍓', '🍒', '🍑', '🥝', '🍅', '🥥', '🥑', '🍆', '🥔', '🥕', '🌽', '🌶️',
'🫑', '🥒', '🥬', '🥦', '🧄', '🧅', '🍄', '🥜', '🌰', '🍞', '🥐', '🥖', '🫓', '🥨', '🧀', '🥚', '🍳', '🧈',
'🥞', '🧇', '🥓', '🥩', '🍗', '🍖', '🦴', '🌭', '🍔', '🍟', '🍕', '🫔', '🌮', '🌯', '🥙', '🧆', '🥚', '🍱',
'🍘', '🍙', '🍚', '🍛', '🍜', '🍝', '🍠', '🍢', '🍣', '🍤', '🍥', '🥮', '🍡', '🥟', '🥠', '🥡', '🦪', '🍦',
'🍧', '🍨', '🍩', '🍪', '🎂', '🍰', '🧁', '🥧', '🍫', '🍬', '🍭', '🍮', '🍯', '🍼', '🥛', '☕', '🫖', '🍵',
'🧃', '🥤', '🧋', '🍶', '🍺', '🍻', '🥂', '🍷', '🥃', '🍸', '🍹', '🧉', '🍾', '🧊', '🥄', '🍴', '🍽️', '🥢',
],
Travel: [
'🚗','🚕','🚙','🚌','🚎','🏎️','🚓','🚑','🚒','🚐','🛻','🚚','🚛','🚜','🦯','🦽','🦼','🛺',
'🚲','🛴','🛵','🏍️','🛺','🚨','🚔','🚍','🚘','🚖','🚡','🚠','🚟','🚃','🚋','🚞','🚝','🚄',
'🚅','🚈','🚂','🚆','🚇','🚊','🚉','✈️','🛫','🛬','🛩️','💺','🛸','🚁','🛶','⛵','🚤','🛥️',
'🛳️','⛴️','🚢','⚓','🗺️','🧭','🏔️','⛰️','🌋','🗻','🏕️','🏖️','🏜️','🏝️','🏞️','🏟️','🏛️','🏗️',
'🧱','🪨','🪵','🛖','🏘️','🏚️','🏠','🏡','🏢','🏣','🏤','🏥','🏦','🏨','🏩','🏪','🏫','🏬',
'🏭','🏯','🏰','💒','🗼','🗽','⛪','🕌','🛕','🕍','⛩️','🕋','⛲','⛺','🌁','🌃','🏙️','🌄',
'🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐', '🛻', '🚚', '🚛', '🚜', '🦯', '🦽', '🦼', '🛺',
'🚲', '🛴', '🛵', '🏍️', '🛺', '🚨', '🚔', '🚍', '🚘', '🚖', '🚡', '🚠', '🚟', '🚃', '🚋', '🚞', '🚝', '🚄',
'🚅', '🚈', '🚂', '🚆', '🚇', '🚊', '🚉', '✈️', '🛫', '🛬', '🛩️', '💺', '🛸', '🚁', '🛶', '⛵', '🚤', '🛥️',
'🛳️', '⛴️', '🚢', '⚓', '🗺️', '🧭', '🏔️', '⛰️', '🌋', '🗻', '🏕️', '🏖️', '🏜️', '🏝️', '🏞️', '🏟️', '🏛️', '🏗️',
'🧱', '🪨', '🪵', '🛖', '🏘️', '🏚️', '🏠', '🏡', '🏢', '🏣', '🏤', '🏥', '🏦', '🏨', '🏩', '🏪', '🏫', '🏬',
'🏭', '🏯', '🏰', '💒', '🗼', '🗽', '⛪', '🕌', '🛕', '🕍', '⛩️', '🕋', '⛲', '⛺', '🌁', '🌃', '🏙️', '🌄',
],
Activities: [
'⚽','🏀','🏈','⚾','🥎','🎾','🏐','🏉','🥏','🎱','🏓','🏸','🏒','🏑','🥍','🏏','🪃','🥅',
'⛳','🪁','🛝','🏹','🎣','🤿','🥊','🥋','🎽','🛹','🛷','⛸️','🥌','🎿','⛷️','🏂','🪂','🏋️',
'🤼','🤸','⛹️','🤺','🏇','🧘','🏄','🏊','🤽','🚣','🧗','🚵','🚴','🏆','🥇','🥈','🥉','🏅',
'🎖️','🏵️','🎗️','🎫','🎟️','🎪','🤹','🎭','🩰','🎨','🖼️','🎰','🎲','🧩','🪄','🎯','🪅','🎮',
'🕹️','🎳','🎻','🎷','🥁','🪘','🎺','🎸','🪗','🎹','🎵','🎶','🎼','🎤','🎧','📻','🎙️','🎚️',
'🎬','📽️','🎞️','📱','📲','☎️','📞','📟','📠','🔋','🪫','🔌','💡','🔦','🕯️','💸','💵','🪙',
'⚽', '🏀', '🏈', '⚾', '🥎', '🎾', '🏐', '🏉', '🥏', '🎱', '🏓', '🏸', '🏒', '🏑', '🥍', '🏏', '🪃', '🥅',
'⛳', '🪁', '🛝', '🏹', '🎣', '🤿', '🥊', '🥋', '🎽', '🛹', '🛷', '⛸️', '🥌', '🎿', '⛷️', '🏂', '🪂', '🏋️',
'🤼', '🤸', '⛹️', '🤺', '🏇', '🧘', '🏄', '🏊', '🤽', '🚣', '🧗', '🚵', '🚴', '🏆', '🥇', '🥈', '🥉', '🏅',
'🎖️', '🏵️', '🎗️', '🎫', '🎟️', '🎪', '🤹', '🎭', '🩰', '🎨', '🖼️', '🎰', '🎲', '🧩', '🪄', '🎯', '🪅', '🎮',
'🕹️', '🎳', '🎻', '🎷', '🥁', '🪘', '🎺', '🎸', '🪗', '🎹', '🎵', '🎶', '🎼', '🎤', '🎧', '📻', '🎙️', '🎚️',
'🎬', '📽️', '🎞️', '📱', '📲', '☎️', '📞', '📟', '📠', '🔋', '🪫', '🔌', '💡', '🔦', '🕯️', '💸', '💵', '🪙',
],
Objects: [
'⌚','📱','📲','💻','⌨️','🖥️','🖨️','🖱️','🖲️','💾','💿','📀','🧮','📷','📸','📹','🎥','📽️',
'📞','☎️','📟','📠','📺','📻','🧭','⏱️','⏲️','⏰','🕰️','⌛','⏳','📡','🔋','🪫','🔌','💡',
'🔦','🕯️','🪔','🧱','💰','💴','💵','💶','💷','💸','💳','🪙','💹','✉️','📧','📨','📩','📤',
'📥','📦','📫','📪','📬','📭','📮','🗳️','✏️','✒️','🖊️','🖋️','📝','📁','📂','🗂️','📅','📆',
'🗒️','🗓️','📇','📈','📉','📊','📋','📌','📍','🗺️','📏','📐','✂️','🗃️','🗄️','🗑️','🔒','🔓',
'🔏','🔐','🔑','🗝️','🔨','🪓','⛏️','⚒️','🛠️','🗡️','⚔️','🔫','🪃','🏹','🛡️','🪚','🔧','🪛',
'⌚', '📱', '📲', '💻', '⌨️', '🖥️', '🖨️', '🖱️', '🖲️', '💾', '💿', '📀', '🧮', '📷', '📸', '📹', '🎥', '📽️',
'📞', '☎️', '📟', '📠', '📺', '📻', '🧭', '⏱️', '⏲️', '⏰', '🕰️', '⌛', '⏳', '📡', '🔋', '🪫', '🔌', '💡',
'🔦', '🕯️', '🪔', '🧱', '💰', '💴', '💵', '💶', '💷', '💸', '💳', '🪙', '💹', '✉️', '📧', '📨', '📩', '📤',
'📥', '📦', '📫', '📪', '📬', '📭', '📮', '🗳️', '✏️', '✒️', '🖊️', '🖋️', '📝', '📁', '📂', '🗂️', '📅', '📆',
'🗒️', '🗓️', '📇', '📈', '📉', '📊', '📋', '📌', '📍', '🗺️', '📏', '📐', '✂️', '🗃️', '🗄️', '🗑️', '🔒', '🔓',
'🔏', '🔐', '🔑', '🗝️', '🔨', '🪓', '⛏️', '⚒️', '🛠️', '🗡️', '⚔️', '🔫', '🪃', '🏹', '🛡️', '🪚', '🔧', '🪛',
],
Symbols: [
'❤️','🧡','💛','💚','💙','💜','🖤','🤍','🤎','💔','❣️','💕','💞','💓','💗','💖','💘','💝',
'💟','☮️','✝️','☪️','🕉️','☸️','✡️','🔯','🕎','☯️','☦️','🛐','⛎','♈','♉','♊','♋','♌',
'♍','♎','♏','♐','♑','♒','♓','🆔','⚛️','🉑','☢️','☣️','📴','📳','🈶','🈚','🈸','🈺',
'🈷️','✴️','🆚','💮','🉐','㊙️','㊗️','🈴','🈵','🈹','🈲','🅰️','🅱️','🆎','🆑','🅾️','🆘',
'❌','⭕','🛑','⛔','📛','🚫','💯','💢','♨️','🚷','🚯','🚳','🚱','🔞','📵','🚭','❗','❕',
'❓','❔','‼️','⁉️','🔅','🔆','📶','🛜','📳','📴','🔱','📛','🔰','♻️','✅','🈯','💹','❎',
'🌐','💠','Ⓜ️','🌀','💤','🏧','🚾','♿','🅿️','🛗','🈳','🈹','🚰','🔤','🔡','🔠','🆖','🆗',
'🆙','🆒','🆕','🆓','🔟','📊','🔣','✔️','☑️','🔘','🔲','🔳','⬛','⬜','◼️','◻️','◾','◽',
'▪️','▫️','🔶','🔷','🔸','🔹','🔺','🔻','💠','🔘','🔲','🔳','🏁','🚩','🎌','🏴','🏳️','⭐',
'🌟','💫','✨','🌈','☀️','🌤️','⛅','🌥️','☁️','🌦️','🌧️','⛈️','🌩️','🌨️','❄️','☃️','⛄','🌬️',
'❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔', '❣️', '💕', '💞', '💓', '💗', '💖', '💘', '💝',
'💟', '☮️', '✝️', '☪️', '🕉️', '☸️', '✡️', '🔯', '🕎', '☯️', '☦️', '🛐', '⛎', '♈', '♉', '♊', '♋', '♌',
'♍', '♎', '♏', '♐', '♑', '♒', '♓', '🆔', '⚛️', '🉑', '☢️', '☣️', '📴', '📳', '🈶', '🈚', '🈸', '🈺',
'🈷️', '✴️', '🆚', '💮', '🉐', '㊙️', '㊗️', '🈴', '🈵', '🈹', '🈲', '🅰️', '🅱️', '🆎', '🆑', '🅾️', '🆘',
'❌', '⭕', '🛑', '⛔', '📛', '🚫', '💯', '💢', '♨️', '🚷', '🚯', '🚳', '🚱', '🔞', '📵', '🚭', '❗', '❕',
'❓', '❔', '‼️', '⁉️', '🔅', '🔆', '📶', '🛜', '📳', '📴', '🔱', '📛', '🔰', '♻️', '✅', '🈯', '💹', '❎',
'🌐', '💠', 'Ⓜ️', '🌀', '💤', '🏧', '🚾', '♿', '🅿️', '🛗', '🈳', '🈹', '🚰', '🔤', '🔡', '🔠', '🆖', '🆗',
'🆙', '🆒', '🆕', '🆓', '🔟', '📊', '🔣', '✔️', '☑️', '🔘', '🔲', '🔳', '⬛', '⬜', '◼️', '◻️', '◾', '◽',
'▪️', '▫️', '🔶', '🔷', '🔸', '🔹', '🔺', '🔻', '💠', '🔘', '🔲', '🔳', '🏁', '🚩', '🎌', '🏴', '🏳️', '⭐',
'🌟', '💫', '✨', '🌈', '☀️', '🌤️', '⛅', '🌥️', '☁️', '🌦️', '🌧️', '⛈️', '🌩️', '🌨️', '❄️', '☃️', '⛄', '🌬️',
],
};
@ -115,14 +115,14 @@ const EmojiPicker = () => ({
onclick: () => { ChatHubState.emojiSearch = ''; },
}, m('i.fas.fa-times')),
]),
!search && m('.emoji-categories', EMOJI_CATEGORIES.map(c =>
!search && m('.emoji-categories', EMOJI_CATEGORIES.map((c) =>
m('button.emoji-cat-btn' + (c === cat ? '.active' : ''), {
title: c,
onclick: () => { ChatHubState.emojiCategory = c; },
}, EMOJI_ICONS[c])
)),
m('.emoji-grid',
emojis.map(e =>
emojis.map((e) =>
m('button.emoji-btn', {
onclick: () => {
insertEmojiIntoTextarea(e, onSelect);

View File

@ -1,7 +1,5 @@
const m = require('mithril');
const rs = require('rswebui');
const peopleUtil = require('people/people_util');
const people = require('people/people');
// **************** utility functions ********************
@ -35,7 +33,7 @@ function loadDistantChatDetails(pid, apply) {
rs.rsJsonApiRequest(
'/rsChats/getDistantChatStatus',
{
pid: pid,
pid,
},
(detail, success) => {
if (success && detail.retval) {
@ -118,7 +116,7 @@ function renderChatMessage(rawText) {
if (src) {
parts.push(
m('img.chat-embedded-image', {
src: src,
src,
style: {
maxWidth: '100%',
maxHeight: '300px',
@ -160,7 +158,7 @@ function renderChatMessage(rawText) {
if (rawText.trim().startsWith('data:image/')) {
const src = rawText.trim();
return m('img.chat-embedded-image', {
src: src,
src,
style: {
maxWidth: '100%',
maxHeight: '300px',
@ -248,7 +246,7 @@ function renderTextWithEmoji(text) {
const parts = [];
let last = 0;
let match;
// eslint-disable-next-line no-cond-assign
while ((match = emojiRegex.exec(text)) !== null) {
if (match[0].length === 0) { emojiRegex.lastIndex++; continue; }
if (match.index > last) parts.push(text.slice(last, match.index));
@ -442,8 +440,8 @@ const Message = () => {
x: e.clientX,
y: e.clientY,
messageText: targetText,
username: username,
gxsId: gxsId,
username,
gxsId,
};
m.redraw();
};
@ -581,7 +579,7 @@ const ChatLobbyModel = {
loadHistory(id, type) {
const chatPeerId = {
broadcast_status_peer_id: '00000000000000000000000000000000',
type: type,
type,
peer_id: '00000000000000000000000000000000',
distant_chat_id: '00000000000000000000000000000000',
lobby_id: { xstr64: '0' },
@ -594,7 +592,7 @@ const ChatLobbyModel = {
rs.rsJsonApiRequest(
'/rsHistory/getMessages',
{
chatPeerId: chatPeerId,
chatPeerId,
loadCount: 20,
},
(data, success) => {
@ -623,11 +621,11 @@ const ChatLobbyModel = {
rs.rsJsonApiRequest(
'/rsHistory/getMessages',
{
chatPeerId: chatPeerId,
chatPeerId,
loadCount: 0,
},
(data, success) => {
let msgs = (success && data && data.msgs) ? data.msgs : [];
const msgs = (success && data && data.msgs) ? data.msgs : [];
msgs.sort((a, b) => (a.sendTime || a.recvTime) - (b.sendTime || b.recvTime));
ChatHubState.fullHistoryMessages = msgs;
ChatHubState.isHistoryLoading = false;
@ -642,7 +640,7 @@ const ChatLobbyModel = {
'/rsChats/setIdentityForChatLobby',
{
lobby_id: { xstr64: lobbyId },
nick: nick,
nick,
},
() => m.route.set('/chat/:lobby', { lobby: lobbyId }),
true
@ -685,7 +683,7 @@ const ChatLobbyModel = {
const id = this.lastLobbyId || m.route.param('lobby');
const cid = {
broadcast_status_peer_id: '00000000000000000000000000000000',
type: type,
type,
peer_id: '00000000000000000000000000000000',
distant_chat_id: '00000000000000000000000000000000',
lobby_id: { xstr64: '0' },
@ -805,13 +803,13 @@ const ChatLobbyModel = {
'/rsChats/sendChat',
{
id: cid,
msg: msg,
msg,
},
(data, success) => {
if (success) {
const echoMsg = {
chat_id: cid,
msg: msg,
msg,
sendTime: Math.floor(Date.now() / 1000),
lobby_peer_gxs_id: this.currentLobby.gxs_id,
};

View File

@ -10,8 +10,8 @@ const ConfigChat = () => {
let maxStorageDays = 10;
// History states
let historyEnable = { private: true, distant: true, lobby: true };
let historySaveCount = { private: 500, distant: 500, lobby: 500 };
const historyEnable = { private: true, distant: true, lobby: true };
const historySaveCount = { private: 500, distant: 500, lobby: 500 };
function loadSettings() {
// Load Own Identities

View File

@ -579,8 +579,8 @@ const displayHiddenServiceInfo = () => {
details && details.hiddenNodeAddress &&
m('.hidden-service-info', { style: 'display: flex; flex-direction: column; gap: 0.75rem; width: 100%;' }, [
m('p.proxy-description', { style: 'margin-bottom: 0.5rem; color: #475569;' }, details.hiddenType === 4
? "I2P has been automatically configured by Retroshare. You shouldn't need to change anything here."
: "Tor has been automatically configured by Retroshare. You shouldn't need to change anything here."
? 'I2P has been automatically configured by Retroshare. You shouldn\'t need to change anything here.'
: 'Tor has been automatically configured by Retroshare. You shouldn\'t need to change anything here.'
),
// Local Address + Local Port row
m('.nw-config-row', { style: 'display: grid; grid-template-columns: 200px 1fr; gap: 1rem; align-items: center;' }, [

View File

@ -2,7 +2,7 @@ const m = require('mithril');
const rs = require('rswebui');
const util = require('forums/forums_util');
const peopleUtil = require('people/people_util');
const { updatedisplayforums, loadPostContent, getTimestampValue, formatTimestamp } = require('./forums_util');
const { loadPostContent, getTimestampValue, formatTimestamp } = require('./forums_util');
function createforum() {
let title;
@ -75,75 +75,6 @@ function createforum() {
]),
};
}
const EditThread = () => {
let title = '';
let body = '';
return {
oninit: (vnode) => {
title = vnode.attrs.current_title;
body = vnode.attrs.current_body;
},
view: (vnode) =>
m('.widget', [
m('h3', 'Edit Thread'),
m('hr'),
m(
'iddisplay',
{
style: { display: 'block ruby' }, // same line block ruby
},
[
'Identity: ',
m('h5[id=authid]', rs.userList.username(vnode.attrs.authorId)),
]
),
m(
'titledisplay',
{
style: { display: 'block ruby' },
},
[
'Title: ',
m('input[type=text][placeholder=Title]', {
value: vnode.attrs.current_title,
oninput: (e) => (title = e.target.value),
}),
]
),
m('textarea[rows=5]', {
style: { width: '90%', display: 'block' },
oninput: (e) => (body = e.target.value),
value: vnode.attrs.current_body,
}),
m(
'button',
{
onclick: async () => {
const res = await rs.rsJsonApiRequest('/rsgxsforums/createPost', {
forumId: vnode.attrs.forumId,
mBody: body,
title,
authorId: vnode.attrs.authorId,
parentId: vnode.attrs.current_parent,
origPostId: vnode.attrs.current_msgid,
});
res.body.retval === false
? util.popupmessage([m('h3', 'Error'), m('hr'), m('p', res.body.errorMessage)])
: util.popupmessage([
m('h3', 'Success'),
m('hr'),
m('p', 'Thread edited successfully'),
]);
util.updatedisplayforums(vnode.attrs.forumId);
m.redraw();
},
},
'Add'
),
]),
};
};
const AddThread = () => {
let title = '';
let body = '';
@ -228,166 +159,6 @@ const AddThread = () => {
// getTimestampValue and formatTimestamp are imported from forums_util.js
function displaythread() {
// recursive function to display all the threads
let groupmessagepair;
let unread;
let editpermission = false;
return {
view: (v) => {
const thread = v.attrs.threadStruct.thread;
groupmessagepair = { first: thread.mMeta.mGroupId, second: thread.mMeta.mOrigMsgId };
let parMap = [];
if (util.Data.ParentThreadMap[thread.mMeta.mOrigMsgId]) {
parMap = util.Data.ParentThreadMap[thread.mMeta.mOrigMsgId];
}
unread = thread.mMeta.mMsgStatus === util.THREAD_UNREAD;
v.attrs.identity &&
v.attrs.identity.map((val) => {
if (val.localeCompare(thread.mMeta.mAuthorId) === 0) {
// if the author of the thread matches one of our own ids
editpermission = true;
}
});
return [
m(
'tr',
{
style: unread ? { fontWeight: 'bold' } : '',
},
[
Object.keys(parMap).length // if this thread has some replies
? m(
'td',
m('i.fas.fa-angle-right', {
class: 'fa-rotate-' + (v.attrs.threadStruct.showReplies ? '90' : '0'),
style: 'margin-top:12px',
onclick: () => {
v.attrs.threadStruct.showReplies = !v.attrs.threadStruct.showReplies;
},
})
)
: m('td', ''),
m(
'td',
{
style: {
position: 'relative',
'--replyDepth': v.attrs.replyDepth,
left: 'calc(30px*var(--replyDepth))', // shifts reply by 30 px
padding: '10px 0',
},
},
[
m('div.date', { style: { fontSize: '0.8em', color: '#888' } },
formatTimestamp(thread.mMeta.mPublishTs)
),
m('div.title', {
style: { fontWeight: 'bold', fontSize: '1.1em', cursor: 'pointer', margin: '5px 0' },
onclick: async () => {
v.attrs.changeThread(thread.mMeta.mOrigMsgId);
if (unread) {
const res = await rs.rsJsonApiRequest('/rsgxsforums/markRead', {
messageId: groupmessagepair,
read: true,
});
if (res.body.retval) {
updatedisplayforums(thread.mMeta.mGroupId);
m.redraw();
}
}
},
ondblclick: () =>
(v.attrs.threadStruct.showReplies = !v.attrs.threadStruct.showReplies),
}, [
thread.mMeta.mMsgName,
m('options', { style: 'display:block; margin-top: 5px;' }, [
m(
'button',
{
style: 'font-size:12px; margin-right: 5px;',
onclick: (e) => {
e.stopPropagation();
util.popupmessage(
m(AddThread, {
parent_thread: thread.mMeta.mMsgName,
forumId: thread.mMeta.mGroupId,
authorId: v.attrs.identity,
parentId: thread.mMeta.mMsgId,
})
);
},
},
'Reply'
),
editpermission &&
m(
'button',
{
style: 'font-size:12px; margin-right: 5px;',
onclick: async (e) => {
e.stopPropagation();
const body = await loadPostContent(
thread.mMeta.mGroupId,
thread.mMeta.mOrigMsgId
);
util.popupmessage(
m(EditThread, {
current_thread: thread.mMeta.mMsgName,
forumId: thread.mMeta.mGroupId,
current_title: thread.mMeta.mMsgName,
current_body: body || '',
authorId: thread.mMeta.mAuthorId,
current_parent: thread.mMeta.mParentId,
current_msgid: thread.mMeta.mOrigMsgId,
})
);
},
},
'Edit'
),
m(
'button',
{
style: { fontSize: '12px' },
onclick: async (e) => {
e.stopPropagation();
const res = await rs.rsJsonApiRequest('/rsgxsforums/markRead', {
messageId: groupmessagepair,
read: !unread ? true : false,
});
if (res.body.retval) {
updatedisplayforums(thread.mMeta.mGroupId);
m.redraw();
}
},
},
unread ? 'Mark Read' : 'Mark Unread'
),
]),
]),
m('div.author', { style: { fontSize: '0.9em', fontStyle: 'italic' } }, rs.userList.username(thread.mMeta.mAuthorId)),
]
),
]
),
v.attrs.threadStruct.showReplies &&
Object.keys(parMap).map((key, index) =>
m(displaythread, {
// recursive call to all replies
threadStruct: util.Data.Threads[parMap[key].mGroupId][parMap[key].mOrigMsgId],
replyDepth: v.attrs.replyDepth + 1,
identity: v.attrs.identity,
changeThread: v.attrs.changeThread,
})
),
];
},
};
}
const ThreadView = () => {
let ownId;
return {
@ -449,7 +220,7 @@ const ThreadView = () => {
style: { marginRight: '10px' },
onclick: () => util.popupmessage(m(AddThread, {
parent_thread: meta.mMsgName,
forumId: forumId,
forumId,
authorId: ownId,
parentId: msgId,
}))

View File

@ -106,7 +106,7 @@ async function updatedisplayforums(keyid) {
const threadStruct = {
thread: { mMeta: meta, mMsg: null },
replies: replies,
replies,
showReplies: false,
};
@ -156,7 +156,7 @@ async function loadPostContent(forumId, msgId) {
try {
const res = await rs.rsJsonApiRequest('/rsgxsforums/getForumContent', {
forumId: forumId,
forumId,
msgsIds: [msgId],
});
if (res && res.body && res.body.retval && res.body.msgs && res.body.msgs.length > 0) {

View File

@ -41,7 +41,7 @@ function renderPolygon(shapePoints, x, y, angle, shapeAngle, size, color) {
function toSvg(hash, width) {
if (!hash || hash.length < 18) {
hash = "00000000000000000000000000000000";
hash = '00000000000000000000000000000000';
}
const csh = parseInt(hash.substr(0, 1), 16);
@ -99,4 +99,4 @@ function toSvg(hash, width) {
module.exports = {
toSvg
};
};

View File

@ -18,7 +18,7 @@ function formatFileSize(bytes) {
const Layout = () => {
let showCc = false;
let showBcc = false;
let ownAvatars = {};
const ownAvatars = {};
let attachments = [];
let showEmojiPicker = false;
let emojiSearch = '';
@ -397,7 +397,7 @@ const Layout = () => {
const attHtml = `
<br/><hr style="border:none;border-top:1px solid #e2e8f0;margin:1rem 0;"/><div style="margin-top:10px;font-weight:bold;color:#475569;font-size:0.9rem;">Attachments (${attachments.length}):</div>
<ul style="list-style:none;padding:0;margin:6px 0;">
${attachments.map(att => `<li style="padding:4px 0;color:#1e293b;font-size:0.875rem;">📎 <b>${att.name}</b> <span style="color:#94a3b8;font-size:0.8em;">(${att.size})</span></li>`).join('')}
${attachments.map((att) => `<li style="padding:4px 0;color:#1e293b;font-size:0.875rem;">📎 <b>${att.name}</b> <span style="color:#94a3b8;font-size:0.8em;">(${att.size})</span></li>`).join('')}
</ul>
`;
fullMailBody += attHtml;
@ -701,7 +701,7 @@ const Layout = () => {
}),
]),
!emojiSearch && m('.emoji-cat-bar', { style: 'display: flex; background: #f8fafc; border-bottom: 1px solid #e2e8f0; padding: 0.25rem; overflow-x: auto;' },
chatEmoji.EMOJI_CATEGORIES.map(c =>
chatEmoji.EMOJI_CATEGORIES.map((c) =>
m('button', {
style: `border: none; background: ${c === emojiCategory ? '#ffffff' : 'transparent'}; border-radius: 0.25rem; padding: 0.3rem 0.4rem; cursor: pointer; font-size: 1rem; box-shadow: ${c === emojiCategory ? '0 1px 2px rgba(0,0,0,0.1)' : 'none'};`,
title: c,
@ -711,9 +711,9 @@ const Layout = () => {
),
m('.emoji-grid-body', { style: 'padding: 0.5rem; display: grid; grid-template-columns: repeat(7, 1fr); gap: 0.25rem; max-height: 230px; overflow-y: auto;' },
(emojiSearch
? Object.values(chatEmoji.EMOJI_DATA).flat().filter(e => e.includes(emojiSearch))
? Object.values(chatEmoji.EMOJI_DATA).flat().filter((e) => e.includes(emojiSearch))
: (chatEmoji.EMOJI_DATA[emojiCategory] || [])
).map(e =>
).map((e) =>
m('button', {
style: 'border: none; background: transparent; font-size: 1.25rem; cursor: pointer; padding: 0.25rem; border-radius: 0.25rem; transition: background 0.15s ease;',
onmouseenter: (ev) => (ev.currentTarget.style.background = '#f1f5f9'),

View File

@ -195,7 +195,7 @@ const GenericMailList = () => {
m(util.MessageSummary, {
key: msg.msgId,
details: msg,
category: category,
category,
})
)
)

View File

@ -622,11 +622,11 @@ const Table = () => {
};
let totalItems = 0;
let tbody = v.children[0];
const tbody = v.children[0];
if (tbody && tbody.children) {
const flatChildren = Array.isArray(tbody.children) ? tbody.children.flat().filter(Boolean) : [tbody.children].filter(Boolean);
totalItems = flatChildren.length;
const start = currentPage * pageSize;
const end = start + pageSize;
tbody.children = flatChildren.slice(start, end);

File diff suppressed because it is too large Load Diff

View File

@ -149,7 +149,7 @@ function sendDirectChatMessage() {
'/rsChats/sendChat',
{
id: { type: 1, peer_id: State.currentChatPeerId },
msg: msg,
msg,
},
(data, success) => {
if (success) {

View File

@ -100,14 +100,6 @@ const ChatTab = () => {
const canTalk = State.distantChatStatus && State.distantChatStatus.status === 2;
// Filter history by search query for history modal
const query = (State.historySearchQuery || '').toLowerCase();
const filteredHistory = (State.fullHistoryMessages || []).filter((msg) => {
if (!query) return true;
const text = (msg.msg || msg.message || '').toLowerCase();
return text.includes(query);
});
return m('.network-chat-view', [
m('.chat-identity-select-container', {
style: 'padding: 0.5rem 1rem; background-color: #ffffff; border-bottom: 1px solid #cbd5e1; display: flex; align-items: center; justify-content: space-between; font-size: 0.85rem;',

View File

@ -33,7 +33,7 @@ const PeopleSidebar = () => {
},
view: () => {
// 1. Determine list based on mainTab ('people' vs 'chats')
let displayItems = [];
let displayItems;
// 0. Compute active chats count (conversations with real message history)
const allUserGroupIds = new Set((rs.userList.users || []).map((u) => u.mGroupId));
@ -47,7 +47,7 @@ const PeopleSidebar = () => {
});
if (State.mainTab === 'people') {
let baseList = [];
let baseList;
if (State.activeFilter === 'own') {
baseList = peopleUtil.sortIds(State.ownGxsIds) || [];
} else if (State.activeFilter === 'contacts') {
@ -57,13 +57,13 @@ const PeopleSidebar = () => {
}
displayItems = baseList.filter((item) => {
let name = State.activeFilter === 'own' ? (rs.userList.username(item) || 'Unknown') : (item.mGroupName || 'Unknown');
const name = State.activeFilter === 'own' ? (rs.userList.username(item) || 'Unknown') : (item.mGroupName || 'Unknown');
return name.toLowerCase().includes(State.searchString.toLowerCase());
});
displayItems.sort((a, b) => {
let nameA = State.activeFilter === 'own' ? (rs.userList.username(a) || '') : (a.mGroupName || '');
let nameB = State.activeFilter === 'own' ? (rs.userList.username(b) || '') : (b.mGroupName || '');
const nameA = State.activeFilter === 'own' ? (rs.userList.username(a) || '') : (a.mGroupName || '');
const nameB = State.activeFilter === 'own' ? (rs.userList.username(b) || '') : (b.mGroupName || '');
return nameA.localeCompare(nameB);
});
} else {

View File

@ -359,7 +359,7 @@ function loadChatMessages() {
rs.rsJsonApiRequest(
'/rsHistory/getMessages',
{
chatPeerId: chatPeerId,
chatPeerId,
loadCount: 50,
},
(data, success) => {
@ -509,7 +509,7 @@ function preloadAllChatHistory() {
if (!existing || lastTime > existing.lastTime) {
State.chatHistoryMap[gxsId] = {
lastMsg: last.message || last.msg || '',
lastTime: lastTime,
lastTime,
};
m.redraw();
}
@ -573,7 +573,7 @@ function loadAllHistoryForSelectedPeer(callback) {
rs.rsJsonApiRequest(
'/rsHistory/getMessages',
{
chatPeerId: chatPeerId,
chatPeerId,
loadCount: 0, // 0 = load all messages in C++
},
(msgData, success) => {
@ -588,7 +588,7 @@ function loadAllHistoryForSelectedPeer(callback) {
const key = `${mItem.sendTime || mItem.recvTime}_${text}`;
if (!map.has(key)) map.set(key, mItem);
});
let uniqueMsgs = Array.from(map.values());
const uniqueMsgs = Array.from(map.values());
uniqueMsgs.sort((a, b) => (a.sendTime || a.recvTime) - (b.sendTime || b.recvTime));
State.fullHistoryMessages = uniqueMsgs;
State.isHistoryLoading = false;

View File

@ -70,7 +70,7 @@ const UserAvatar = () => ({
width: sizeStr,
height: sizeStr,
borderRadius: isSquare ? '0' : '50%',
backgroundColor: backgroundColor,
backgroundColor,
}
},
m('p', {

View File

@ -15,11 +15,11 @@
border: 0;
border-radius: 5px;
cursor: pointer;
box-shadow: inset -3px -3px 0 darken($color: $bg-color, $amount: 20);
box-shadow: inset -3px -3px 0 color.adjust($bg-color, $lightness: -20%);
&:active {
outline: none;
box-shadow: inset 3px 3px 0 darken($color: $bg-color, $amount: 20);
box-shadow: inset 3px 3px 0 color.adjust($bg-color, $lightness: -20%);
}
}

View File

@ -372,6 +372,7 @@ textarea.chatMsg {
padding: 1.25rem;
border-bottom: 1px solid #e2e8f0;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
position: relative; // anchors the absolutely-positioned .chat-create-lobby-btn
.profile-header {
display: flex;
@ -896,6 +897,7 @@ textarea.chatMsg {
display: flex;
flex-direction: column;
flex-shrink: 0;
position: relative; // anchors the hovered-participant .user-tooltip (top offset is measured against this)
}
.chat-hub-rightbar .rightbar-title {
@ -926,6 +928,7 @@ textarea.chatMsg {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: relative;
}
.chat-hub-rightbar .user:hover {
@ -1125,4 +1128,490 @@ textarea.chatMsg {
}
// Chat-hub extras: user tooltip, right-bar context menu, attach-file modal,
// emoji picker, create-lobby button (chat.js). Recovered from compiled styles.css.
.chat-create-lobby-btn {
position: absolute;
bottom: 0.5rem;
right: 1.25rem;
background-color: #0084ff;
color: #ffffff;
border: none;
border-radius: 0.375rem;
padding: 0.35rem 0.75rem;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
box-shadow: 0 4px 6px -1px rgba(0, 132, 255, 0.2), 0 2px 4px -1px rgba(0, 132, 255, 0.1);
transition: background-color 0.2s, transform 0.2s;
display: flex;
align-items: center;
gap: 0.25rem;
}
.chat-create-lobby-btn:hover {
background-color: #0073e6;
transform: translateY(-1px);
}
.chat-create-lobby-btn:active {
transform: translateY(0);
}
.chat-hub-rightbar .user .user-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.user-tooltip {
position: absolute;
width: 260px;
background-color: #ffffe1;
border: 1px solid #7f7f7f;
box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.25);
padding: 0.5rem;
border-radius: 0.25rem;
z-index: 10000;
white-space: normal;
display: flex;
gap: 0.5rem;
align-items: flex-start;
}
.chat-hub-rightbar .user-tooltip {
left: -275px;
transform: translateY(-50%);
z-index: 1000;
}
.user-tooltip .tooltip-avatar {
flex-shrink: 0;
}
.user-tooltip .tooltip-details {
display: flex;
flex-direction: column;
gap: 0.25rem;
font-size: 0.8rem;
color: #000000;
text-align: left;
}
.user-tooltip .tooltip-row {
line-height: 1.2;
}
.user-tooltip .tooltip-label {
font-weight: bold;
}
.user-tooltip .tooltip-value {
font-weight: normal;
word-break: break-all;
}
.user-tooltip .tooltip-value.tooltip-id {
font-family: monospace;
}
.chat-hub-rightbar .rightbar-context-menu {
position: absolute;
right: 1rem;
width: 210px;
background-color: #ffffff;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
border-radius: 0.375rem;
z-index: 1010;
padding: 0.25rem 0;
display: flex;
flex-direction: column;
}
.chat-hub-rightbar .rightbar-context-menu .menu-item {
padding: 0.5rem 1rem;
font-size: 0.85rem;
color: #334155;
cursor: pointer;
display: flex;
align-items: center;
transition: background-color 0.2s;
}
.chat-hub-rightbar .rightbar-context-menu .menu-item:hover {
background-color: #f1f5f9;
color: #0f172a;
}
.chat-emoji {
font-size: 1.45em;
line-height: 1;
vertical-align: -0.15em;
display: inline-block;
}
.chat-hub-attach-btn,
.chat-hub-action-btn {
background-color: transparent !important;
border: none !important;
font-size: 1.15rem !important;
color: #64748b !important;
cursor: pointer !important;
padding: 0.4rem 0.5rem !important;
border-radius: 0.375rem !important;
flex-shrink: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
transition: all 0.2s !important;
box-shadow: none !important;
margin: 0 !important;
line-height: 1 !important;
height: 36px !important;
width: 36px !important;
}
.chat-hub-attach-btn:hover,
.chat-hub-action-btn:hover {
background-color: #f1f5f9 !important;
color: #3b82f6 !important;
transform: none !important;
}
.attach-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(15, 23, 42, 0.4);
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
}
.attach-modal {
background-color: #ffffff;
border-radius: 0.5rem;
width: 450px;
max-width: 90%;
padding: 1.5rem;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
gap: 1rem;
}
.attach-modal .attach-modal-header {
display: flex;
align-items: center;
gap: 0.6rem;
margin-bottom: 0.25rem;
}
.attach-modal .attach-modal-icon {
font-size: 1.2rem;
color: #3b82f6;
}
.attach-modal h4 {
margin: 0;
font-size: 1.2rem;
color: #0f172a;
}
.attach-modal p {
margin: 0;
font-size: 0.9rem;
color: #475569;
}
.attach-modal .attach-path-row {
display: flex;
gap: 0.5rem;
align-items: center;
}
.attach-modal .attach-path-row input[type="text"] {
flex: 1;
padding: 0.75rem;
border: 1px solid #cbd5e1;
border-radius: 0.375rem;
font-size: 0.9rem;
outline: none;
transition: border-color 0.2s;
min-width: 0;
}
.attach-modal .attach-path-row input[type="text"]:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}
.attach-browse-btn {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 0.35rem;
padding: 0.625rem 0.9rem;
font-size: 0.875rem;
background-color: #f1f5f9;
color: #334155;
border: 1px solid #cbd5e1;
border-radius: 0.375rem;
cursor: pointer;
box-shadow: none;
transition: background-color 0.2s, border-color 0.2s;
white-space: nowrap;
}
.attach-browse-btn:hover {
background-color: #e2e8f0;
border-color: #94a3b8;
}
.attach-path-hint {
display: flex;
align-items: flex-start;
gap: 0.5rem;
padding: 0.6rem 0.75rem;
background-color: #fffbeb;
border: 1px solid #fcd34d;
border-left: 3px solid #f59e0b;
border-radius: 0.375rem;
font-size: 0.825rem;
color: #92400e;
line-height: 1.45;
}
.attach-path-hint i {
color: #f59e0b;
margin-top: 0.1rem;
flex-shrink: 0;
}
.attach-path-hint code {
font-family: monospace;
background-color: rgba(245, 158, 11, 0.15);
padding: 0.05rem 0.25rem;
border-radius: 0.2rem;
}
.attach-modal .hashing-spinner {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
color: #3b82f6;
}
.attach-modal .error-text {
color: #ef4444;
font-size: 0.85rem;
margin: 0;
}
.attach-modal .modal-buttons {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
margin-top: 0.5rem;
}
.attach-modal .modal-buttons button {
padding: 0.5rem 1rem;
font-size: 0.9rem;
border-radius: 0.25rem;
border: none;
cursor: pointer;
transition: opacity 0.2s;
}
.attach-modal .modal-buttons button:hover {
opacity: 0.9;
}
.chat-hub-emoji-btn {
background-color: transparent;
border: none;
font-size: 1.3rem;
cursor: pointer;
padding: 0.35rem 0.4rem;
margin-right: 0.25rem;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.375rem;
line-height: 1;
transition: background-color 0.15s, transform 0.15s;
box-shadow: none;
}
.chat-hub-emoji-btn:hover {
background-color: #f1f5f9;
transform: scale(1.1);
}
.emoji-picker-wrapper {
position: relative;
flex-shrink: 0;
display: flex;
align-items: center;
}
.emoji-picker {
position: absolute;
bottom: calc(100% + 0.5rem);
left: 0;
width: 320px;
background-color: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 0.625rem;
box-shadow: 0 8px 30px -4px rgba(0, 0, 0, 0.18), 0 4px 12px -2px rgba(0, 0, 0, 0.1);
z-index: 3000;
display: flex;
flex-direction: column;
overflow: hidden;
animation: emoji-pop 0.15s ease-out;
}
.emoji-search-row {
display: flex;
align-items: center;
gap: 0.4rem;
padding: 0.6rem 0.75rem 0.4rem;
border-bottom: 1px solid #f1f5f9;
}
.emoji-search-icon {
color: #94a3b8;
font-size: 0.8rem;
flex-shrink: 0;
}
.emoji-search-input {
flex: 1;
border: 1px solid #e2e8f0;
border-radius: 0.375rem;
padding: 0.3rem 0.5rem;
font-size: 0.85rem;
outline: none;
background-color: #f8fafc;
transition: border-color 0.15s;
}
.emoji-search-input:focus {
border-color: #3ba4d7;
background-color: #fff;
}
.emoji-search-clear {
background: none;
border: none;
cursor: pointer;
color: #94a3b8;
padding: 0.2rem;
font-size: 0.8rem;
box-shadow: none;
display: flex;
align-items: center;
}
.emoji-search-clear:hover {
color: #475569;
}
.emoji-categories {
display: flex;
gap: 0.1rem;
padding: 0.35rem 0.5rem;
border-bottom: 1px solid #f1f5f9;
overflow-x: auto;
scrollbar-width: none;
}
.emoji-categories::-webkit-scrollbar {
display: none;
}
.emoji-cat-btn {
background: none;
border: none;
cursor: pointer;
font-size: 1.2rem;
padding: 0.3rem 0.35rem;
border-radius: 0.375rem;
line-height: 1;
box-shadow: none;
transition: background-color 0.1s;
flex-shrink: 0;
}
.emoji-cat-btn:hover {
background-color: #f1f5f9;
}
.emoji-cat-btn.active {
background-color: #e0f2fe;
box-shadow: inset 0 -2px 0 #3ba4d7;
}
.emoji-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0;
padding: 0.4rem 0.35rem;
max-height: 220px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #cbd5e1 transparent;
}
.emoji-grid::-webkit-scrollbar {
width: 4px;
}
.emoji-grid::-webkit-scrollbar-track {
background: transparent;
}
.emoji-grid::-webkit-scrollbar-thumb {
background-color: #cbd5e1;
border-radius: 4px;
}
.emoji-btn {
background: none;
border: none;
cursor: pointer;
font-size: 1.7rem;
padding: 0.25rem;
border-radius: 0.3rem;
line-height: 1;
box-shadow: none;
text-align: center;
transition: background-color 0.1s, transform 0.1s;
display: flex;
align-items: center;
justify-content: center;
aspect-ratio: 1;
}
.emoji-btn:hover {
background-color: #f1f5f9;
transform: scale(1.2);
}
@keyframes emoji-pop {
from { opacity: 0; transform: scale(0.92) translateY(6px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}

View File

@ -1,3 +1,4 @@
@use 'sass:color';
@use '../abstracts' as *;
.mail {
@ -7,13 +8,13 @@
}
&-tags {
padding: 0.5rem;
border: 1px solid transparentize($dark-color, 0.8);
border: 1px solid color.adjust($dark-color, $alpha: -0.8);
border-radius: 6px;
&__container {
@include flex(column);
& .tag-item {
@include flex($align: center, $gap: 4px);
border-bottom: 1px solid transparentize($dark-color, 0.9);
border-bottom: 1px solid color.adjust($dark-color, $alpha: -0.9);
padding: 2px 0;
&:last-child {

View File

@ -1,3 +1,4 @@
@use 'sass:color';
@use '../abstracts/' as *;
.file-view {
@ -71,7 +72,7 @@ table.friendsfiles td:nth-child(2) {
margin-top: 1rem;
padding: 8px;
@include flex($gap: 8px);
border: 1px solid transparentize($dark-color, 0.8);
border: 1px solid color.adjust($dark-color, $alpha: -0.8);
border-radius: 6px;
height: 100%;
overflow: auto;
@ -79,11 +80,11 @@ table.friendsfiles td:nth-child(2) {
&__keywords {
flex-basis: 15%;
padding-right: 0.25rem;
border-right: 1px solid transparentize($dark-color, 0.9);
border-right: 1px solid color.adjust($dark-color, $alpha: -0.9);
& .keywords-container {
@include flex(column);
border-top: 2.5px solid transparentize($dark-color, 0.92);
border-top: 2.5px solid color.adjust($dark-color, $alpha: -0.92);
margin-top: 0.125rem;
padding-top: 0.25rem;

View File

@ -1,3 +1,4 @@
@use 'sass:color';
@use '../abstracts/' as *;
.side-bar {
@ -182,7 +183,7 @@ table.mails {
/* attachments */
&:nth-child(2) {
width: 5%;
color: darken($light-color, 50%);
color: color.adjust($light-color, $lightness: -50%);
}
/* subject */
&:nth-child(3) {
@ -239,7 +240,7 @@ table.mails {
&:hover {
background-color: $light-color;
color: darken($light-color, 80%);
color: color.adjust($light-color, $lightness: -80%);
}
}
}

View File

@ -104,6 +104,7 @@
display: flex;
flex-direction: column;
overflow: hidden;
position: relative; // anchors the absolutely-positioned .people-context-menu
.searchbar-container {
padding: 0.75rem 1rem;

View File

@ -195,3 +195,134 @@ img.avatar {
}
}
// Recent-chats split-pane + friends context menu (people_sidebar.js).
// Recovered from compiled styles.css; hard-coded colors kept as-was.
.friends-list-container .people-context-menu {
position: absolute;
left: 2rem;
width: 210px;
background-color: #ffffff;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
border-radius: 0.375rem;
z-index: 1010;
padding: 0.25rem 0;
display: flex;
flex-direction: column;
}
.friends-list-container .people-context-menu .menu-item {
padding: 0.5rem 1rem;
font-size: 0.85rem;
color: #334155;
cursor: pointer;
display: flex;
align-items: center;
transition: background-color 0.2s;
}
.friends-list-container .people-context-menu .menu-item:hover {
background-color: #f1f5f9;
color: #0f172a;
}
.people-container {
display: flex;
height: calc(100vh - 55px);
width: 100%;
overflow: hidden;
}
.people-left-pane {
width: 320px;
border-right: 1px solid #cbd5e1;
display: flex;
flex-direction: column;
background-color: #ffffff;
overflow: hidden;
}
.people-right-pane {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: #f8fafc;
}
.people-left-pane .chat-item {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
gap: 0.75rem;
border-bottom: 1px solid #f1f5f9;
cursor: pointer;
transition: background-color 0.15s ease;
position: relative;
}
.people-left-pane .chat-item:hover {
background-color: #f8fafc;
}
.people-left-pane .chat-item.selected {
background-color: #eff6ff;
border-left: 3px solid #3b82f6;
}
.people-left-pane .chat-item .chat-avatar-wrapper {
position: relative;
flex-shrink: 0;
}
.people-left-pane .chat-item .chat-avatar-wrapper .status-dot {
position: absolute;
bottom: 0;
right: 0;
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid #ffffff;
}
.people-left-pane .chat-item .chat-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.people-left-pane .chat-item .chat-info .chat-name {
font-size: 0.9rem;
font-weight: 700;
color: #1e293b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.people-left-pane .chat-item .chat-info .chat-last-msg {
font-size: 0.8rem;
color: #64748b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.people-left-pane .chat-item .chat-meta {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.25rem;
flex-shrink: 0;
}
.people-left-pane .chat-item .chat-meta .chat-time {
font-size: 0.75rem;
color: #94a3b8;
white-space: nowrap;
}

View File

@ -0,0 +1,51 @@
import js from '@eslint/js';
import globals from 'globals';
export default [
{
ignores: ['app/mithril.js', 'assets/**', 'make-src/**'],
},
{
files: ['app/**/*.js'],
languageOptions: {
ecmaVersion: 2020,
sourceType: 'commonjs',
globals: {
...globals.browser,
...globals.commonjs,
},
},
rules: {
...js.configs.recommended.rules,
'linebreak-style': ['off', 'unix'],
quotes: ['error', 'single'],
semi: ['error', 'always'],
'arrow-spacing': ['error', { before: true, after: true }],
'arrow-parens': ['error', 'always'],
'comma-style': ['error', 'last'],
'comma-spacing': ['error', { before: false, after: true }],
camelcase: ['error', {
allow: ['^UNSAFE_'],
properties: 'never',
ignoreGlobals: true,
}],
'eol-last': 'error',
eqeqeq: ['error', 'always', { null: 'ignore' }],
'no-irregular-whitespace': 'error',
'no-trailing-spaces': 'error',
'no-unexpected-multiline': 'error',
'no-unreachable': 'error',
'no-var': 'warn',
'no-unused-vars': ['error', {
args: 'none',
caughtErrors: 'none',
ignoreRestSiblings: true,
vars: 'all',
}],
'object-shorthand': 'error',
'prefer-const': ['error', { destructuring: 'all' }],
'semi-style': ['warn', 'last'],
'spaced-comment': ['warn', 'always'],
},
},
];

View File

@ -5,59 +5,71 @@ setlocal enabledelayedexpansion
echo "### Starting WebUI build ###"
set src=%~dp0..\..\webui-src
set "src=%~dp0..\..\webui-src"
rem Output destination
if "%~1" == "" (
set publicdest=%~dp0..\..\webui
if "%~1"=="" (
set "publicdest=%~dp0..\..\webui"
) else (
set publicdest=%~1\webui
set "publicdest=%~1\webui"
)
if exist "%publicdest%" echo removing existing %publicdest%&&rd %publicdest% /S /Q
if exist "%publicdest%" (
echo removing existing %publicdest%
rd "%publicdest%" /S /Q
)
echo creating %publicdest%
md %publicdest%
md "%publicdest%"
rem Make full path
pushd %publicdest%
set publicdest=%cd%
pushd "%publicdest%"
set "publicdest=%cd%"
popd
echo copying html file
xcopy /s %src%\index.html %publicdest%
copy /Y "%src%\index.html" "%publicdest%\index.html" >nul
echo copying css file
xcopy /s %src%\styles.css %publicdest%
copy /Y "%src%\styles.css" "%publicdest%\styles.css" >nul
echo building app.js
echo - copying template.js ...
copy %src%\make-src\template.js %publicdest%\app.js
copy /Y "%src%\make-src\template.js" "%publicdest%\app.js" >nul
pushd %src%\app
pushd "%src%\app"
set "basefolder=%cd%\"
set "lastsection="
for /R %%F in (*.js) do call :addfile-js "%basefolder%" "%%F"
popd
echo copying assets folder
xcopy /s %src%\assets\ %publicdest%
xcopy "%src%\assets\*" "%publicdest%\" /E /I /Y >nul
echo "### WebUI build complete ###"
goto :EOF
:addfile-js
set basefolder=%~1
set fname=%~2
set "basefolder=%~1"
set "fname=%~2"
set registername=%~dpn2
set registername=!registername:%basefolder%=!
set registername=%registername:\=/%
set "registername=%~dpn2"
set "registername=!registername:%basefolder%=!"
set "registername=%registername:\=/%"
echo - adding %registername% ...
echo require.register("%registername%", function(exports, require, module) { >> %publicdest%\app.js
type %fname% >> %publicdest%\app.js
echo. >> %publicdest%\app.js
echo }); >> %publicdest%\app.js
for /f "tokens=1,2 delims=/" %%A in ("!registername!") do (
if "%%B"=="" (
echo - adding !registername! ...
set "lastsection="
) else if not "!lastsection!"=="%%A" (
echo - adding %%A/* ...
set "lastsection=%%A"
)
)
echo require.register("%registername%", function(exports, require, module) { >>"%publicdest%\app.js"
type "%fname%" >>"%publicdest%\app.js"
echo. >>"%publicdest%\app.js"
echo }); >>"%publicdest%\app.js"
:EOF

View File

@ -0,0 +1,35 @@
'use strict';
const assert = require('node:assert/strict');
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const shellScript = path.join(__dirname, 'build.sh');
const batchScript = path.join(__dirname, 'build.bat');
function getRunner(platform, commandShell = 'cmd.exe') {
return platform === 'win32'
? { command: commandShell, args: ['/d', '/c', batchScript] }
: { command: 'sh', args: [shellScript] };
}
function run(runner) {
const result = spawnSync(runner.command, runner.args, { stdio: 'inherit' });
if (result.error) {
console.error(result.error.message);
return 1;
}
return result.status ?? 1;
}
if (process.argv[2] === '--check') {
assert.equal(path.basename(getRunner('win32').args[2]), 'build.bat');
assert.equal(path.basename(getRunner('linux').args[0]), 'build.sh');
const status = process.platform === 'win32'
? 0
: run({ command: 'sh', args: ['-n', shellScript] });
if (status === 0) console.log('Build dispatcher checks passed.');
process.exitCode = status;
} else {
process.exitCode = run(getRunner(process.platform, process.env.ComSpec));
}

View File

@ -1,68 +1,85 @@
#!/bin/bash
#!/bin/sh
# create webfiles from sources at compile time (works without npm/node.js)
# Create webfiles from sources at compile time (works without npm/node.js)
#
# Usage: build.sh [DEST_PARENT] [TARGET_FILE] [EXTRA_COPY_DIR]
# DEST_PARENT parent dir of the generated webui/ (default: repo root)
# TARGET_FILE build only this file (index.html|styles.css|app.js); default: all
# EXTRA_COPY_DIR also copy TARGET_FILE into EXTRA_COPY_DIR/webui/
set -eu
echo "### Starting WebUI build ###"
src=$(readlink -f $(dirname $0))/../../webui-src
# Resolve the script's own directory portably.
script_dir=$(cd -- "$(dirname -- "$0")" && pwd -P)
src="$script_dir/../../webui-src"
if [ "$1" = "" ]; then
publicdest=$(readlink -f $(dirname $0))/../../webui
dest_parent="${1:-}"
target="${2:-}"
extra_copy_dir="${3:-}"
if [ -z "$dest_parent" ]; then
publicdest="$script_dir/../../webui"
else
publicdest=$1/webui
publicdest="$dest_parent/webui"
fi
if [ "$2" = "" ]; then
if [ -d "$publicdest" ]; then
echo removing existing $publicdest
rm $publicdest -R
fi
# Full rebuild (no specific target): remove any existing output first.
if [ -z "$target" ] && [ -d "$publicdest" ]; then
echo "removing existing $publicdest"
rm -rf -- "$publicdest"
fi
if [ ! -d "$publicdest" ]; then
echo creating $publicdest
mkdir $publicdest
mkdir -p -- "$publicdest"
if [ -z "$target" ] || [ "$target" = "index.html" ]; then
echo "copying html file"
cp -- "$src/index.html" "$publicdest/"
fi
# For using recursive directory search(requires bash v4+)
shopt -s globstar
if [ "$2" = "" ]||[ "$2" = "index.html" ]; then
echo copying html file
cp $src/index.html $publicdest/
if [ -z "$target" ] || [ "$target" = "styles.css" ]; then
echo "copying css file"
cp -- "$src/styles.css" "$publicdest/"
fi
if [ "$2" = "" ]||[ "$2" = "styles.css" ]; then
echo copying css file
cp $src/styles.css $publicdest/
if [ -z "$target" ] || [ "$target" = "app.js" ]; then
echo "building app.js:"
echo "- copying template.js ..."
cp -- "$src/make-src/template.js" "$publicdest/app.js"
js_root="$src/app"
find "$js_root" -type f -name '*.js' | LC_ALL=C sort | while IFS= read -r filename; do
fname="${filename#"$js_root/"}"
fname="${fname%.*}"
case "$fname" in
*/*)
section="${fname%%/*}/*"
if [ "$section" != "${last_section:-}" ]; then
echo "- adding $section ..."
last_section="$section"
fi
;;
*)
echo "- adding $fname ..."
last_section=
;;
esac
{
printf 'require.register("%s", function(exports, require, module) {\n' "$fname"
cat -- "$filename"
printf '\n});\n'
} >>"$publicdest/app.js"
done
fi
if [ "$2" = "" ]||[ "$2" = "app.js" ]; then
echo building app.js:
echo - copying template.js ...
cp $src/make-src/template.js $publicdest/app.js
echo "copying assets folder"
cp -R -- "$src/assets/." "$publicdest/"
js_source=$src/app/
for filename in $src/app/**/*.js; do
fname="${filename#$js_source}"
fname="${fname%.*}"
echo - adding $fname ...
echo require.register\(\"$fname\", function\(exports, require, module\) { >> $publicdest/app.js
cat $filename >> $publicdest/app.js
echo >> $publicdest/app.js
echo }\)\; >> $publicdest/app.js
done
if [ -n "$target" ] && [ -n "$extra_copy_dir" ]; then
mkdir -p -- "$extra_copy_dir/webui"
echo "copying $target to $extra_copy_dir/webui/$target"
cp -- "$publicdest/$target" "$extra_copy_dir/webui/$target"
fi
echo copying assets folder
cp -r $src/assets/* $publicdest/
if [ "$2" != "" ]&&[ "$3" != "" ]; then
if [ ! -d "$3/webui" ]; then
echo creating $3/webui
mkdir $3/webui
fi
echo copying $2 nach $3/webui/$2
cp $publicdest/$2 $3/webui/$2
fi
echo "### WebUI build complete ###"

1311
webui-src/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,11 +3,18 @@
"version": "1.0.0",
"description": "Retroshare's Web Interface",
"scripts": {
"watch": "rm ./styles.css && sass --watch --embed-sources --embed-source-map ./app/scss/main.scss ./styles.css",
"build": "sass --no-source-map --style=compressed ./app/scss/main.scss ./styles.css"
"watch": "sass --watch --embed-sources --embed-source-map ./app/scss/main.scss ./styles.css",
"build": "sass --no-source-map --style=compressed ./app/scss/main.scss ./styles.css && node ./make-src/build.js",
"lint": "eslint app && node ./make-src/build.js --check && echo Lint checks passed."
},
"license": "ISC",
"engines": {
"node": ">=24"
},
"devDependencies": {
"sass": "^1.60.0"
"@eslint/js": "^10.0.1",
"eslint": "^10.8.0",
"globals": "^17.8.0",
"sass": "1.97.3"
}
}

View File

@ -1,240 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
sass:
specifier: ^1.60.0
version: 1.97.3
packages:
'@parcel/watcher-android-arm64@2.5.6':
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [android]
'@parcel/watcher-darwin-arm64@2.5.6':
resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [darwin]
'@parcel/watcher-darwin-x64@2.5.6':
resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [darwin]
'@parcel/watcher-freebsd-x64@2.5.6':
resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [freebsd]
'@parcel/watcher-linux-arm-glibc@2.5.6':
resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm-musl@2.5.6':
resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-arm64-glibc@2.5.6':
resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-arm64-musl@2.5.6':
resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@parcel/watcher-linux-x64-glibc@2.5.6':
resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@parcel/watcher-linux-x64-musl@2.5.6':
resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@parcel/watcher-win32-arm64@2.5.6':
resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [win32]
'@parcel/watcher-win32-ia32@2.5.6':
resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==}
engines: {node: '>= 10.0.0'}
cpu: [ia32]
os: [win32]
'@parcel/watcher-win32-x64@2.5.6':
resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [win32]
'@parcel/watcher@2.5.6':
resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
engines: {node: '>= 10.0.0'}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
immutable@5.1.5:
resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
sass@1.97.3:
resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==}
engines: {node: '>=14.0.0'}
hasBin: true
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
snapshots:
'@parcel/watcher-android-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-arm64@2.5.6':
optional: true
'@parcel/watcher-darwin-x64@2.5.6':
optional: true
'@parcel/watcher-freebsd-x64@2.5.6':
optional: true
'@parcel/watcher-linux-arm-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm-musl@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-arm64-musl@2.5.6':
optional: true
'@parcel/watcher-linux-x64-glibc@2.5.6':
optional: true
'@parcel/watcher-linux-x64-musl@2.5.6':
optional: true
'@parcel/watcher-win32-arm64@2.5.6':
optional: true
'@parcel/watcher-win32-ia32@2.5.6':
optional: true
'@parcel/watcher-win32-x64@2.5.6':
optional: true
'@parcel/watcher@2.5.6':
dependencies:
detect-libc: 2.1.2
is-glob: 4.0.3
node-addon-api: 7.1.1
picomatch: 4.0.3
optionalDependencies:
'@parcel/watcher-android-arm64': 2.5.6
'@parcel/watcher-darwin-arm64': 2.5.6
'@parcel/watcher-darwin-x64': 2.5.6
'@parcel/watcher-freebsd-x64': 2.5.6
'@parcel/watcher-linux-arm-glibc': 2.5.6
'@parcel/watcher-linux-arm-musl': 2.5.6
'@parcel/watcher-linux-arm64-glibc': 2.5.6
'@parcel/watcher-linux-arm64-musl': 2.5.6
'@parcel/watcher-linux-x64-glibc': 2.5.6
'@parcel/watcher-linux-x64-musl': 2.5.6
'@parcel/watcher-win32-arm64': 2.5.6
'@parcel/watcher-win32-ia32': 2.5.6
'@parcel/watcher-win32-x64': 2.5.6
optional: true
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
detect-libc@2.1.2:
optional: true
immutable@5.1.5: {}
is-extglob@2.1.1:
optional: true
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
optional: true
node-addon-api@7.1.1:
optional: true
picomatch@4.0.3:
optional: true
readdirp@4.1.2: {}
sass@1.97.3:
dependencies:
chokidar: 4.0.3
immutable: 5.1.5
source-map-js: 1.2.1
optionalDependencies:
'@parcel/watcher': 2.5.6
source-map-js@1.2.1: {}

File diff suppressed because one or more lines are too long

View File

@ -33,7 +33,7 @@ WEBUI_SRC_CSS = $$PWD/webui-src/styles.css
WEBUI_SRC_IMAGE = $$PWD/data/retroshare.svg
win32-g++ {
win32 {
isEmpty(QMAKE_SH) {
# Windows native build
WEBUI_SRC_SCRIPT = $$PWD/webui-src/make-src/build.bat
@ -72,7 +72,7 @@ win32-g++ {
} else {
# create dummy files, we need it to include files on first try
system(bash webui-src/make-src/build.sh .)
system(sh webui-src/make-src/build.sh .)
WEBUI_SRC_SCRIPT = webui-src/make-src/build.sh
@ -80,19 +80,19 @@ win32-g++ {
create_webfiles_html.output = webui/index.html
create_webfiles_html.input = WEBUI_SRC_HTML
create_webfiles_html.commands = bash $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ index.html .
create_webfiles_html.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ index.html .
create_webfiles_html.variable_out = JUNK
create_webfiles_html.CONFIG = combine no_link
create_webfiles_js.output = webui/app.js
create_webfiles_js.input = WEBUI_SRC_JS
create_webfiles_js.commands = bash $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ app.js .
create_webfiles_js.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ app.js .
create_webfiles_js.variable_out = JUNK
create_webfiles_js.CONFIG = combine no_link
create_webfiles_css.output = webui/styles.css
create_webfiles_css.input = WEBUI_SRC_CSS
create_webfiles_css.commands = bash $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ styles.css .
create_webfiles_css.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ styles.css .
create_webfiles_css.variable_out = JUNK
create_webfiles_css.CONFIG = combine no_link