Compare commits

...

2254 commits

Author SHA1 Message Date
Reto Brunner 45c2fc87ee client: properly type Socket in window 2024-05-10 12:27:41 +02:00
Max Leiter 74563effa7
Merge pull request #4869 from thelounge/tsUpdate
Ts update
2024-05-04 21:49:58 -07:00
Max Leiter cbab10f416
Merge pull request #4870 from thelounge/dts
remove unused .d.ts file
2024-05-04 21:46:54 -07:00
Reto Brunner 4dfeb899b4 remove unused .d.ts file 2024-05-04 13:45:40 +02:00
Reto Brunner 3259ac596d client: fix all new linter errros 2024-05-04 12:29:57 +02:00
Reto Brunner 3fbbc39cd6 client/commands: statically import commands
Dynamic imports won't work very well with modules and we don't
really need them, it's just there to save us an import statement.

Let's flip this to a static version.
2024-05-04 12:29:57 +02:00
Reto Brunner 9ae9482223 bump all eslint related deps 2024-05-02 08:21:34 +02:00
Reto Brunner a3953405ed bump eslint to latest 8.* 2024-04-27 13:19:39 +02:00
Reto Brunner 9086bc648d bump typescript to v5 2024-04-27 13:06:30 +02:00
Reto Brunner da2572fe25 Merge remote-tracking branch 'origin/renovate/read-0.x' 2024-04-27 12:50:11 +02:00
Reto Brunner d9977df315 Merge remote-tracking branch 'origin/renovate/sqlite3-3.x' 2024-04-27 12:48:20 +02:00
Reto Brunner cc0aa5e8e5 Merge remote-tracking branch 'origin/renovate/npm-webpack-dev-middleware-vulnerability' 2024-04-27 12:47:01 +02:00
Reto Brunner 02df78b0f2 Merge remote-tracking branch 'origin/renovate/npm-express-vulnerability' 2024-04-27 12:46:42 +02:00
Reto Brunner 18b0e06855 Merge remote-tracking branch 'origin/renovate/express-4.x' 2024-04-27 12:46:27 +02:00
Reto Brunner d5db9c653b Merge remote-tracking branch 'origin/renovate/ua-parser-js-0.x' 2024-04-27 12:46:09 +02:00
Reto Brunner f7926267d9 untangle client and server
Our project was quite confused as to the boundaries between client and
server code.
This false sharing meant that it was quite hard to tell what was actually
sent to the client and what was uniquely scoped to either side.

Further, this meant that our compilation and build pipelines were very
confused and pulled in files they should not have.

This commit series tries to untangle the two. This also entails fixing
quite some typing issues.
It's hard to make this in sane, small, commits that still build at each
step (it's impossible, as fixing one type error / any type immediately lead
to further errors in a game of whack a mole).
So you'll get my actual progress in small commits that can each be reviewed,
however the earlier ones are in fact sometimes wrong and get cleaned up later
once the picture is a bit clearer.
2024-04-26 09:39:15 +02:00
Reto Brunner 8eb398c5cc server: don't throw in async callback from index requests
This was flagged as an issue by codeQL

> Server crash [High]
> The server of this route handler will terminate when an
> uncaught exception from this location escapes an
> asynchronous callback.
2024-04-21 15:49:51 +02:00
Reto Brunner 36cb75ee99 NetworkForm: disable unsafe return lints for now
The NetworkForm type is wrong, hence the compiler can't infer the type.
This needs quite some changes, so for now we just turn the linter off
for the 2 watch functions.
The whole component is too dynamic to fix easily.
2024-04-21 15:11:52 +02:00
Reto Brunner 1ec67a6605 test/sqlite: remove unused eslint directive 2024-04-21 15:11:52 +02:00
Reto Brunner 8372c5a57e test: token in init event payload is undefined not null
Doesn't matter which, code happens to emit undefined.
Adapt test expectation over writing strange || null code.
The conditional just checks for a falsey value.
2024-04-21 15:11:52 +02:00
Reto Brunner 5567f07a7c test/chan: remove users field from test
The client side fetches the user list when needed, we don't send
it over from the server.
Hence modify the test expectation.
2024-04-21 15:11:52 +02:00
Reto Brunner a200bab8bd test/chan: getFilteredClone has more fields than the test thinks
The codebase shoves various things into channel objects to transmit them
for things like channel lists etc.

This however means that the type does contains the fields and needs
to export them.

We should clean up the events so that we can get rid of all that.
But for now, we adapt the test expectation to reality.
2024-04-21 15:11:52 +02:00
Reto Brunner 91ac363cc6 components/MessageTypes/errors: fix eslint errors
This makes the code somewhat ugly, but to properly fix we need
to enforce the needed fields
2024-04-21 15:11:52 +02:00
Reto Brunner 6c9d2c36a1 components/Message: fix eslint errors 2024-04-21 15:11:52 +02:00
Reto Brunner 6241eed8f4 client/ImageViewer: fix types and guard against undefined 2024-04-21 15:11:52 +02:00
Reto Brunner 03151e0ab1 test/plugins/sqlite: fix import path 2024-04-21 15:11:52 +02:00
Reto Brunner 7f5e0f3ebf test/plugins/link: fix import path 2024-04-21 15:11:52 +02:00
Reto Brunner 5e444be37b test/models/network: fix import path 2024-04-21 15:11:52 +02:00
Reto Brunner c8664301ba test/models/msg: fix linkpreview import 2024-04-21 15:11:52 +02:00
Reto Brunner 1edb5a72c1 test/models/chan: we do not send an empty user list anymore 2024-04-21 15:11:52 +02:00
Reto Brunner 31d987283a fix mode tests 2024-04-21 15:11:52 +02:00
Reto Brunner 4ceafb653f test/client: fix import path 2024-04-21 15:11:52 +02:00
Reto Brunner f25fee4c6c previews: fix possibly undefined 2024-04-21 15:11:52 +02:00
Reto Brunner 96848c1c1b msg_preview: fix possibly undefined error 2024-04-21 15:11:52 +02:00
Reto Brunner 4b07e05491 client: add missing import for SharedMsg 2024-04-21 15:11:52 +02:00
Reto Brunner fc9805545b sharedMsg: remove userAway
userAway is purely server side and we don't send it to the client
2024-04-21 15:11:52 +02:00
Reto Brunner 82e4150cc8 server: remove type cast from change pw 2024-04-21 15:11:52 +02:00
Reto Brunner e61e356f1e server: somewhat type fix auth related functions
The auth functions are a bloody mess and need to be cleaned up.
using various callback functions and using variables as pointers makes the logic
hard to follow and hence idiotic to type too, as multiple orthogonal logic paths
are mixed up into one function.

This really needs to be untangled
2024-04-21 15:11:52 +02:00
Reto Brunner 5001d607b1 server: mark req params as unused 2024-04-21 15:11:52 +02:00
Reto Brunner 8c41356ae9 publicClient: type fix 2024-04-21 15:11:52 +02:00
Reto Brunner e2b56cf16b irc-events/message: fix types 2024-04-21 15:11:52 +02:00
Reto Brunner 92a0affba1 kick: use the user object 2024-04-21 15:11:52 +02:00
Reto Brunner edb96f683b cap: type the boolean 2024-04-21 15:11:52 +02:00
Reto Brunner 5c8951ffc3 fix extractTargetGroup typing 2024-04-21 15:11:52 +02:00
Reto Brunner c3fc54e158 ignorelist: shut up the linter 2024-04-21 15:11:52 +02:00
Reto Brunner 917fdb2a0a ignore: remove dead import 2024-04-21 15:11:52 +02:00
Reto Brunner b8400a3a46 ignore: clean up the types and conditionals
Now that ignorelist doesn't muddy the waters, we can clean up
all the funny conditional types and enforce `when`
2024-04-21 15:11:52 +02:00
Reto Brunner 071a5afda6 ignore: move ignorelist to its own command
ignorelist shares no logic with /ignore or /unignore so it shouldn't
share a file. That just makes typing awkward.
2024-04-21 15:11:52 +02:00
Reto Brunner 5274fdc21a ignore: keep happy path on the left
It is much easier to follow the control flow if error checks
are done on the indented path, immediately returning.
2024-04-21 15:11:52 +02:00
Reto Brunner b8a9fe08ab clientCertificate: remove unsafe casts 2024-04-21 15:11:52 +02:00
Reto Brunner a4afa08add ldap: type SearchOptions scope 2024-04-21 15:11:52 +02:00
Reto Brunner 4614c35486 chan: type untyped method params 2024-04-21 15:11:52 +02:00
Reto Brunner 540144c417 chan: remove cast in pushMessage 2024-04-21 15:11:52 +02:00
Reto Brunner bb7c3925c6 type serverOptions for network:options 2024-04-21 15:11:52 +02:00
Reto Brunner 9898f38de6 add todo 2024-04-21 15:11:52 +02:00
Reto Brunner 9f2c82e152 fix mentions 2024-04-21 15:11:52 +02:00
Reto Brunner 17ba07db3b fix mentions import 2024-04-21 15:11:52 +02:00
Reto Brunner 0311e5f836 add socket-events import to entry point
socket-events aren't ever imported, if we don't do that however
webpack never actually sees any code that leads to it and skips
bundling it.

So for now, do an import that has the side effect of registering
all the events until we have a proper registration in place that's
a bit more sane to call
2024-04-21 15:11:52 +02:00
Reto Brunner 4d0474b897 store: don't duplicate import 2024-04-21 15:11:52 +02:00
Reto Brunner 14b9169899 store: fix import 2024-04-21 15:11:52 +02:00
Reto Brunner 50037644c0 socket-events: fix join 2024-04-21 15:11:52 +02:00
Reto Brunner 7287c6bcaa remove dead import 2024-04-21 15:11:52 +02:00
Reto Brunner bfca0ca612 fix more 2024-04-21 15:11:52 +02:00
Reto Brunner 300bd4c84c add timestamp to NotificationOptions 2024-04-21 15:11:52 +02:00
Reto Brunner 42ea66c343 socket-events/msg: fix errors 2024-04-21 15:11:51 +02:00
Reto Brunner 1565eb8d05 socket-events/msg: if/else chains are not a switch replacement
If we switch on a field, use switch for god's sake.
If/elif chains are for cases where you have multiple selectors.
2024-04-21 15:11:51 +02:00
Reto Brunner 29750a3e51 ClientChan: does need a user array after all 2024-04-21 15:11:51 +02:00
Reto Brunner 3ea5170e6a socket-events: fix network:status 2024-04-21 15:11:51 +02:00
Reto Brunner fe4f497fad fix socket-event: network 2024-04-21 15:11:51 +02:00
Reto Brunner c20cd6bda1 publicClient: add FIXME and ignore the type mismatch
The publicClient interface is utterly horrific.
It allows any client to inject arbitrary events into the socket.io
event stream.
This should get wrapped into a "plugin" event so that it can get properly
typed, better yet, this should get removed completely.
2024-04-21 15:11:51 +02:00
Reto Brunner 1c4ce5d4a5 fix sync_sort:channels emitter 2024-04-21 15:11:51 +02:00
Reto Brunner 9c4d24d1f7 fix join socket type 2024-04-21 15:11:51 +02:00
Reto Brunner 35e38d13c4 client: properly type the emit method
This breaks the world -.-
2024-04-21 15:11:51 +02:00
Reto Brunner bf7eb0e727 network event: remove unused array
All the network events only ever emit a single copy
There's no point in wrapping it into an array
2024-04-21 15:11:51 +02:00
Reto Brunner 5ee9c2b338 type Server 2024-04-21 15:11:51 +02:00
Reto Brunner e15b121080 remove obsolete error override 2024-04-21 15:11:51 +02:00
Reto Brunner 98452ccc18 remove obsolete import 2024-04-21 15:11:51 +02:00
Reto Brunner a8e7022d04 fix search event params 2024-04-21 15:11:51 +02:00
Reto Brunner 60486bf5e3 server: fix init client 2024-04-21 15:11:51 +02:00
Reto Brunner 46f3fd9682 server: fix push subscription 2024-04-21 15:11:51 +02:00
Reto Brunner 56215382a3 server: remove static props which are currently unused 2024-04-21 15:11:51 +02:00
Reto Brunner 9ab9ad0f56 socket-events: fix up init 2024-04-21 15:11:51 +02:00
Reto Brunner 0660a8772c server: fix getFilteredClone of chan 2024-04-21 15:11:51 +02:00
Reto Brunner f5c691f37b wip: unbork init progress 2024-04-21 15:11:51 +02:00
Reto Brunner 0067c30273 Split sort event
The sort event bundled networks and channels for no reason at all.
They share none of the actual logic, so combining them just makes
the typing poor but serves no benefit.
2024-04-21 15:11:51 +02:00
Reto Brunner 843db1727b server: actually type the socket 2024-04-21 15:11:51 +02:00
Reto Brunner e9ef59b641 fix bad typing
There were quite some errors, where the type was passed the wrong way
```
// This is invalid
"change-password": ({ old_password: string, new_password: string, verify_password: string})

// What was actually meant
"change-password": (data: { old_password: string, new_password: string, verify_password: string})
```

The whole callback function is also very verbose as is, with fluff we don't need.
It's always a function that returns void, so there's no real information to be gained
by spelling it out time and time again.

Let's use a helper type that just accepts the payload.
That should make the above error impossible to do.
2024-04-21 15:11:51 +02:00
Reto Brunner fceffd42b9 fix missing import 2024-04-21 15:11:51 +02:00
Reto Brunner b89b0cad53 client: id is always a string, not a number 2024-04-21 15:11:51 +02:00
Reto Brunner c869ea9a73 sharedchans does not have users 2024-04-21 15:11:51 +02:00
Reto Brunner 9aee3e3e98 Some whitespace is good mkey 2024-04-21 15:11:51 +02:00
Reto Brunner 636b5c5b04 models/network: unfuck client export 2024-04-21 15:11:51 +02:00
Reto Brunner 6984e8f25a unused import 2024-04-21 15:11:51 +02:00
Reto Brunner e43cbb139c remove all server files from compilation 2024-04-21 15:11:51 +02:00
Reto Brunner e57e547b74 further chan fixes 2024-04-21 15:11:51 +02:00
Reto Brunner 3217536245 searchresponse 2024-04-21 15:11:51 +02:00
Reto Brunner 194b4e1a2f import fix 2024-04-21 15:11:51 +02:00
Reto Brunner 88c8830a17 chatuserlist 2024-04-21 15:11:51 +02:00
Reto Brunner 7073584f1c fix msg event 2024-04-21 15:11:51 +02:00
Reto Brunner 8e6920af1d configuration 2024-04-21 15:11:51 +02:00
Reto Brunner 7bc184b252 changelog data type 2024-04-21 15:11:51 +02:00
Reto Brunner 4d237600d5 changelog: don't type assert to a broken type
The mandatory fields are unset, stop lying to the compiler
2024-04-21 15:11:51 +02:00
Reto Brunner 383907c2b8 Use SharedTypes 2024-04-21 15:10:45 +02:00
Reto Brunner f0ee3be6fb wip: config 2024-04-21 15:10:45 +02:00
Reto Brunner 12a0b0b6f9 network 2024-04-21 15:10:45 +02:00
Reto Brunner d716402da2 mention 2024-04-21 15:10:45 +02:00
Reto Brunner d0b71aba32 shared: extract chan + user 2024-04-21 15:10:45 +02:00
Reto Brunner 3f0ee6a961 move chan enums 2024-04-21 15:10:45 +02:00
Reto Brunner b67e4699f5 wip sharedmsg in client 2024-04-21 15:10:45 +02:00
Reto Brunner 68ba13ca12 wip: searchquery 2024-04-21 15:10:45 +02:00
Reto Brunner 3eb19135f5 wip: msg 2024-04-21 15:10:41 +02:00
Max Leiter 549c445853
Merge pull request #4856 from thelounge/generateConfigDoc
fix generate-config-doc.js
2024-04-07 19:38:13 -07:00
Reto Brunner 2466c1b1e4 fix generate-config-doc.js
It errored out with
> Error: Cannot find module '../server/log'
Which is expected, but we don't really need it, we can just open code
the log functions
2024-04-07 16:22:08 +02:00
Reto Brunner f5867c3643 v4.4.3
Bump version to kick CI/CD for the actual deployment
2024-04-06 13:48:04 +02:00
Reto Brunner 231c498def release workflow: fix broken npm update
The release workflow wants to use --provenance but the update fails:

Run npm install -g npm
npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/share/man/man7
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/share/man/man7'
npm ERR!  [Error: EACCES: permission denied, mkdir '/usr/local/share/man/man7'] {
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'mkdir',
npm ERR!   path: '/usr/local/share/man/man7'
npm ERR! }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

So we fix it by just telling the action what we want rather than monkey patching it.

Link: https://github.com/thelounge/thelounge/actions/runs/8580746748/job/23517165611
2024-04-06 13:31:48 +02:00
Reto Brunner eeaec413d6 v4.4.2 2024-04-03 08:20:36 +02:00
Reto Brunner 515f894c13 changelog: don't break if author is nil
The author field can somehow be null for whatever reason...
Guard the script against blowing up
2024-04-01 15:15:50 +02:00
renovate[bot] e8f6ba5b08
chore(deps): update dependency @types/ua-parser-js to v0.7.39 2024-04-01 00:49:35 +00:00
renovate[bot] 07276bbde4
chore(deps): update dependency @types/express to v4.17.21 2024-04-01 00:49:23 +00:00
renovate[bot] 9ad92e1860
fix(deps): update dependency express to v4.19.2 [security] 2024-03-28 00:33:05 +00:00
renovate[bot] 7923d4a2cd
chore(deps): update dependency webpack-dev-middleware to v5.3.4 [security] 2024-03-23 16:07:59 +00:00
renovate[bot] 9248358169
chore(deps): update dependency @types/sqlite3 to v3.1.11 2024-03-01 02:16:09 +00:00
renovate[bot] 6ab52bc9a9
chore(deps): update dependency @types/read to v0.0.32 2024-03-01 02:15:57 +00:00
Reto Brunner 48213955b9 v4.4.2-rc.1 2024-02-19 19:52:37 +01:00
Reto Brunner 682b3b91aa rc changelog 2024-02-19 19:46:46 +01:00
Reto Brunner be3e27aa19 Merge remote-tracking branch 'origin/pull/4231' 2024-02-19 13:50:00 +01:00
Reto Brunner c09f751552 Merge remote-tracking branch 'origin/pull/4834' 2024-02-19 13:45:53 +01:00
Reto Brunner fb5864ee00 Merge branch 'linkifyOverride' 2024-02-19 13:43:00 +01:00
Reto Brunner 3bd5b704c7 Merge remote-tracking branch 'origin/renovate/node-17.x' 2024-02-19 13:37:06 +01:00
Reto Brunner 139ce47b73 update @types/mousetrap 2024-02-19 13:34:46 +01:00
Zach Bloomquist 45563d9a59
server: remove version from CTCP response 2024-02-01 00:09:45 -05:00
renovate[bot] e2fda1fb84
chore(deps): update dependency @types/node to v17.0.45 2024-02-01 03:42:37 +00:00
renovate[bot] a77fbb894f
chore(deps): update dependency @types/mousetrap to v1.6.15 2024-02-01 03:42:30 +00:00
renovate[bot] fe50a90235
chore(deps): update dependency @types/lodash to v4.14.202 2024-02-01 01:15:55 +00:00
Reto Brunner a8be84028c Merge rm-node-16 2024-01-28 10:55:12 +01:00
Max Leiter 25e55ce75c
Merge pull request #4815 from thelounge/renovate/sqlite3-5.x
chore(deps): update dependency sqlite3 to v5.1.7
2024-01-27 15:56:29 -08:00
Max Leiter 113e9bd2fb Remove Node.js 16 from package.json and testing matrix
Node.js 16 entered EOL in September 2023 (https://nodejs.org/en/blog/announcements/nodejs16-eol)
2024-01-27 15:55:55 -08:00
Max Leiter 2b146ba3e6
Merge pull request #4825 from thelounge/testingSetup
Testing setup
2024-01-27 15:40:09 -08:00
Max Leiter f95dd29a0d
Merge pull request #4811 from thelounge/renovate/textcomplete-textarea-0.x
chore(deps): update dependency @textcomplete/textarea to v0.1.13
2024-01-27 15:40:01 -08:00
Max Leiter 91dc719c93
Merge branch 'master' into testingSetup 2024-01-27 15:34:07 -08:00
Max Leiter 5af893db3a
Merge pull request #4826 from thelounge/huskyDie
Remove husky, add githooks-install
2024-01-27 15:33:26 -08:00
Nachtalb daabb76781
Add shorcut to navigate between channels with undread msgs 2024-01-27 22:50:10 +01:00
Reto Brunner 393d0a63b7 Remove husky, add githooks-install
Fixes: https://github.com/thelounge/thelounge/issues/4452
2024-01-27 16:59:46 +01:00
Reto Brunner 037fc479b8 test: be specific as to which command we want to invoke 2024-01-27 15:41:46 +01:00
Reto Brunner 646bafab99 mocha: move spec to invocation
If we specify the spec in the config file, we can't manually
specify a specific test file from the cli.

This is annoying, as the alternative is copying out the full
package.json blurb into the shell.

Rather, give the spec in the invocation and add a helper
that makes testing a specific file simple.

With this `yarn test:nospec test/plugins/link.ts` will only run
tests within that file
2024-01-27 15:34:13 +01:00
Reto Brunner d4c77c74f6 test:mocha remove duplicate flags
We already specify color in the config file.
ts-node is already required in the config file
2024-01-27 15:26:37 +01:00
Reto Brunner eeefeb229c mocharc: Remove interactive
Interactive isn't a thing according to the help output or the
config docs
2024-01-27 15:24:09 +01:00
Reto Brunner 29c5323bfd test:mocha: webpack doesn't switch on NODE_ENV=test
So we might as well not complicate the cli for no reason
2024-01-27 14:11:30 +01:00
Reto Brunner a12ddc75d8 test:mocha don't run coverage report
Tests should run the tests, not the coverage.
Frequently one is debugging a test, the coverage won't change
between runs but it delays the cycle considerably.

Rather, if one wants to look at the coverage, one should use
the "coverage" command
2024-01-27 14:11:30 +01:00
Reto Brunner dd24cb1300 linkify: simplify noscheme detection logic
Overriding the built in is poor form, as this prevents adding
a new type handler with its own normalize handler.

We only ever want to override protocol-less URLs to http, so
we just do so explicitly in the "//" schema normalizer.

This also means that we don't need all that type conversion dance,
we simply set the schema to null when we patch it and filter on the
schema directly
2024-01-21 21:18:09 +01:00
SoniEx2 ae6bae69ac linkify: Add web+ schema support
Co-Authored-By: Reto Brunner <reto@slightlybroken.com>
2024-01-21 17:47:32 +01:00
renovate[bot] b5372e3ed7
chore(deps): update dependency sqlite3 to v5.1.7 2024-01-06 10:39:32 +00:00
Reto Brunner d15998d919 Merge renovate/content-disposition-0.x 2024-01-04 19:04:00 +01:00
Reto Brunner 436bf6a180 Merge renovate/linkify-it-3.x 2024-01-04 19:03:40 +01:00
renovate[bot] 1d2fdd95b0
chore(deps): update dependency @types/linkify-it to v3.0.5 2024-01-01 03:42:56 +00:00
renovate[bot] eaa70caad7
chore(deps): update dependency @types/is-utf8 to v0.2.3 2024-01-01 03:42:48 +00:00
renovate[bot] aa95032760
chore(deps): update dependency @types/content-disposition to v0.5.8 2024-01-01 01:41:48 +00:00
renovate[bot] e636121d7a
chore(deps): update dependency @textcomplete/textarea to v0.1.13 2024-01-01 01:41:39 +00:00
Max Leiter 083abae750
Merge pull request #4783 from thelounge/router-api
router: don't use next() in router guards
2023-12-26 16:49:37 -08:00
Max Leiter 01cfe3d19d
Merge pull request #4807 from flotwig/fixup-generate-config-docs-script
scripts: fix generate-config-doc, handle usage errors
2023-12-26 16:49:20 -08:00
Reto Brunner 7f0b721790 add storage cleaner
Introduce the ability to clean up old messages from the sqlite db.
The StoragePolicy can be chosen by the user. Currently there's
two versions, delete everything based on age is the obvious.

The other is for the data hoarders among us. It'll only delete
message types which can be considered low value... Types with
a time aspect like away / back... joins / parts etc.

It tries to do that in a sensible way, so that we don't block
all other db writers that are ongoing.
The "periodically" interval is by design not exposed to the user.
2023-12-26 12:00:53 +01:00
Reto Brunner edb1226b47 sqlite: add msg type index to speed up cleaner 2023-12-24 16:55:45 +01:00
Reto Brunner b0ca8e51fb wire up storage cleaner upon server start 2023-12-24 16:55:45 +01:00
Reto Brunner 21b1152f53 cleaner: expose cli task to do cleaning + vacuum
Make the cleaner available to users by exposing it as a subcommand
to thelounge storage.

This is recommended to be run whenever the storage policy significantly
changes in a way that makes many messages eligible for deletion.
The cleaner would cope, but it'll be inefficient and can take many hours.
Due to how storage works in sqlite, the space would not actually be
given back to the OS, just marked for future writes.
Hence this also runs a vacuum to compact the DB as much as it can.
2023-12-24 16:55:45 +01:00
Reto Brunner 74aff7ee5a introduce storage cleaner
Once this is getting hooked up, it'll periodically delete old
messages.

The StoragePolicy can be chosen by the user, currently there's
two versions, delete everything based on age is the obvious.

The other is for the data hoarders among us. It'll only delete
message types which can be considered low value... Types with
a time aspect like away / back... joins / parts etc.

It tries to do that in a sensible way, so that we don't block
all other db writers that are ongoing.
The "periodically" interval is by design not exposed to the user.
2023-12-24 16:55:45 +01:00
Reto Brunner 14d9ff247d sqlite: implement deleteMessages
This is laying the foundation to build a cleaning task that's
sort of database agnostic.
All calls are done by acting on a "DeletionRequest" so interpretation
of the config will go through a single point
2023-12-23 21:08:07 +01:00
Reto Brunner aec8d0b033 sqlite: accept db connection string
This allows us to inject a memory db during testing
2023-12-23 21:08:07 +01:00
Reto Brunner 60ddf17124 sqlite: use variadic function for serialize_run
This makes the usage of the function a bit nicer
2023-12-23 21:08:07 +01:00
dependabot[bot] 20227b174c
build(deps): bump @babel/traverse from 7.18.9 to 7.23.6
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.18.9 to 7.23.6.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.6/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-23 10:07:23 +00:00
Reto Brunner d18182da8b Merge 'renovate/cheerio-0.x' 2023-12-20 07:40:09 +01:00
Reto Brunner ea35040b42 Merge renovate/bcryptjs-2.x 2023-12-20 07:38:59 +01:00
Reto Brunner 97f553eea8 cli: don't fail if stderr is not in json format
A user reported in the IRC chan that installing packages fails with

```
2023-12-13 20:02:34 [INFO] Installing thelounge-theme-solarized v1.1.9...
undefined:1
(node:3329) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
^
SyntaxError: Unexpected token '(', "(node:3329"... is not valid JSON
```

Now, this happens as yarn helpfully prints a deprecation warning
that is shown in the stack trace.

Let's assume that we may get non json messages and log them at debug, as we
don't know their severity.
2023-12-13 22:55:53 +01:00
Zach Bloomquist 6603c1a6e6
scripts: fix generate-config-doc, handle usage errors 2023-12-12 11:52:45 -05:00
renovate[bot] 73a529acea
chore(deps): update dependency @types/cheerio to v0.22.35 2023-12-01 01:11:42 +00:00
renovate[bot] 2f40d9dbcc
chore(deps): update dependency @types/bcryptjs to v2.4.6 2023-12-01 01:11:29 +00:00
Reto Brunner d1561f8ebc sqlite: return new version in downgrade()
We want to give the caller the current version, not the last
version we rolled back, fix that
2023-11-06 07:52:59 +01:00
Reto Brunner ec75ff00cb sqlite: don't modify global array during tests 2023-11-06 07:52:59 +01:00
Reto Brunner 884a92c74b sqlite: fix typo fetch_rollbacks 2023-11-06 07:52:59 +01:00
Reto Brunner 77b64c546b dont' crash on rDNS failure 2023-11-06 07:36:52 +01:00
Reto Brunner cc59e6b578 Merge renovate/is-utf8-0.x 2023-11-04 12:22:05 +01:00
Reto Brunner fb1d79f5fa Merge renovate/bcryptjs-2.x 2023-11-04 12:21:46 +01:00
Reto Brunner 100ff3c198 Merge renovate/actions-setup-node-4.x 2023-11-04 12:19:04 +01:00
Reto Brunner d893feff1c Merge renovate/mousetrap-1.x 2023-11-04 12:18:47 +01:00
Reto Brunner 88a5fef4ea Merge renovate/lodash-4.x 2023-11-04 12:17:54 +01:00
Reto Brunner 5b64ecbe68 Merge renovate/content-disposition-0.x 2023-11-04 12:11:33 +01:00
Reto Brunner 5024acd7dc Merge renovate/cheerio-0.x 2023-11-04 12:10:14 +01:00
Reto Brunner bbfada251c Merge renovate/npm-postcss-vulnerability 2023-11-04 12:09:49 +01:00
Reto Brunner 8cec292f2c Merge dependabot/npm_and_yarn/get-func-name-2.0.2 2023-11-04 12:08:42 +01:00
Reto Brunner 22ae594cc3 bump caniuse-lite 2023-11-04 12:04:51 +01:00
Reto Brunner 1c6bec2323 Merge branch 'cliMigrations' 2023-11-04 11:59:41 +01:00
Reto Brunner 9105fbc23a Merge branch 'emoji' 2023-11-04 11:53:30 +01:00
Reto Brunner 8c54cd50d8 don't crash on rDNS failure
Node apparently throws even on valid ipv6 input in certain environments,
probably due to the DNS server returning SERVFAIL.
Guard against it and fallback with the plain IP

Fixes: https://github.com/thelounge/thelounge/issues/4768
2023-11-04 11:45:11 +01:00
renovate[bot] 59de6afd3f
chore(deps): update dependency @types/is-utf8 to v0.2.2 2023-11-01 00:33:46 +00:00
renovate[bot] b506966b08
chore(deps): update dependency @types/bcryptjs to v2.4.5 2023-11-01 00:33:33 +00:00
renovate[bot] 785ec0a0e2
chore(deps): update actions/setup-node action to v4 2023-10-24 16:50:41 +00:00
renovate[bot] 250433c875
chore(deps): update dependency @types/mousetrap to v1.6.13 2023-10-19 10:52:51 +00:00
renovate[bot] d4d5a8e386
chore(deps): update dependency @types/lodash to v4.14.200 2023-10-19 08:04:02 +00:00
renovate[bot] bcca111a4d
chore(deps): update dependency @types/content-disposition to v0.5.7 2023-10-19 02:00:59 +00:00
renovate[bot] b686059c6b
chore(deps): update dependency @types/cheerio to v0.22.33 2023-10-19 02:00:46 +00:00
renovate[bot] ff77a33663
chore(deps): update dependency postcss to v8.4.31 [security] 2023-10-08 01:28:22 +00:00
dependabot[bot] d308e74183
build(deps): bump get-func-name from 2.0.0 to 2.0.2
Bumps [get-func-name](https://github.com/chaijs/get-func-name) from 2.0.0 to 2.0.2.
- [Release notes](https://github.com/chaijs/get-func-name/releases)
- [Commits](https://github.com/chaijs/get-func-name/commits/v2.0.2)

---
updated-dependencies:
- dependency-name: get-func-name
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-28 05:21:42 +00:00
Reto Brunner f999db99c7 Merge branch 'renovate/linkify-it-3.x' 2023-09-18 21:03:10 +02:00
Reto Brunner 76c896aea2 Merge branch 'renovate/bcryptjs-2.x' 2023-09-18 20:22:50 +02:00
dependabot[bot] 08413c7b6b
build(deps): bump word-wrap from 1.2.3 to 1.2.5
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.5.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.5)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 18:13:29 +00:00
renovate[bot] 48301b1ca3
chore(deps): update dependency @types/bcryptjs to v2.4.4 2023-09-16 19:28:31 +00:00
Reto Brunner 03795a2718 router: don't use next() in router guards
Vue wants to get rid of the next call.
https://router.vuejs.org/guide/advanced/navigation-guards.html#Optional-third-argument-next

For one of the router guards, it's easy enough to do so let's do
that.
2023-09-16 13:17:06 +02:00
renovate[bot] 2985727996
chore(deps): update dependency @types/linkify-it to v3.0.3 2023-09-01 01:55:02 +00:00
Max Leiter 9f05a75c39
Merge pull request #4770 from thelounge/smallTScleanup 2023-07-31 11:04:08 -07:00
Reto Brunner c0b38d4762 store: use return type over a type cast 2023-07-31 10:50:48 +02:00
Reto Brunner 2878f87879 Respect bind setting for all outgoing requests
So far the bind config only impacted the IRC connections.
However, nothing in our doc comment says that this is intentional.

bind
Set the local IP to bind to for outgoing connections.

This commit fixes the leak and uses it for all outgoing requests
as described by the docstring.
2023-07-16 12:02:22 +02:00
dependabot[bot] 447a237fc6
build(deps): bump semver from 7.3.5 to 7.5.2
Bumps [semver](https://github.com/npm/node-semver) from 7.3.5 to 7.5.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.3.5...v7.5.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-16 09:56:00 +00:00
Reto Brunner 430a865e9f update dependency postcss to v8.4.26 2023-07-16 11:52:16 +02:00
Reto Brunner 816b7686e3 update dependency @types/chai to v4.3.5 2023-07-16 11:50:19 +02:00
Reto Brunner 2e019a2fdb update dependency @types/lodash to v4.14.195 2023-07-16 11:49:15 +02:00
Reto Brunner 4f9ca3e192 update dependency @vue/test-utils to v2.4.0 2023-07-16 11:45:47 +02:00
Reto Brunner 57c4d5513c update dependency webpack-hot-middleware to v2.25.4 2023-07-16 11:42:58 +02:00
Reto Brunner 3e21bfcbea update dependency cheerio to v1.0.0-rc.12 2023-07-16 11:35:49 +02:00
Reto Brunner 607b9fc96a update emoji 2023-07-15 10:41:43 +02:00
Reto Brunner 1a1153aed6 use shebang for generate-emoji script 2023-07-15 10:40:44 +02:00
renovate[bot] 54ff563247
chore(deps): update dependency postcss to v8.4.26 2023-07-14 20:02:42 +00:00
Reto Brunner ed0a47fe2c bump emoji-regex to latest
Fixes: https://github.com/thelounge/thelounge/issues/4761
2023-07-12 08:24:58 +02:00
Reto Brunner 3af4ad1076 Respect bind setting for all outgoing requests
So far the bind config only impacted the IRC connections.
However, nothing in our doc comment says that this is intentional.

> ### bind
> Set the local IP to bind to for outgoing connections.

This commit fixes the leak and uses it for all outgoing requests
as described by the docstring.
2023-06-25 19:30:52 +02:00
Reto Brunner 79fae26f39 test/storage: use helper for url creation
We keep repeating ourselves, let's move that into a helper instead.
In order to get a sane host, we fix the listener to 127.0.0.1
else we get the unspecified :: ipv6 addr (on suitable hosts),
which isn't useful.
2023-06-25 19:30:52 +02:00
Reto Brunner c6b1913b91 test/link: use helper for url creation
We keep repeating ourselves, let's move that into a helper instead.
In order to get a sane host, we fix the listener to 127.0.0.1
else we get the unspecified :: ipv6 addr (on suitable hosts),
which isn't useful.
2023-06-25 19:08:23 +02:00
Reto Brunner 071ad96d9b Merge branch 'signin' 2023-06-25 10:15:21 +02:00
Reto Brunner 2ef8b37009 sqlite: add migrations support and introduce primary key
Add the ability to migrate our db in the upwards direction.
Use the facility to add primary keys to our messages table.
This should allow work like jumping to messages and the likes.

This also introduces the framework for rollback, without actually
hooking it up.
This should be easy enough to do when the need arises.
2023-06-24 14:50:39 +02:00
Reto Brunner 8aa5e33b1d Fix semver for prerelease versions #4744 2023-06-24 14:35:41 +02:00
Reto Brunner 43a2b397a2 Add comments explaining behavior when echo-message is not available 2023-06-24 14:32:33 +02:00
Reto Brunner c43a47afc1 Merge branch 'applePush' 2023-06-24 14:31:23 +02:00
Reto Brunner 14575c94cf Merge branch 'frameworkInternals' 2023-06-24 14:28:48 +02:00
renovate[bot] 303f53fe72
chore(deps): update dependency @vue/test-utils to v2.4.0 2023-06-23 00:52:37 +00:00
renovate[bot] 06f1387f7b
chore(deps): update dependency webpack-hot-middleware to v2.25.4 2023-06-21 23:04:16 +00:00
Reto Brunner c5326e8795 Sign in: use v-model
There's no need to mess with DOM elements, we can use the normal
v-model approach for both username and password
2023-06-18 15:20:08 +02:00
Reto Brunner 355c5d6fa4 v4.4.1 2023-06-13 08:24:37 +02:00
Kufat 7ac2a6fd77 Fix semver for prerelease versions
Noticed this breakage while trying to install a plugin on 4.4.1-rc2.

```
> semver.default.satisfies("4.4.1-rc2", ">=4.3.0")
false
> semver.default.satisfies("4.4.1-rc2", ">=4.3.0", {includePrerelease: true})
true
```
2023-06-01 08:03:07 -04:00
renovate[bot] c4879fdbba
fix(deps): update dependency cheerio to v1.0.0-rc.12 2023-06-01 02:09:05 +00:00
Val Lorentz 4255c1cdec Add comments explaining behavior when echo-message is not available 2023-05-30 22:09:39 +02:00
Reto Brunner ae9d312b2a v4.4.1-rc.2 2023-05-27 08:51:42 +02:00
renovate[bot] f7c6ba5eb1
chore(deps): update dependency @types/lodash to v4.14.195 2023-05-26 23:27:55 +00:00
Reto Brunner 4d60d9c282 bump socket.io-parser from 4.2.1 to 4.2.3 2023-05-25 07:36:54 +02:00
dependabot[bot] af49ef21ea
build(deps): bump socket.io-parser from 4.2.1 to 4.2.3
Bumps [socket.io-parser](https://github.com/socketio/socket.io-parser) from 4.2.1 to 4.2.3.
- [Release notes](https://github.com/socketio/socket.io-parser/releases)
- [Changelog](https://github.com/socketio/socket.io-parser/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/socket.io-parser/compare/4.2.1...4.2.3)

---
updated-dependencies:
- dependency-name: socket.io-parser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-25 05:33:59 +00:00
Reto Brunner 7a9ddc01e1 settings: make missing_field msg descriptive
The "missing_fields" error triggers on any missing field (duh).
"Please enter a new password" is not a sensible string for that.
2023-05-23 08:24:20 +02:00
Reto Brunner 8f08cf3d0b client: fix password change input
The TS rewrite dropped the form that was expected to be passed
as props.
That lead to the password change being borked, as the fields
were always set to "null".
We don't need a form, can just use refs here.
2023-05-23 08:18:20 +02:00
Reto Brunner e05871fd2f 4.4.1-rc.1
Co-Authored-By: Max Leiter <maxwell.leiter@gmail.com>
2023-05-21 14:42:30 +02:00
Reto Brunner ede48ab034 Merge branch 'previewFix' 2023-05-20 10:10:50 +02:00
Reto Brunner 4c6fa550aa Merge branch 'nodeEOL' 2023-05-20 10:09:48 +02:00
Reto Brunner 9388960497 linkPreview: Pass channel prop
Else the update logic doesn't work and we don't show
the next / prev buttons
2023-05-15 09:38:09 +02:00
Reto Brunner 7bce779254 Remove unused code 2023-05-15 09:37:40 +02:00
Reto Brunner a7b85db990 v4.4.0 2023-05-13 13:21:12 +02:00
Reto Brunner f4ef11de3f v4.4.0-rc.1 2023-05-06 10:39:34 +02:00
Reto Brunner 3a63484762 Merge branch 'changelog' 2023-05-06 10:36:58 +02:00
Reto Brunner 04b2bf036b bump socket.io to 4.6.1 2023-05-04 23:39:07 +02:00
Max Leiter 3066f48a69
Merge pull request #4728 from thelounge/settingsFormProp 2023-05-01 11:43:07 -07:00
renovate[bot] ed40c83a2b
Update dependency @types/chai to v4.3.5 2023-05-01 02:39:13 +00:00
Reto Brunner f21f665384 Add changelog for 4.4.0
Co-Authored-by: Max Leiter <maxwell.leiter@gmail.com>
2023-04-29 13:52:18 +02:00
Reto Brunner 12d9ef34f0 Client/Settings: Remove bogus settings-form prop
As is this has no effect, other than looking weird in html:
<div settings-form="[object HTMLFormElement]"><div><h2>Native app</h2>
2023-04-29 11:52:34 +02:00
Reto Brunner 9ee1cf13a8 Publish to npm with provenance 2023-04-25 22:36:42 +02:00
Lenore ba1a4206a6 fix motd display to match settings 2023-04-25 00:49:57 +02:00
Reto Brunner 59cf29ef4a workflows: bump Windows + Mac node versions to 18 2023-04-23 21:48:39 +02:00
Reto Brunner 8e43d8083d node: remove v14, add v20 to build matrix
EOL date reached.
2023-04-23 21:46:53 +02:00
Pavel Djundik 3cd0a75ac2 Publish to npm with provenance
Ref: https://github.blog/changelog/2023-04-19-npm-provenance-public-beta/
2023-04-23 12:11:08 +03:00
Reto Brunner 21d1dbaad6 Unbreak nick colors for existing themes
https://github.com/thelounge/thelounge/pull/4649 broke existing
themes by removing the colored-nicks class from chat.

Considering that we don't bump the major version, keep backwards
compatibility for now
2023-04-17 01:11:35 +02:00
Reto Brunner 90ad06a29a Fix load of channels from user config
While the commits that caused the problem have been reverted,
this still adds test cases to it and make the loading more robust.
2023-04-08 13:46:13 +02:00
Val Lorentz 0c7cc85184 Fix load of channels from user config
Network.export() only writes the "type" key if it's a ChanType.QUERY;
so the config on disk has no "type".

This causes it to be undefined when loading, which breaks various other
checks, and then drops it the next time the config is saved.
2023-04-08 12:38:06 +02:00
Reto Brunner 3be805bd38 sqlite: Add rollback support
This enables db migrations to be undone, or "down migrated".
The down migration shouldn't be done automatically
as it could lead to severe data loss if that were done.
Hence, we still hard fail if we encounter a version lower than what
we have in the DB.

A CLI will be added in a later commit that allows users to explicitly
do that.
2023-03-31 11:34:26 +02:00
Reto Brunner e25c296901 push: remove iOS warning
iOS 16.4 introduced webpush, we can get rid of the special case
in our settings panel.
2023-03-28 10:00:56 +02:00
Max Leiter 4babd17383
Merge pull request #4715 from progval/test-server-teardown
tests/server: Tear down test fixtures in the order they were setup
2023-03-20 13:54:06 -07:00
Max Leiter b408843ff1
Merge pull request #4717 from thelounge/changelog
Inline logger into changelog script
2023-03-20 13:53:41 -07:00
Reto Brunner 0f3487c533 Inline logger into changelog script
We can't really import easily from our build without it being
brittle. TL isn't meant to be used as a library.

Instead, just inline the logger as it is trivial enough.
2023-03-19 23:49:42 +01:00
Reto Brunner 21ada132b1 v4.4.0-pre.2 2023-03-19 22:07:59 +01:00
Reto Brunner 2f162daee1 Revert "models/chan: don't force existence of constructor properties"
This reverts commit e31c95e32d.
2023-03-19 21:58:14 +01:00
Reto Brunner 3ac9c36d95 Revert "user: don't force existence of constructor properties"
This reverts commit c3e3322a79.
2023-03-19 21:58:14 +01:00
Reto Brunner c30da27f95 Revert "network: don't force existence of constructor properties"
This reverts commit 429efb0c3c.
2023-03-19 21:58:14 +01:00
Reto Brunner 30a3ba489a 4.4.0-pre.1 2023-03-19 13:43:01 +01:00
Reto Brunner 0dca3954f4 Add changelog entry for v4.4.0-pre.1 2023-03-19 13:35:26 +01:00
Reto Brunner e8b6434144 Clean up command input code 2023-03-19 12:57:08 +01:00
Val Lorentz edc6f77c64
add setup 2023-03-18 07:50:35 +01:00
Val Lorentz 0dd74a93bf tests/server: Tear down test fixtures in the order they were setup
if for whatever reason before() fails to import the server, it causes after()
to fail on the first line, so it doesn't restore stubs; causing other errors
to be printed in other tests ("TypeError: Attempted to wrap warn which is
already wrapped")
2023-03-17 17:12:29 +01:00
Reto Brunner 4e954b919c server/client: refactor command input
Keep happy path on the left and try to return as early
as we can to help the reader understand the logic better

The function is too large to be able to quickly scan an if / else
chain and see the function return at the end
2023-03-17 11:28:54 +01:00
Reto Brunner eb509f7100 Fix config typing and make Client easier to test 2023-03-17 11:03:50 +01:00
Reto Brunner 845dabad53 Fix sqlite query invocation in test
46da1abba4
changed the types of the db functions... fix our code to deal with it.
2023-03-17 10:57:21 +01:00
Reto Brunner 6b00ccf82b update dependency webpack to v5.76.0 2023-03-17 10:19:43 +01:00
renovate[bot] 34a01c2dd1
chore(deps): update dependency sqlite3 to v5.1.6 2023-03-15 19:08:51 +00:00
Val Lorentz 320075e376 Remove override of UserConfig 2023-03-15 11:49:13 +01:00
Val Lorentz d58fb84565 Fix test wording 2023-03-15 08:40:53 +01:00
Val Lorentz a049a01aeb Client: move socket connection out of the constructor
It will make it easier to write tests for what used to be in
the connect() method
2023-03-15 08:40:53 +01:00
Val Lorentz 76098d7e76 Fix incorrect typing of dehydrated networks and channels
Client and ClientManager deal with both 'dehydrated' channels/networks (ie. directly
from JSON configuration) and the 'rehydrated' ones (classes, with socket objects,
message arrays, etc.).

However, because their attributes are similar, both types were used interchangeably,
which becomes an issue when splitting Client's configuration loading into smaller
methods.
2023-03-15 08:40:53 +01:00
renovate[bot] a67cee1ee4
chore(deps): update dependency webpack to v5.76.0 [security] 2023-03-14 22:38:59 +00:00
Reto Brunner efd24fd12c packaging: Use an include list in package.json 2023-03-14 07:35:50 +01:00
renovate[bot] bc4c3082b8
chore(deps): update dependency sqlite3 to v5.1.5 [security] 2023-03-14 00:17:53 +00:00
Reto Brunner d471a4c959 packaging: Use an include list in package.json
Rather than playing whack a mole with an exclude list, let's use
an include list instead.
2023-03-12 17:44:21 +01:00
Reto Brunner 4831c20804 update dependency webpack-dev-middleware to v5.3.3 2023-03-12 12:33:10 +01:00
Reto Brunner eddcbcc766 update dependency vue-loader to v17.0.1 2023-03-12 12:32:48 +01:00
Reto Brunner 0183d89384 update dependency sinon to v13.0.2 2023-03-12 12:32:28 +01:00
Reto Brunner 95e56300db update dependency postcss to v8.4.21 2023-03-12 12:32:12 +01:00
renovate[bot] 8e249d46af
chore(deps): update dependency postcss to v8.4.21 2023-03-12 11:18:55 +00:00
renovate[bot] 50e8d2a890
chore(deps): update dependency @vue/test-utils to v2.3.1 2023-03-10 04:58:30 +00:00
Reto Brunner 7f6059d5b7 input/raw: use the irc-framework api
We are not allowed to mess with the connection object directly
according to the public api surface of the framework
2023-03-04 18:17:17 +01:00
Reto Brunner 8ca9ee873b use the irc connected helper function
We should not mess with irc-framework internals.
Technically we shouldn't even access the connection object,
it's not part of the documented API surface
2023-03-04 18:16:28 +01:00
Reto Brunner 402332340b pluginCommand: type it and guard against bad input 2023-03-04 17:00:53 +01:00
renovate[bot] 4742a07721
chore(deps): update dependency webpack-dev-middleware to v5.3.3 2023-03-01 00:16:25 +00:00
renovate[bot] 2f8dc01930
chore(deps): update dependency vue-loader to v17.0.1 2023-03-01 00:15:55 +00:00
Reto Brunner fade6a8d2e network: add getLobby accessor
This documents what we actually want and allows us to shift the
logic to the network
2023-02-27 18:30:33 +01:00
Reto Brunner dfed1dd757 skip migrations if the user has disabled logging 2023-02-27 14:33:34 +01:00
Reto Brunner d67277d996 clientManager: Expose user config 2023-02-27 14:33:34 +01:00
Reto Brunner 95aaba43fa cli: Implement storage migrate subcommand
This introduces the ability to run the migration offline, while
TL is not running as the migrations can take a long time.

The migrate command is added as a `thelounge storage` subcommand.
Reason being that it is expected that more subcommands will follow,
say `thelounge storage clean` to remove partial data from the db.
2023-02-27 14:33:34 +01:00
Reto Brunner 3e7255ff20 sqlite: Add primary keys to the messages table
We want primary keys to never get re-used to so that we
can implement jump to messages / context fetching etc
in the future.

This isn't hooked up yet at all to the rest of the code, only
the schema is changed
2023-02-27 14:20:31 +01:00
Reto Brunner 86e376fc03 sqlite: run migrations on startup 2023-02-27 14:20:31 +01:00
Reto Brunner 899762cddd sqlite: Add infrastructure for migration tests
This sets up the testing infrastructure to test migrations we are
doing.
It's done on a in memory database directly, we are only interested
in the statements themselves and it's easier than to try and
inject a prepared db into the store.

We do add some dummy data though to make sure we actually execute
the things as we expect.
2023-02-27 14:20:29 +01:00
Reto Brunner 063aca948c sqlite: don't hardcode version test 2023-02-27 14:17:04 +01:00
Reto Brunner 25642fbe98 sqlite: delete table creation test
This just repeats the hard coded values from the code, which
is not helping.
We need to touch that test whenever we modify the sql which is
undesired and it doesn't test any useful functionality.

Any error that may ensue would hopefully be tracked by the other
test.
2023-02-27 14:11:47 +01:00
Max Leiter c2e7390127
Merge pull request #4685 from thelounge/networkProps
network: don't force existence of constructor properties
2023-02-26 17:23:23 -08:00
Max Leiter d10a59395c
Merge pull request #4684 from thelounge/userProps
user: don't force existence of constructor properties
2023-02-26 17:22:31 -08:00
Max Leiter 8fc696620f
Merge pull request #4683 from thelounge/chanProps
models/chan: don't force existence of constructor properties
2023-02-26 17:22:06 -08:00
Max Leiter c6a202d6ab
Merge pull request #4686 from thelounge/decoupleServer
Decouple server
2023-02-26 17:20:20 -08:00
Max Leiter 7c9ed14909
Merge pull request #4695 from maxpoulin64/fix/oidentd-crash-race-condition
Don't crash on oidentd socket race condition
2023-02-25 13:44:37 -08:00
Reto Brunner bdc1f23107 fix formatting 2023-02-18 11:46:31 +01:00
Reto Brunner e9a09f5447 Add id to error log 2023-02-18 11:35:52 +01:00
Reto Brunner d93cd88dd5 Fix uploader mount/unmount lifecycle #4691 2023-02-18 11:16:30 +01:00
Pavel Djundik 2f04150461 Fix git commit not being available in dist build 2023-02-15 12:03:08 +02:00
Max Leiter c816e4053e
Merge pull request #4692 from thelounge/irc-framework
bump irc-framework to 4.13.1
2023-02-13 20:56:38 -08:00
Max Leiter 4cff2ccabe
Link to PR in log.warn 2023-02-13 20:51:27 -08:00
Max Leiter 26b7fbf2c0
Apply suggestions from code review
Co-authored-by: Mina Galić <me+github@igalic.co>
2023-02-13 20:50:16 -08:00
Maxime Poulin 243cb10e2a
Don't crash on oidentd socket race condition 2023-02-08 22:05:22 -05:00
dependabot[bot] 7304acd8e0
build(deps): bump http-cache-semantics from 4.1.0 to 4.1.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 21:21:55 +00:00
Reto Brunner 511209a100 bump irc-framework to 4.13.1 2023-02-06 22:20:40 +01:00
Maxime Poulin 2ce374fe85 Fix uploader mount/unmount lifecycle
Currently, in `ChatInput.vue` we call `upload.abort()` which removes the event listeners, which are never added back. This effectively permanently disable uploads if the user navigates away to Settings or any other non-chat pages, and back.

Moves the binding to `mounted()` so that they're properly rebound when a chat window is in view, and also adds an `unmounted()` for clarity.

This should also fix an edge case if the page opens up on a non-chat page and there was never a ChatInput to unbind it, such as login page or add network pages.
2023-02-05 22:32:12 -05:00
Max Leiter 00366967ae
Merge pull request #4690 from maxpoulin64/fix-morning-colors
Fix Morning theme nick colors
2023-02-05 14:01:49 -08:00
Maxime Poulin f2c59c23e2 Fix Morning theme nick colors
PR #4649 introduced a regression on the Morning theme as the `#chat.colored-nicks` CSS selector was removed from Default but not Morning. The result is that Morning no longer had nick colors.
2023-02-05 03:22:43 -05:00
renovate[bot] 90d17cacc1
chore(deps): update dependency sinon to v13.0.2 2023-02-01 02:17:22 +00:00
Reto Brunner 12c03a868d base tsconfig: remove files section
All files are specified in the individual sub projects
2023-01-30 09:14:40 +01:00
Reto Brunner b7540b5827 Move condensedTypes to shared/
This decouples the rest of the server from the client
2023-01-30 09:14:40 +01:00
Reto Brunner 6f13735a7f eslint: add shared/ 2023-01-30 09:14:40 +01:00
Reto Brunner 60bb561e49 Extract tests to shared/ 2023-01-30 09:14:40 +01:00
Reto Brunner e305e23c43 client: use the versions in shared/ where applicable 2023-01-30 09:14:40 +01:00
Reto Brunner 9d34955836 extract cleanIrcMessage from client to shared 2023-01-30 09:14:40 +01:00
Reto Brunner a8149c0f1a Extract linkify to shared directory
This is the first step to sever any dependency of the server on
the client
2023-01-30 09:14:40 +01:00
Reto Brunner 21d1eea6b8 tsconfig: Add shared reference 2023-01-30 09:14:40 +01:00
Reto Brunner e1ae79cb9c server/tsconfig: remove redundant options 2023-01-30 09:14:40 +01:00
Reto Brunner 429efb0c3c network: don't force existence of constructor properties 2023-01-30 08:52:30 +01:00
Reto Brunner c3e3322a79 user: don't force existence of constructor properties 2023-01-30 01:45:58 +01:00
Reto Brunner e31c95e32d models/chan: don't force existence of constructor properties 2023-01-30 00:29:09 +01:00
William Goodspeed f785acb07d
Fix misleading LDAP filiter in default config
The default filter provided in config files is invalid. This may mislead people. Confirm to issue #4620.
2023-01-27 20:02:57 +08:00
renovate[bot] bde5c3d443
fix(deps): update dependency ua-parser-js to v1.0.33 [security] 2023-01-24 17:40:19 +00:00
Reto Brunner 375164ca88 Merge branch 'storageCleanup' 2023-01-22 15:23:56 +01:00
Reto Brunner 7f3ac62e0d Merge branch 'searchFixTakeTwo' 2023-01-22 15:22:32 +01:00
dependabot[bot] ce3ad56ced build(deps): bump json5 from 2.2.1 to 2.2.3
Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-22 15:13:25 +01:00
Reto Brunner efd3b64564 caniuse-lite: update db 2023-01-22 15:13:25 +01:00
renovate[bot] 6b23b87063 chore(deps): update dependency @vue/test-utils to v2.2.7 2023-01-22 15:13:25 +01:00
renovate[bot] 502fb7a705 chore(deps): update dependency @types/ws to v8.5.4 2023-01-22 15:13:24 +01:00
renovate[bot] c854d27d3d chore(deps): update dependency sqlite3 to v5.1.4 2023-01-22 15:13:24 +01:00
Max Leiter 2803018c5a
Merge pull request #4669 from thelounge/collapseAway
allow away and back to be collapsed
2023-01-20 15:30:53 -08:00
Reto Brunner 0ebc3a574c search: ignore searchResults if it isn't the active query
Prior to this, the search is still racy but one tends to notice
this only when the DB is large or network is involved.
The user can initiate a search, get bored, navigate to another chan
issue a different search.

Now however, the results of the first search come back in and
hilarity ensues as we are now confused with the state.

To avoid this, keep track of the last search done and any result
that comes in that isn't equal to the active query is garbage and
can be dropped.
2023-01-08 11:41:09 +01:00
Reto Brunner 958a948456 sqlite: Remove client from sqlitestorage
The only reason we accepted a client was that so we have access
to the next message id when we need it.
So let's accept an id provider function instead.
2022-12-30 16:52:04 +01:00
Reto Brunner 52b8a2a78e textStorage: rip out client instance
We don't need the client, so there's no need to accept it.
2022-12-30 16:42:48 +01:00
Reto Brunner 661d5cb5b0 messagestorage: remove implementation details from interface
The interface should not contain things that aren't the API of the
storage interface.
Further, rename ISqliteMessageStorage to SearchableMessageStorage,
as that's also an implementation detail.
We'll never have a second sqlite backend, so the name seems
strange.
2022-12-30 16:42:48 +01:00
Reto Brunner e597e75847 allow away and back to be collapsed
This means we also apply the collapsing to normal queries,
which might also collapse other things like joins / quits
which may be undesired by some

Fixes: https://github.com/thelounge/thelounge/issues/4583
2022-12-30 13:35:38 +01:00
aab12345 8b1a4f72fa Add password param to /join docs 2022-12-29 13:12:15 +01:00
Pavel Djundik 502780c5a3 Fix sidebar swipe flicker after letting go 2022-12-23 10:50:20 +02:00
Reto 073a38ef1e
Fix previous-source calculation (#4656)
CondensedMessage is a proxy object, outside of the templates
we need to unwrap it manually
2022-12-17 13:59:07 -08:00
Reto Brunner c67df36a29 update dependency @types/lodash to v4.14.191 2022-12-04 12:58:15 +01:00
Reto Brunner d50296385f Merge branch 'sqliteHotFix' 2022-12-04 12:54:45 +01:00
Reto Brunner 068de0c10c Merge branch 'nickColorScope' 2022-12-04 12:53:31 +01:00
renovate[bot] d61ab7e7a0
chore(deps): update dependency @types/lodash to v4.14.191 2022-12-01 21:36:32 +00:00
Reto Brunner 2d4143b779 sqlite: synchronize enable() internally
TL is stupid and doesn't wait for message{Provider,Storage} to
settle before it starts using the store.

While this should be fixed globally, we can hack around the problem
by pushing everything onto the call stack and hope that we'll eventually
finish the setup before we blow the stack.
2022-11-30 10:28:26 +01:00
Reto Brunner f55f772659 style: Put user colors into the smallest possible scope
The only thing that cares about user colors is the user component.
Putting a class value on the chat component seems to be the wrong
place.

This also allows us to remove various css selectors so that we
don't need to be that specific.
After all whatever has that class needs to be colored, we don't
care where it is.
2022-11-27 16:04:56 +01:00
Reto Brunner 982816ff20 store: addMessageSearchResults shouldn't accept null
It makes no sense to emit a add mutation with null, so let's
forbid it.
2022-11-27 14:06:14 +01:00
Reto Brunner 8204c3481a search: fix order of result merging
During a search, we get the results from oldest --> newest.
When we hit the more button, we get the results of the second batch
in the same order.
However, logically to the first batch everything is older, so we
need to prepend it to the result array, not
append.

msg  DB  logical ID
A    3     5
B    2     4
C    1     3

D    3     2
E    2     1
F    1     0
2022-11-27 14:06:14 +01:00
Reto Brunner deeea274da Merge branch 'sqlite_cleanup'
Converts sqlite to async, providing a way forward
for migrations to actually happen
2022-11-24 09:45:01 +01:00
Reto Brunner d34b58811a Merge branch 'search' 2022-11-24 09:34:24 +01:00
Reto Brunner dfb4217167 remove VueApp from router
Nothing actually depends on the vue app being monkey patched onto
the router, so let's get rid of it.
2022-11-22 21:27:19 +01:00
dependabot[bot] f8eb0ebafd
Bump engine.io from 6.2.0 to 6.2.1
Bumps [engine.io](https://github.com/socketio/engine.io) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/socketio/engine.io/releases)
- [Changelog](https://github.com/socketio/engine.io/blob/main/CHANGELOG.md)
- [Commits](https://github.com/socketio/engine.io/compare/6.2.0...6.2.1)

---
updated-dependencies:
- dependency-name: engine.io
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-22 08:33:49 +00:00
Reto Brunner fd14b4a172 make getClientConfiguration type safe
TS type assertions need to be avoided.

The following trivial example demonstrates why

```
type Person = {
	name: string;
	isBad: boolean;
};

function makePerson(): Person {
	const p: Person = {name: 'whatever'} as Person
	p.isBad = false
	return p // theoretically we are now good, p is a Person
}
```

Should the type ever change though, TS will happily trot along

```
type Person = {
	name: string;
	isBad: boolean;
	omgHowCouldYou: number;
};

function makePerson(): Person {
	const p: Person = {name: 'whatever'} as Person
	p.isBad = true
	return p // p is *not* a Person, omgHowCouldYou is missing
}
```

But we pinky swore to the compiler that p is in fact a Person.
In other words, the types are now wrong and you will fail during
runtime.
2022-11-22 03:07:29 +01:00
Reto Brunner 1597c2c56e server: the http{,s} server can't be null 2022-11-22 02:21:27 +01:00
Reto Brunner 4c7337b625 bump socket.io-client to 4.5.0 2022-11-16 07:18:26 +01:00
Reto Brunner 0765d209f2 keybinds: Fix invalid return
Mousetrap doesn't take an async function.
It either accepts False (stop key propagation) or any other
value (bubble up the event)
2022-11-16 06:50:56 +01:00
Reto Brunner 7ee4b80a6e update dependency @types/mousetrap to v1.6.11 2022-11-16 06:32:22 +01:00
Reto Brunner 21c8b0d17f Bump loader-utils from 2.0.2 to 2.0.4 2022-11-16 06:31:19 +01:00
dependabot[bot] 89245455ce
Bump loader-utils from 2.0.2 to 2.0.4
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.4.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.4)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-16 05:29:50 +00:00
Reto Brunner d4bbd9191c bump socket.io to 4.5.2 2022-11-16 06:28:03 +01:00
renovate[bot] 5037383c4c
chore(deps): update dependency @types/mousetrap to v1.6.11 2022-11-16 00:46:37 +00:00
Reto Brunner 83e11b0143 Search: Clear earlier searches when a new one is executed
Fixes: https://github.com/thelounge/thelounge/issues/4637
2022-11-15 18:50:52 +01:00
Reto Brunner 51c9ce078d Search: fix off by one offset error
Offset is eventually passed to sqlite as an OFFSET clause.

This works as follows:

sqlite> select num from seq limit 5 offset 0;
┌─────┐
│ num │
├─────┤
│ 1   │
│ 2   │
│ 3   │
│ 4   │
│ 5   │
└─────┘

sqlite> select num from seq limit 5 offset 5;
┌─────┐
│ num │
├─────┤
│ 6   │
│ 7   │
│ 8   │
│ 9   │
│ 10  │
└─────┘

However, the code currently emits a request for offset + 1, which ends
up skipping a message

sqlite> select num from seq limit 5 offset 5+1;
┌─────┐
│ num │
├─────┤
│ 7   │
│ 8   │
│ 9   │
│ 10  │
│ 11  │
└─────┘
2022-11-15 18:50:52 +01:00
Reto Brunner 8095d9e88a SearchQuery: offset is always a number
Fix type confusion that specified offset to be a string, it is
always a number.
2022-11-15 18:50:52 +01:00
renovate[bot] 221884166d
chore(deps): update dependency postcss to v8.4.19 2022-11-13 17:56:51 +00:00
renovate[bot] 19307d05e7
chore(deps): update dependency @types/chai to v4.3.4 2022-11-13 17:49:23 +00:00
renovate[bot] dfe288ef16 chore(deps): update dependency @types/lodash to v4.14.188 2022-11-13 18:11:48 +01:00
renovate[bot] b5ea7cceb3 chore(deps): update dependency @types/is-utf8 to v0.2.1 2022-11-13 18:11:48 +01:00
renovate[bot] 0ad033fe0a chore(deps): update dependency chai to v4.3.7 2022-11-13 18:11:48 +01:00
renovate[bot] 5a4a39b9d1 chore(deps): update dependency postcss to v8.4.18 2022-11-13 18:11:48 +01:00
renovate[bot] cb17f8d87f chore(deps): update dependency @vue/test-utils to v2.2.1 2022-11-13 18:11:48 +01:00
renovate[bot] 5a803ccd23 chore(deps): update dependency sqlite3 to v5.1.2 2022-11-13 18:11:48 +01:00
Reto 53f6041f42
SearchResults: remove dead code (#4639)
Nachtalb put some infra in place that was never actually working.
It errors out when a user clicks on a message.

Remove the offending code, but keep it all in place so that we
can improve on it.
2022-11-12 22:34:41 -08:00
Reto Brunner dca202427a SearchResults: Fix search progess upon search
When we hit doSearch, we always reset the offset value to 0,
meaning we always hit the conditional (!0) and always set the
messageSearchInProgress flag to undefined.
This is wrong, we do want to set this flag when we initiate a search.
2022-11-12 23:14:53 +01:00
Reto Brunner 6b617f893d SearchResults: remove computed search prop
It is only used in one location, and not from the template.
In other words we should inline it to make the code simpler.
2022-11-12 23:14:53 +01:00
Reto Brunner d62dd3e62d messageStorage: convert to async
Message stores are more complicated that a sync "fire and forget"
API allows for.
For starters, non trivial stores (say sqlite) can fail during init
and we want to be able to catch that.
Second, we really need to be able to run migrations and such, which
may block (and fail) the activation of the store.

On the plus side, this pushes error handling to the caller rather
than the stores, which is a good thing as that allows us to eventually
push this to the client in the UI, rather than just logging it in the
server on stdout
2022-11-02 00:01:36 +01:00
Reto Brunner f068fd4290 sqlite: convert migrations to async
This removes quite a bunch of indention and callbacks
2022-11-01 22:23:47 +01:00
Reto Brunner bbe81bb2fa sqlite: add serialize_get 2022-11-01 22:23:47 +01:00
Reto Brunner f04a06682d extract migrations 2022-11-01 22:23:47 +01:00
Reto Brunner 5e1cbe32f9 sqlite: use serialize_fetchall in search 2022-11-01 22:23:47 +01:00
Reto Brunner ee8223c200 sqlite: use serialize_fetchall in getMessages 2022-11-01 22:23:47 +01:00
Reto Brunner cc3302e874 sqlite: create serialize_fetchall helper function
That puts all the serialization logic into one place and
allows us to use async / promises
2022-11-01 22:23:47 +01:00
Reto Brunner 89ee537364 sqlite: add run helper function
Extract the serialization logic into a single place and
consistently log errors to the console rather than a fire
and forget approach.
2022-11-01 22:23:30 +01:00
Reto Brunner e62b169a6a sqlite: fix docstring 2022-11-01 22:19:56 +01:00
Reto Brunner f6b292107e sqlite: move export to bottom of the file
This makes it easier to see what's getting exported, rather than
if it's interspersed randomly in the middle of the file
2022-11-01 22:19:56 +01:00
Reto Brunner bea4545abf don't call search on a disabled msg provider
A provider might be available, but not functional (broken migration
invalid configuration or what have you).
Don't try to call search in this case.
2022-11-01 22:19:56 +01:00
Reto Brunner cebc6d069f sqlite: error if sqlite isn't enabled but search() is called
When we assert that something can't possibly happen, we better
error out rather than jugging on with no error ;)
2022-11-01 22:19:56 +01:00
Reto 0fa203569a
connect: Trim white space from user input fields (#4623)
Fixes: https://github.com/thelounge/thelounge/issues/4521
2022-09-07 20:25:08 -07:00
Reto Brunner 30e9f45fac Use nick as a realname fallback
Currently the realname is set to an advertisement if it isn't explicitly
set by the user.
Some clients started to show the realname as a display name in their
UI, which makes this tedious as you'll end up with gazillion "The Lounge
User" entries.

To avoid this, set the realname to the nick on first connect, so that
it is useful.
Note that this isn't done on nick changes, but only on the initial
connect step.

Fixes: https://github.com/thelounge/thelounge/issues/4527
2022-08-28 11:21:54 +02:00
Antonio Mika 117c5fa3fd
Added client type checking to webpack (#4619)
* Added client type checking

* Fixed client-side typescript issues
2022-08-23 00:26:07 -07:00
Reto Brunner 621fa92036 linkPreviews: Enforce TLS validity
When a URL is prefixed with a TLS scheme, we should make sure
that the remote provides a valid cert, even just for prefetches.
Else MITM of such a site is trivial.

This probably breaks some people with self signed cert, but the
age where that was acceptable is past. We have free CAs now like
Let's Encrypt.
2022-08-06 12:37:51 +02:00
Reto Brunner 11f7ae98be Merge branch 'regexFix' 2022-08-01 13:31:59 +02:00
Reto Brunner a95ab55154 Merge branch 'installDocs' 2022-08-01 13:29:48 +02:00
Reto Brunner 38bccd3635 Merge branch 'installExpandHome' 2022-08-01 13:29:39 +02:00
Reto Brunner 3240997347 Revert "chore(deps): update dependency @textcomplete/core to v0.1.12"
This reverts commit 0cb4791cd0.
It breaks the autocompletion when clicking on a suggestion
from the nick popup.
2022-07-25 06:44:58 +02:00
renovate[bot] 57ed37c1fd
chore(deps): lock file maintenance 2022-07-24 13:33:42 +00:00
renovate[bot] 0495761c44
fix(deps): update dependency file-type to v16.5.4 [security] 2022-07-24 13:20:16 +00:00
renovate[bot] 520646a212 chore(deps): update dependency sqlite3 to v5.0.10 2022-07-24 15:02:54 +02:00
renovate[bot] 0cb4791cd0 chore(deps): update dependency @textcomplete/core to v0.1.12 2022-07-24 15:02:14 +02:00
renovate[bot] 740618ca49 chore(deps): update dependency @types/content-disposition to v0.5.5 2022-07-24 15:01:22 +02:00
renovate[bot] e97216518a chore(deps): update dependency @textcomplete/textarea to v0.1.12 2022-07-24 14:55:19 +02:00
Reto Brunner 31739b8ac9 install: Document file: prefix in cli help 2022-07-23 23:05:34 +02:00
Reto Brunner e221e708c1 install: expand ~ for local paths
Make `thelounge install file:~/path/to/package` work rather than
erroring out that the folder doesn't exists.

Probably funny on Windows, but it doesn't hurt either
2022-07-23 17:05:31 +02:00
Reto c8cd4057bc
Fix ctcp request message (#4603)
The message was ordered the wrong way in the TS rewrite.

Old:
    +bookworm sent a CTCP request: "chadler" to version
New:
    +bookworm sent a CTCP request: "version" to chadler
2022-07-06 22:28:18 -07:00
Reto Brunner d6e1af0e7d Fix regex escape for prefix patterns
Our regex escape function escapes proper regexes, however
it isn't meant to be shoved into a char class via string interpolation.

We need to also escape '-' if we do so.
2022-07-04 10:08:23 +02:00
Max Leiter d72d8694bb
Potentially fix saving new networks (#4599) 2022-06-28 13:32:08 -07:00
Max Leiter 80f65c5b72
Remove uploading event listeners on ChatInput unmount (#4600) 2022-06-28 13:31:55 -07:00
Pavel Djundik bc709af9fe
Merge pull request #4596 from thelounge/renovate/npm-got-vulnerability
fix(deps): update dependency got to v11.8.5 [security]
2022-06-22 18:48:15 +03:00
renovate[bot] e7d18a91c0
fix(deps): update dependency got to v11.8.5 [security] 2022-06-22 05:33:15 +00:00
renovate[bot] ddcee5371a
chore(deps): update dependency sqlite3 to v5.0.8 (#4564)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-06-21 22:32:58 -07:00
renovate[bot] 194b85be4d
chore(deps): update dependency mocha to v9.2.2 (#4581)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-06-21 22:32:18 -07:00
Pavel Djundik f715c833e7
Merge pull request #4594 from thelounge/xpaw/fix-4593
Fix user commands not working
2022-06-21 15:05:19 +03:00
Pavel Djundik a15ac88ff2 Fix user commands not working
Fixes #4593
2022-06-21 10:51:24 +03:00
Murph Finnicum 4af5fc6f33
Use correct option name (filter instead of ldapFilter) in config.js comment. (#4590)
The comment for the "filter" key under "searchDN" refers to it as "ldapFilter" instead of "filter".
2022-06-18 18:23:59 -07:00
Max Leiter dd05ee3a65
TypeScript and Vue 3 (#4559)
Co-authored-by: Eric Nemchik <eric@nemchik.com>
Co-authored-by: Pavel Djundik <xPaw@users.noreply.github.com>
2022-06-18 17:25:21 -07:00
Eric Nemchik 2e3d9a6265
Fix yarn dev (#4574)
* Fix yarn dev and yarn test
2022-05-21 11:45:42 -07:00
Eric Nemchik c205b89523
Convert configs to cjs, move babel to own file, combine webpack configs (#4561)
* Convert configs to cjs
* Fix lint script in package.json
* Move babel config to separate file
* Combine webpack configs and include babelConfig
2022-05-02 19:19:12 -07:00
John Sullivan 5f7acbf994
Merge pull request #4489 from thelounge/maxleiter/tabbedSettings
Refactor settings to their own tabs and routes
2022-05-01 15:03:03 -07:00
Reto d4cc2dd361
Refactor config out of Helper (#4558)
* Remove config from Helper

Helper is the usual util grab bag of useful stuff.
Somehow the config ended up there historically but
structurally that doesn't make any sense.

* Add cert folder to prettier ignore file
2022-05-01 12:12:39 -07:00
renovate[bot] 38f13525e6
chore(deps): update babel monorepo (#4554)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-01 02:40:09 -07:00
renovate[bot] 99c48dbcea
chore(deps): update dependency @textcomplete/core to v0.1.11 (#4555)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-05-01 02:39:58 -07:00
Pavel Djundik 9dbb6e5e19
Remove node 12, add node 18. Bump minimum node version 14 (#4552)
* Remove node 12, add node 18
* Bump minimum node version
2022-04-30 13:12:24 -07:00
Reto Brunner 791205d4f0 Merge branch 'zncPlayback' 2022-04-30 12:55:54 +02:00
Max Leiter 437dd1667d
Improve setings menu responsiveness 2022-04-29 19:30:35 -07:00
Max Leiter 24bdc46b0a
Settings: move nav to left side on larger screens 2022-04-29 19:27:52 -07:00
Max Leiter 5a383814f6
Settings: nav style tweaks 2022-04-29 19:27:51 -07:00
Max Leiter 1f39e078f4
Settings: change general icon from paintbrush to desktop 2022-04-29 19:27:51 -07:00
Max Leiter 6f64243671
Settings: rename user settings -> account 2022-04-29 19:27:51 -07:00
Max Leiter 31b67b7786
git push --set-upstream origin maxleiter/tabbedSettings 2022-04-29 19:27:50 -07:00
Reto Brunner abf8906757 Merge sqlite3 upgrade to v5.0.6 2022-04-28 21:38:01 +02:00
Reto Brunner c8115e22ac enable znc/playback even without message storage
Fixes: https://github.com/thelounge/thelounge/issues/4464
2022-04-28 17:02:36 +02:00
Reto Brunner aa7db1e7f7 Merge pull/4524: Add prefetchTimeout 2022-04-27 18:22:49 +02:00
Renovate Bot da02350725
chore(deps): update dependency sqlite3 to v5.0.6 2022-04-27 16:04:31 +00:00
Reto Brunner c9c8cadb1a Merge pull/4477 Preserve client certificate 2022-04-27 17:58:09 +02:00
Max Leiter 3726a8d00b
Merge pull request #4541 from thelounge/renovate/sqlite3-5.x
Update dependency sqlite3 to v5.0.4
2022-04-19 17:23:23 -07:00
Reto 605b75c6ed
Merge pull request #4537 from bookworm/renovateShutUp
renovate: Disable digest updates
2022-04-19 06:33:35 +02:00
Max Leiter 5e8adafb3e
Merge pull request #4539 from ronilaukkarinen/master
Fix the alignment of the header buttons
2022-04-18 15:15:48 -07:00
John Sullivan 487d880d32
Merge pull request #4540 from itsjohncs/sortable-upstream
Pull in SortableJS from NPM again.
2022-04-18 15:05:18 -07:00
Renovate Bot 7cb8d33122
Update dependency sqlite3 to v5.0.4 2022-04-18 13:05:43 +00:00
itsjohncs bbe103ca6f Pull in SortableJS from NPM again.
SortableJS/Sortable#2095 has been merged so we no longer need to use
our fork.
2022-04-17 17:34:07 -07:00
Roni Laukkarinen ec757c9b69 Fix topic wrapping on mobile 2022-04-17 19:07:56 +03:00
Roni Laukkarinen 7b725ea55c Fix the alignment of the header buttons 2022-04-17 13:39:52 +03:00
Reto Brunner 0d12be138b renovate: Disable digest updates
There's a reason one pins a commit and it is because we
want that specific commit.
Renovate gets *really* noisy if commits are made frequently
to a repo, so let's disable it.
2022-04-13 21:48:21 +02:00
Pavel Djundik 7db0d4619d
Update sqlite3 to 5.0.3 2022-04-13 19:07:31 +03:00
Max Leiter bdd6e71049
Autocomplete: update to @textcomplete package and close on blur (#4493)
* Autocomplete: update to @textcomplete package
* Autocomplete: close on blur
2022-04-11 18:11:43 -07:00
xnaas 57b1e51e9f
set 'video/quicktime' to 'video/mp4' (#4495)
`video/quicktime` only plays in Firefox and Safari.
`video/mp4` plays in Firefox, Safari, and Chromium-based browsers.
2022-04-11 17:50:00 -07:00
Val Lorentz 20ed3e6dc5
sqlite: Escape '%' and '_' in search queries. (#4487)
I picked '@' arbitrarily, it doesn't matter much.
I just don't like '\' because it needs to be escaped itself in the JS code,
which is annoying.
2022-04-11 17:49:13 -07:00
Reto e4840b4d75
Plugins: include pre-releases in compatibility lookup (#4506)
Semver doesn't treat pre-release versions as upgrades, meaning >4.3.0 isn't satisfied
by 4.3.1-rc.1.
For the purpose of TL plugins however, we are only interested in the semantic version and
expect that rc's adhere to the compatibility promise.
2022-04-11 17:47:51 -07:00
Reto d7bba325a7
Fix user file permissions on create (#4507)
User files contain secrets and should be protected.
Chances are that the user folder can be protected as well,
so let's do that if TL is creating the folder.
2022-04-11 17:47:22 -07:00
Reto 815319810c
cli: don't error if the user folder doesn't exist (#4508)
The user folder gets created on demand, thelounge list should not
fail if the folder doesn't exist.
This just means that no users are present, so report that instead.
2022-04-11 17:46:29 -07:00
Reto 37d7de7671
Kill TL when ident can't start up (#4512)
Fixes: https://github.com/thelounge/thelounge/issues/4509
2022-04-11 17:45:36 -07:00
Max Leiter e362704f6b
v4.3.1 2022-04-11 17:29:13 -07:00
Max Leiter 48f2b79c37
Add changelog entry for v4.3.1 2022-04-11 17:26:23 -07:00
Emily Strickland 3a84290314
Apply fixes suggested by Prettier to fix CI 2022-04-09 19:40:38 +00:00
Emily Strickland ff886846a8
Warn about unset prefetchTimeout, default to 5000 ms 2022-04-09 00:19:08 +00:00
Emily Strickland b2a363f099
Document default value for prefetchTimeout 2022-04-09 00:17:57 +00:00
Emily Strickland 3796485217
Configure link fetch to use prefetchTimeout
This change modifies the `fetch` function in `link.js` to use the new `prefetchTimeout` config setting introduced in the previous commit. This allows configuring the length of the timeout.

I've added a comment here to indicate milliseconds are the unit in use, since otherwise that would no longer be obvious from the code without looking at the default value (which could change).
2022-04-08 22:49:20 +00:00
Emily Strickland 3202b79990
Set prefetchTimeout setting default to 5000 ms
This change adds a `prefetchTimeout` setting to the default configuration and sets it to 5000 milliseconds. Its description indicates the advantages and disadvantages of changing its default value.
2022-04-08 15:42:31 -07:00
Renovate Bot a42325d801
Lock file maintenance 2022-04-06 19:03:11 +00:00
dependabot[bot] bbc7280c41
Bump minimist from 1.2.5 to 1.2.6
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-06 18:42:08 +00:00
Reto Brunner b76058e4cf Merge renovate/express-4.x 2022-04-06 20:39:12 +02:00
Reto Brunner ace09d434c Merge renovate/babel-monorepo 2022-04-06 20:37:14 +02:00
Renovate Bot 4d9442d9e3
Update babel monorepo 2022-04-06 17:19:54 +00:00
Renovate Bot 56bf078e29
Update dependency express to v4.17.3 2022-04-01 00:57:31 +00:00
Renovate Bot 9f7a2e942b
Update dependency node-forge to v1.3.0 [SECURITY] 2022-03-26 03:05:38 +00:00
Reto Brunner f440b67dbe Change sqlite3 module name to the actual name
It leads to confusion as the module is named "sqlite3", not
"node-sqlite3"
2022-03-12 16:39:03 +01:00
Val Lorentz ae7020f569 Do not remove client certificate, even when TLS is disabled
It does not really make sense to remove it, as it can lock someone out of
their account, just by temporarily disabling TLS.
2022-03-05 11:20:57 +01:00
Max Leiter 38fa3bee22
v4.3.1-rc.1 2022-03-02 16:38:22 -08:00
Max Leiter 2e1b2d44f6
Add changelog entry for v4.3.1-rc.1 2022-03-02 16:37:49 -08:00
renovate[bot] 69f3501165
Update actions/setup-node action to v3 (#4496)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-02 16:31:28 -08:00
renovate[bot] 8a92bc9fb9
Update dependency dayjs to v1.10.8 (#4499)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-02 16:27:50 -08:00
renovate[bot] 7cf95d3cbd
Update dependency @babel/core to v7.17.5 (#4498)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-03-01 16:29:56 -08:00
renovate[bot] 53f5b8e991
Lock file maintenance (#4491)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-21 12:39:54 -08:00
Val Lorentz d145fb3738
Re-add missing space between timestamp and nick (#4492)
It was accidentally removed by 027c5b4ff7
2022-02-21 12:38:50 -08:00
Max Leiter 551f85ea51
Fix /collapse and /expand from interacting with the server in public mode (#4488)
Reported by xnaas on IRC
2022-02-18 12:21:17 -08:00
Val Lorentz 66455f2c40
Show a nicer error in Chan.loadMessages() when network is misconfigured (#4476)
Show a nicer error in Chan.loadMessages() when network is misconfigured

ie. an actual error message instead of crashing on a `null` value.
2022-02-16 16:27:41 -08:00
Val Lorentz c12dd6c740
Network.validate: Deduplicate code + tell users what the invalid hostname is (#4475)
* De-duplicate error message creation in Network.validate()
* Tell users what the invalid hostname is.
* Reword the log error message
2022-02-16 16:27:14 -08:00
Reto Brunner cb28204517 Use the DNS result order returned by the OS 2022-02-15 09:20:52 +01:00
Reto Brunner e2e050d3c3 Add leading '<' + trim space when copying messages
Fixes: #4369
2022-02-15 09:15:34 +01:00
sfan5 17b174dddb Use the DNS result order returned by the OS
Effectively, this stops Node from always preferring IPv4.
2022-02-15 07:44:13 +01:00
Val Lorentz 027c5b4ff7 Remove leading space when copying multiple messages 2022-02-14 19:41:34 +01:00
renovate[bot] 1ed4f57afc
Lock file maintenance (#4479)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-13 16:41:18 -08:00
Val Lorentz 53b4d00732 Preserve client certificate when TLS is indirectly enabled by a STS policy
Closes GH-4152.
2022-02-13 14:43:04 +01:00
Val Lorentz ba210e853b test/models/network.js: Add tests for automatic client certificate creation/deletion 2022-02-13 14:42:38 +01:00
Val Lorentz bd2a6cc5be test/models/network.js: Add a simple test for STS policies 2022-02-13 14:42:01 +01:00
Val Lorentz bcd4a060ec test/models/network.js: Reorder tests 2022-02-13 13:24:38 +01:00
Val Lorentz ed3ec6a560 test/models/network.js: Fix test groupping 2022-02-13 13:24:06 +01:00
Val Lorentz 8edec1a5a8 Make sure the leading '<' is select when copypasting a message
Firefox does not seem to select leading (or trailing) characters that are
too small; so this commit sets a very small width, that is still large
enough to be selected.

This commit also adds `display: inline-block`, so the width is not
ignored; but this causes Chrome to ignore the space after `>`, so I made
it a non-breakable space.

An alternative is to make only the leading `only-copy` an
`inline-block`, but I think the non-breakable space is a good idea
regardless.
2022-02-12 13:59:31 +01:00
xnaas 9dfb2a3fdb
Upload m4a as audio/mp4; embed audio/mp4, x-flac, and x-m4a (#4470)
* 'audio/x-m4a' should be 'audio/mp4'
* add handling for x-flac and x-m4a
2022-02-11 17:42:59 -08:00
Max Leiter 4be9a282fa
Add the option to mute channels, queries, and networks (#4282)
Co-authored-by: Reto <reto@labrat.space>
2022-02-10 17:56:17 -08:00
renovate[bot] 337bfa489b
Update dependency cssnano to v5.0.17 (#4441)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-10 16:27:12 -08:00
Val Lorentz 1e3a7b1250 Emit a message for SASL loggedin/loggedout events
Closes GH-3921
2022-02-10 22:30:04 +01:00
renovate[bot] 3fb18717a7
Update dependency postcss to v8.4.6 (#4456) 2022-02-09 18:12:08 -08:00
renovate[bot] 76cbec9ac6
Update dependency @babel/core to v7.17.2 (#4439) 2022-02-09 18:11:05 -08:00
renovate[bot] 734f5b18d3
Lock file maintenance (#4465)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-02-09 15:40:08 -08:00
Reto d228a8c4f4
Bump most deps (#4453) 2022-02-09 15:27:34 -08:00
Val Lorentz f07d6b1ea4
README: suggest running 'yarn format:prettier' when linting fails (#4467)
* README: suggest running 'yarn format:prettier' when linting fails

Co-authored-by: Max Leiter <maxwell.leiter@gmail.com>
2022-02-08 17:52:07 -08:00
Max Leiter d0fab98c1d
Merge pull request #4462 from thelounge/bookworm/certErr
clientCert: fix up error message
2022-02-04 16:56:33 -08:00
Max Leiter d4d139505f
Merge pull request #4463 from thelounge/bookworm/canIuse
update caniuse-lite
2022-02-04 16:55:54 -08:00
Reto Brunner 9528515647 update caniuse-lite 2022-02-04 23:21:26 +01:00
Reto Brunner c0b81902f5 clientCert: fix up error message 2022-02-04 23:06:53 +01:00
Reto a86fa168b8
Merge pull request #4450 from itsjohncs/renovate-lock-file
Have Renovate bot refresh our lockfile for us.
2022-02-03 00:16:34 +01:00
itsjohncs 3e387156f7 Have Renovate bot refresh our lockfile for us.
I noticed that caniuse-lite wants to be updated regularly via
`npx browserslist@latest --update-db`. Renovate bot can do this if we
enable its `lockFileMaintenance` option
([source](https://github.com/renovatebot/renovate/issues/8615)).

I'm not sure exactly how annoying Renovate bot will be if we enable
this option but I figure we can just try it and disable it if it's
annoying.
2022-02-02 14:37:24 -08:00
itsjohncs 7e0afc90fd Replace deprecated Renovate config options.
This was an automatically requested migration that the
`renovate-config-validator` asked for. It's hard to tell because the
config options it asked to be removed are no longer in their docs, but
I believe this will not change the behavior of the renovate bot at all.
2022-02-02 14:36:02 -08:00
Max Leiter dcce9eba25
Merge pull request #4459 from fnutt/patch-2
Adding 'to' in a sentence in config.js
2022-02-01 14:39:34 -08:00
fnutt b1aa8528a4
Added 'to* in a sentence 2022-02-01 11:31:34 +01:00
Max Leiter 4489d5c8b8
Merge pull request #4449 from thelounge/bookworm/node_eol
Remove node 15.x from build matrix
2022-01-27 13:17:25 -08:00
Reto Brunner 1f8881a1d7 Remove node 15.x from build matrix
EOL was June 2021, this time has passed long ago.
2022-01-27 00:47:43 +01:00
Reto Brunner c7e504eeab sqlite3: update to latest commit
It doesn't look like upstream wants to release a new version.
However, it forces us to use python2 and a insecure tar version.
So staying on the release is not really an option.
Mitigate it by switching to the latest commit in the repo.
2022-01-24 23:59:56 +01:00
Reto Brunner 4db2d28216 Merge branch 'bookworm/mentions' 2022-01-03 09:28:29 +01:00
Taavi Väänänen be498e8f93
Count number of mode changes, not MODE messages
Update the code in MessageCondensed that generates the condensed
messages ("X users have joined, Y modes were set") to count the number
of actual mode changes instead of the raw count of MODE messages. One
mode message can contain multiple mode changes.

Signed-off-by: Taavi Väänänen <hi@taavi.wtf>
2021-12-31 23:32:17 +02:00
Reto Brunner e999171f29 Mentions window: filter list when we part a chan
Should some other client part a chan, then we need to clean
up the list from the mentions window in case it's open in ours.
2021-12-29 16:46:16 +01:00
Reto Brunner 0d209fce09 Clear obsolete mentions upon channel part
Currently, the mentions only track the chanID and MsgID.
However, when we part a channel the chanID becomes orphaned.

Considering that mentions from a parted channel probably aren't
that relevant, let's automatically clear them when we part.
Should the user really want to look at them again, they can re-join
the channel and get the scroll back that way.
2021-12-29 16:46:16 +01:00
Max Leiter acf520bd9a
Merge pull request #4435 from thelounge/bookworm/uploadErr
upload: improve error message
2021-12-28 02:40:29 -07:00
Reto Brunner 26c2562124 upload: improve error message 2021-12-27 02:11:56 +01:00
itsjohncs 763047889d Remove uses of window.event.
window.event is a deprecated global that's set to the currently
dispatched event.

- Opened and closed mentions box by clicking its icon in the top bar
- Left and right clicked on an inline channel name and saw context menu
  open both times
- Two-finger swiped on iOS and saw channel change
- Long-touched and dragged channel in network list on iOS and reordered
  the list successfully
2021-12-20 15:34:28 -08:00
Max Leiter e0bbf19d9d
Merge pull request #4430 from thelounge/maxleiter/configAdjustment
Remove extra 'be' in default config.js LDAP comment
2021-12-06 14:40:04 -08:00
Max Leiter 0fce974f2c
Remove extra 'be' in default config.js LDAP comment 2021-12-06 14:30:25 -08:00
John Sullivan cd7916b6d9
Merge pull request #4427 from thelounge/bookworm/mode
handle RPL_UMODEIS
2021-12-06 13:38:29 -08:00
Max Leiter 2c79d53c6d
Merge pull request #4428 from maxpoulin64/switch-busboy-implementation
Switch busboy implementation to `@fastify/busboy`
2021-12-06 12:38:58 -08:00
Reto Brunner 514c6fbf95 Rewrite conditional as switch
This is actually what the code tries to do, the conditional just
makes it harder to read
2021-12-06 07:24:21 +01:00
Reto Brunner 1953e03253 Add RPL_UMODEIS msg handler 2021-12-06 07:24:21 +01:00
Max Leiter 981de663fb
Merge pull request #4426 from brunnre8/worktrees
getGitCommit: allow git worktrees
2021-12-05 14:23:12 -08:00
Maxime Poulin 2c2dd1c76f Switch busboy implementation to @fastify/busboy
I've been notified the current implementation is abandonned and has been forked by fastify to fix bugs, including some crashes and hangs:
See:
* https://github.com/mscdex/busboy/issues/250
* https://github.com/mscdex/dicer/pull/22
* https://github.com/mscdex/dicer/pull/25
2021-12-04 19:49:21 -05:00
Max Leiter ecc0b9183e
Merge pull request #4423 from brunnre8/exit
Use non 0 exit code in abnormal shutdown
2021-12-04 14:57:48 -08:00
Max Leiter 4065d5de97
Merge pull request #4425 from brunnre8/deps
update irc-framework to 4.12.1
2021-12-03 12:02:11 -08:00
Reto Brunner 1c08b6dce6 getGitCommit: allow git worktrees
Change the short circuit logic to only test for a .git path.
With worktrees that's just a file, not a directory and we really
shouldn't play git anyhow and not rely on implementation details.
2021-12-03 18:16:30 +01:00
Reto Brunner 0c50c2d274 update irc-framework to 4.12.1
Remove ping timer on socket close
2021-12-03 18:00:56 +01:00
Reto Brunner 96c2d2419b Use non 0 exit code in abnormal shutdown 2021-12-02 07:55:06 +01:00
Max Leiter 304d207820
Merge pull request #4361 from supertassu/kickban 2021-12-01 18:25:09 -08:00
Max Leiter 35d8f4e212
Merge pull request #4373 from brunnre8/permissions 2021-12-01 14:18:10 -08:00
Reto 3c70fab7c6
Fix vue/this-in-template linter warning (#4418) 2021-11-30 12:01:45 -08:00
Reto Brunner 0ff9703a28 logs: Set umode to a more restrictive value
When TL first creates the log folder, let only the user read the
log files, should the admin override that subsequently we'll
simply warn about it but respect the decision.

Meaning we have private by default, but this can be overriden
2021-11-30 19:13:33 +01:00
Reto Brunner dc3a387120 vapid: keep the file secret
Contains a secret key, so we probably should keep it, well, secret.
Warn if the file is world readable.
2021-11-30 19:08:29 +01:00
renovate[bot] 212212fe70
Update dependency cssnano to v5 (#4408)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-30 02:42:46 -08:00
renovate[bot] 9b9b357001
Update dependency postcss-loader to v6 (#4415)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-30 02:42:22 -08:00
renovate[bot] 684d7f2db4
Update dependency mini-css-extract-plugin to v2 (#4412)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-30 02:42:07 -08:00
renovate[bot] 8040945913
Update dependency css-loader to v6 (#4407)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-30 02:19:32 -08:00
renovate[bot] 168f2ba46b
Update dependency mocha to v9 (#4413)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-30 02:18:38 -08:00
renovate[bot] d9f2fed398
Update dependency package-json to v7 (#4414)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-30 02:18:22 -08:00
renovate[bot] 84d779a4d0
Update dependency postcss-preset-env to v7 (#4416)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-30 02:17:57 -08:00
Taavi Väänänen 324fb9023e
Add /kickban
This commit adds a new command, /kickban, that is a combination of /kick
and /ban: it kicks the specific user from the channel and then sets the
+b mode to ban the user from the channel.
2021-11-30 12:07:11 +02:00
renovate[bot] cda3bb4e7c
Update dependency postcss to v8.4.4 (#4399)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 22:27:39 -08:00
renovate[bot] 117792fb4d
Update dependency webpack to v5.64.4 (#4398)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 22:27:26 -08:00
renovate[bot] c69588dd10
Update vue monorepo (#4403)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 22:16:21 -08:00
renovate[bot] 361af7f514
Update dependency postcss-loader to v5.3.0 (#4400)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 22:16:09 -08:00
renovate[bot] 5b76ec45ee
Update dependency webpack-cli to v4.9.1 (#4402)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 22:15:33 -08:00
renovate[bot] d596c0cee5
Update dependency babel-plugin-istanbul to v6.1.1 (#4396)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 19:54:10 -08:00
renovate[bot] 1bb5b74236
Update dependency mini-css-extract-plugin to v1.6.2 (#4393)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Max Leiter <maxwell.leiter@gmail.com>
2021-11-29 19:54:00 -08:00
renovate[bot] 62fd807f78
Update babel monorepo (#4395)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 19:31:56 -08:00
renovate[bot] 5d6746c9c4
Update dependency mocha to v8.4.0 (#4394)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 19:31:14 -08:00
renovate[bot] 60f4b3a434
Update dependency tlds to v1.226.0 (#4397)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 18:21:18 -08:00
renovate[bot] a05dd6c612
Update dependency irc-framework to v4.12.0 (#4392)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 18:20:44 -08:00
renovate[bot] 244daea66c
Update dependency filenamify to v4.3.0 (#4391)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 18:20:25 -08:00
renovate[bot] 5f78574ecd
Update dependency eslint-plugin-vue to v7.20.0 (#4383)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 18:07:09 -08:00
renovate[bot] e39e9d2f8a
Update dependency webpack-dev-middleware to v5 (#4390)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 18:06:55 -08:00
renovate[bot] 98e38c8947
Update dependency ua-parser-js to v1 (#4389)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 18:06:46 -08:00
renovate[bot] 40fb2190fa
Update dependency sinon to v12 (#4386)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 17:31:31 -08:00
renovate[bot] 1160517c2c
Update dependency css-loader to v5.2.7 (#4381)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 17:31:26 -08:00
renovate[bot] f719027566
Update dependency file-type to v16.5.3 (#4384)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 17:31:21 -08:00
renovate[bot] cffb838284
Update dependency eslint to v7.32.0 (#4382)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 16:17:27 -08:00
renovate[bot] b02001c079
Update dependency pretty-quick to v3.1.2 (#4379)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 16:17:03 -08:00
renovate[bot] 79e56d1c4b
Update dependency yarn to v1.22.17 (#4380)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 16:16:54 -08:00
renovate[bot] b54cdf7880
Update dependency mime-types to v2.1.34 (#4378)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 15:49:01 -08:00
renovate[bot] cb404cd986
Update dependency got to v11.8.3 (#4377)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 15:47:55 -08:00
sfan5 1d5291929c
Add context menu when clicking inline channel name (#4376) 2021-11-29 15:35:26 -08:00
Max Leiter 172cd63739
Remove downloads badge and add thelounge/thelounge-docker link to README (#4371) 2021-11-22 22:51:07 -08:00
Max Leiter 368f3f910b
Add changelog entry for v4.3.0
oops
2021-11-22 17:54:54 -08:00
sfan5 5f7ec9e8da
Don't download image contents during prefetch if not needed (#4363) 2021-11-22 16:59:33 -08:00
Max Leiter 4419029d2e
v4.3.0 2021-11-22 16:54:39 -08:00
Max Leiter af96f7771c
Revert "Preserve location on first and last line when scrolling through inputs" (#4367)
This reverts commit c5f6b4617f.
2021-11-20 18:43:51 -08:00
Max Leiter 315198ac0b
Switch to thelounge/Sortable fork for Sortable.js (#4368) 2021-11-20 18:28:44 -08:00
Max Leiter f4096234d4
v4.3.0-rc.2 2021-11-18 17:57:30 -08:00
Max Leiter bfdbbce77d
Add changelog entry for v4.3.0-rc.2 2021-11-18 17:57:21 -08:00
Reto 9dbf647f7e
Make esc key close mentions window (#4365)
* Mentions: rename method to what it's doing
* Mentions: make <esc> dismiss the window
2021-11-18 17:54:44 -08:00
Max Leiter 6dfd51bb57
Merge pull request #4364 from thelounge/maxleiter/escCloseSearch
Allow escape key to close search bar and search page
2021-11-18 16:47:21 -08:00
Max Leiter 371ebfb810
Close search results with escape 2021-11-18 13:32:21 -08:00
Max Leiter c439e51617
Clear search input on close 2021-11-18 13:31:01 -08:00
Max Leiter 58110189fe
Allow esc key to close search input 2021-11-18 13:27:52 -08:00
Max Leiter 54d1be6b29
v4.3.0-rc.1 2021-11-17 20:28:23 -08:00
Max Leiter 1199183157
Add changelog entry for v4.3.0-rc.1 2021-11-17 20:27:59 -08:00
Max Leiter 40a5ee70b6
Disable /search and hide help item if searching is disabled 2021-11-17 20:23:51 -08:00
Max Leiter 3cec329e3b
Merge branch 'master' into fix-search-query 2021-11-15 12:42:56 -08:00
Max Leiter 25d493453e
Merge pull request #4356 from thelounge/maxleiter/rmNode10
Bump required node version to 12.x and add 16.x builds
2021-11-04 17:11:31 -07:00
Max Leiter f3af454c9e
Add Node 16 LTS tests and bump mac/windows tests 2021-11-04 16:59:38 -07:00
Max Leiter 186f8f68cd
Merge pull request #4351 from brunnre8/glob
Allow wildcards in hostmask
2021-11-04 14:36:27 -07:00
Max Leiter 59280cfdfd
Merge pull request #4329 from brunnre8/plugins
Add more plugin functionality
2021-11-04 14:12:02 -07:00
Reto Brunner 67503efd21 Allow wildcards in hostmask
According to https://modern.ircdocs.horse/#wildcard-expressions
masks should support "*" and "?" wildcards.
Within TL this only impacts the /ignore functionality.

The reasoning for doing this is to ignore say GuestNNNN!*@* with
guest*!*@* and be done with it if someone spams a gateway.
2021-11-04 20:16:20 +01:00
Max Leiter 7ba977d56a
Bump required node version to 12.x
Node 10 was deprecated in April 2021.
2021-11-04 02:36:06 -07:00
Max Leiter 2a901b3475
v4.3.0-pre.6 2021-11-04 02:06:21 -07:00
Max Leiter 2777cc2db9
Add changelog entry for v4.3.0-pre.6 2021-11-04 02:06:09 -07:00
Max Leiter 979dfaf3eb
v4.3.0-pre.5 2021-11-03 15:52:46 -07:00
Max Leiter 9592563a27
Add changelog entry for v4.3.0-pre.5 2021-11-03 15:52:37 -07:00
Max Leiter 0381cd11bf
Merge pull request #4345 from itsjohncs/small-db-cleanups
Small cleanup of messageStorage/sqlite.
2021-11-03 15:45:48 -07:00
Max Leiter b5e99c0489
Merge pull request #4352 from itsjohncs/very-rounded-search
Prevent round and white search styling in iOS 15.
2021-11-03 15:45:33 -07:00
itsjohncs ea619f5463 Prevent round and white search styling in iOS 15. 2021-11-03 01:50:11 -07:00
Max Leiter 3cab39c59b
Merge pull request #4340 from itsjohncs/long-channel-names
Improve responsiveness of channel name and topic.
2021-11-02 13:18:39 -07:00
Max Leiter fd730eeeb1
Merge pull request #4344 from brunnre8/csp
Force CSP headers for all requests
2021-11-02 12:51:39 -07:00
Max Leiter a8d438261a
Merge pull request #4332 from itsjohncs/android-context-menu
Enable Android's context menus in network list.
2021-11-02 11:57:24 -07:00
Max Leiter 3bb8d2f4b8
Merge pull request #4348 from thelounge/renovate/linkify-it-3.x
Update dependency linkify-it to v3.0.3
2021-11-01 17:56:00 -07:00
Max Leiter 80e0e0fd16
Merge pull request #4349 from thelounge/renovate/mime-types-2.x
Update dependency mime-types to v2.1.33
2021-11-01 17:55:50 -07:00
Renovate Bot 3da5e8e8ca
Update dependency mime-types to v2.1.33 2021-11-01 00:21:57 +00:00
Renovate Bot 411ce5d2f8
Update dependency linkify-it to v3.0.3 2021-11-01 00:21:41 +00:00
itsjohncs 602de668ee Use patched Sortable JS from itsjohncs/Sortable.
This is a (hopefully) temporary solution while we wait for SortableJS
to merge in SortableJS/Sortable#2095 and make a release.
2021-10-30 02:06:35 -07:00
itsjohncs 393d4fe591 Enable Android's context menus in network list.
After #4326 Android users could no longer long-touch to bring up the
context menu for channels in the network list. Now they can again.
2021-10-30 02:06:35 -07:00
Max Leiter a3a9a2cdd9
Merge pull request #4342 from deejayy/password-reveal-icon
Move font assignment of password reveal icon
2021-10-28 09:53:36 -07:00
itsjohncs 044cd2403b Small cleanup of messageStorage/sqlite.
* Extend test coverage to the `search` function.
* Test sort order of messages from `getMessages` and `search`
* Move reversal of `search` results from Vue to messageStorage.
* Remove unnecessary uses of `sqlite.serialize` in tests.
* Return promises from test functions where possible.
2021-10-28 00:48:11 -07:00
Reto Brunner 544146d9aa Force CSP header for all requests
Currently styles / plugins were not actually under the CSP
header protection.
There's no real reason to not have them for all requests, so
add them as a root middleware.
2021-10-26 22:20:06 +02:00
Max Leiter 97f3800785
Merge pull request #4343 from hom3chuk/master
bump vulnerable ua-parser-js version
2021-10-26 12:50:46 -07:00
Evgeniy Chekan 8ab486ef0f bump vulnerable ua-parser-js version 2021-10-25 21:09:02 +03:00
deejayy cf18d04f06 Move font assignment of password reveal icon 2021-10-25 19:39:59 +02:00
Max Leiter 5d7e62ed67
Merge pull request #4341 from Nachtalb/na/fix-proxy-password
* Fix authenticated proxy
* Save the proxy password so you don't have to reenter it after restart
2021-10-24 14:06:53 -07:00
Nachtalb 206d554ce1
Save the proxy password so you don't have to reenter it after restart 2021-10-24 21:27:43 +02:00
Nachtalb 578b1947e2
Fix authenticated proxy 2021-10-23 01:11:54 +02:00
itsjohncs 56d4a6afde Improve responsiveness of channel name and topic.
This commit makes two changes:

1. Long channel names are truncated.
2. Topics cannot be shrinked into non-existence.
2021-10-22 13:57:13 -07:00
Reto 3ba7fb6de4
Prevent autocomplete for highlight settings (#4337)
Chrome seems to somewhat often auto fill the text input of the
highlight exception list with my username as the next field that
follows is of type password.
Try to work around that by telling chrome not to autofill either of
those.

Do note that this is only a hint... The broser vendors apply some
$magic heuristics and if they trigger they ignore the hint.
2021-10-20 18:34:31 -07:00
John Sullivan 21c6abdd1d
Clarify description of prefetchMaxSearchSize. (#4338)
This is a comments only change that fixes two problems:

1. The previous comment described it as limiting request size, but it
   instead limits the response size.
2. Previously it was unclear _why_ this size was significant. It wasn't
   obvious to me that the entire response would be stored in memory.
2021-10-18 23:20:11 -07:00
John Sullivan 80acbc7c06
Fix sporadic rounding on message search bar. (#4333)
This is fixed in the same way as #4328.
2021-10-17 22:11:08 -07:00
Reto Brunner 1e896a9672 plugins: prefix logger with the plugin name 2021-10-13 23:39:32 +02:00
John Sullivan 5d76ed888c
Clean up global listener in Sidebar component. (#4331)
Every time the component was mounted it would add another listener.
Since old listeners would often error this could cause a lot of log
spam, particularly when using the hotloader on a mobile device.
2021-10-13 13:19:34 -07:00
John Sullivan 2b634a6ba6
Use SortableJS 1.14.0. (#4330)
This should be reverted after SortableJS/Vue.Draggable#1085 is merged
and a new release of Vue.Draggable becomes available.
2021-10-13 13:18:03 -07:00
William Boman 2693db4274
client/Mentions: change button copy to "Dismiss all" (#4322)
* client/Mentions: change button copy to "Dismiss all"

* s/hide/dismiss/g
2021-10-12 15:56:39 -07:00
Max Leiter 1d33e0195a
Merge pull request #4324 from itsjohncs/gestures-next-channel
Two-finger swipe now switches windows (#3901)
2021-10-12 15:53:54 -07:00
Max Leiter fcffab1259
Merge pull request #4326 from itsjohncs/mobile-channel-list-sorting
Allow network list reordering via touch.
2021-10-12 15:53:18 -07:00
Reto Brunner 02ccbc1f69 plugins: expose persistant data dir
Plugins need to be able to store persistant files, say settings or
databases or similar things.
Expose a standard location that gets created when the path is
accessed.
2021-10-12 23:24:06 +02:00
Reto Brunner bb4ab4f168 plugins: add Logger interface
Plugins need to be able to log messasages, say for errors.
2021-10-12 23:24:06 +02:00
John Sullivan 8a57f90b65
Prevent sporadic rounding of search input on iOS. (#4328) 2021-10-11 10:23:20 -07:00
Noah van der Aa 9a0ba1da6c
Add keyboard shortcut for help screen (#4315)
* Add keyboard shortcut for help screen

* Make escape key go back to the previous screen

* Use key instead of which

* Use router for navigating back

* Use alt instead of cmd/ctrl
2021-10-10 20:48:28 -07:00
itsjohncs 5c614785bf Suppress iOS long touch behavior in network list.
When a user long touches on iOS, they will select the nearest
selectable text. This causes a distracting visual bug when reordering
the network list (which also uses a long press).
2021-10-10 17:08:17 -07:00
itsjohncs a48f449c59 Allow network list reordering via touch.
Users can now long touch and drag a channel or network to change its
ordering in the sidebar.
2021-10-10 14:40:08 -07:00
John Sullivan 2ab671664e
Vertically center topic editing input in Safari. (#4325) 2021-10-09 22:23:58 -07:00
itsjohncs 91a0815bb5 Add Gestures section to help window.
This documents the two gestures that The Lounge currently supports.

The section is only visible if your device supports touch.
2021-10-07 13:39:30 -07:00
itsjohncs ebe39b26dc Two-finger swipe now switches windows (#3901)
The Alt+Up and Alt+Down keybindings on Desktop did not have an
equivalent for Mobile users. Now a two-finger swipe left on a
touchscreen is equivalent to Alt+Up (similarly swipe right is
equivalent to Alt+Down).
2021-10-07 13:39:19 -07:00
renovate[bot] 7b28d3c0f8
Update dependency web-push to v3.4.5 (#4320)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-02 12:03:13 -07:00
renovate[bot] 324f3aa30f
Update dependency webpack-hot-middleware to v2.25.1 (#4321)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-02 12:02:46 -07:00
renovate[bot] e9f0313892
Update dependency ua-parser-js to v0.7.28 (#4319)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-30 17:25:05 -07:00
renovate[bot] 969d3e4ec1
Update dependency socket.io-client to v3.1.3 (#4318)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-30 17:24:54 -07:00
Taavi Väänänen 7873847a7e
Do not condense single messages (#4313) 2021-09-29 12:33:40 -07:00
Max Leiter cc0dc6266e
Update dependencies (#4312) 2021-09-15 10:12:19 -07:00
renovate[bot] 535ac7ca39
Update dependency stylelint to v13.13.1 (#4307)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-02 14:04:21 -07:00
renovate[bot] c8cdadeb02
Update dependency pretty-quick to v3.1.1 (#4304)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-02 14:03:16 -07:00
Max Leiter beb5530c65
Revert "Support animated webp images" (#4287)
This reverts pull/4186.
2021-08-31 12:27:43 -07:00
Reto 8fcd079204
Properly track user modes for context menu (#4267)
* properly track user modes for context menu

The RPL_ISUPPORT response contains a PREFIX element, which not only tracks the
prefix chars ("@", "+" etc) but also their corresponding mode chars (+O, +v)
This commit changes the context menu to not rely on a hardcoded list but rather
user the one given in the prefix response by the server.

Co-authored-by: Max Leiter <maxwell.leiter@gmail.com>
2021-07-21 00:30:07 -07:00
JeDaYoshi 0a6c33af57
Create a message for search disabled error 2021-07-07 23:00:44 +00:00
JeDaYoshi 162b42d9b0
Apply changes to getCommands/search 2021-07-07 22:12:28 +00:00
Max Leiter 03d38812e3
Merge pull request #4275 from hellomouse/isupport-modes
Optimise modes based on ISUPPORT
2021-07-06 17:09:31 -07:00
JeDaYoshi 35fcacb767
Add firstCommand and do further checks on mode tests 2021-07-06 18:15:37 +00:00
JeDaYoshi d96704835a
Send all modes in case of no ISUPPORT 2021-07-06 15:48:01 +00:00
JeDaYoshi 0d839c501e
Optimise commands processing 2021-07-06 15:43:02 +00:00
Max Leiter 24316fc304
Merge pull request #4281 from thelounge/updateDependencies
Update dependencies
2021-07-06 01:40:13 -07:00
Max Leiter 11ba27d809
Update emoji map 2021-07-06 01:31:24 -07:00
Max Leiter a59c5d65fb
Update dependencies 2021-07-06 01:29:53 -07:00
Max Leiter 7fdd363ee8
Merge pull request #4205 from Nachtalb/ne/fix-input-history-scroll-behaviour
Only scroll history when cursor is on first or last row
2021-07-06 01:27:06 -07:00
Max Leiter 75cf4445c4
Merge pull request #4277 from hellomouse/server-privmsg-fix
Fix nick-less messages from servers
2021-07-06 01:02:18 -07:00
Max Leiter 18b003db9c
Merge pull request #4280 from hellomouse/restrict-notifs-to-https
Add warning for HTTPS requirement on notifications
2021-07-06 00:58:50 -07:00
JeDaYoshi 372d74db69
Add warning for HTTPS requirement on notifications 2021-07-04 20:22:49 +00:00
JeDaYoshi bbda392c3d
Move clientCommands to client
This approach automatically imports the command names.
2021-07-04 02:15:33 +00:00
JeDaYoshi 23f6886cc1
Add test for ISUPPORT-less networks on /mode shorthands 2021-07-04 01:01:45 +00:00
JeDaYoshi 521426bb05
Add test for /search in getCommands 2021-07-04 00:46:36 +00:00
JeDaYoshi 69c37a535b
Only add /search when there's a message provider 2021-07-04 00:31:43 +00:00
Max Leiter 98e8640932
Merge pull request #4279 from hellomouse/fix-user-list
Fix userlist's wrong position on mobile devices
2021-07-03 17:04:09 -07:00
JeDaYoshi 998f8d2beb
Fix userlist's wrong position on mobile devices 2021-07-03 23:50:51 +00:00
JeDaYoshi 058b3155d0
Display error when /search is not enabled
Fixes thelounge/thelounge#4273
2021-07-03 22:53:45 +00:00
JeDaYoshi e0e12c1960
Fix tests for mode shorthand commands 2021-07-03 21:20:28 +00:00
JeDaYoshi 16177eb9f4
Move server nick code to handleMessage 2021-07-03 21:06:16 +00:00
Max Leiter 5e0a12b124
Merge pull request #4274 from hellomouse/add-umode
Add /umode support
2021-07-03 11:48:38 -07:00
JeDaYoshi 6439afd5c6
Fix nick-less PRIVMSGs from servers 2021-07-03 15:27:08 +00:00
JeDaYoshi 4dacaa46f3
Optimise modes based on ISUPPORT
This will see the maximum allowed of modes that are allowed at once as sent in RPL_ISUPPORT
and will send multiple batches while using /op, /voice, etc.

This also fixes a minor issue where it would try sending an empty voice if it had an extra space on arguments
(such as using '/voice  ')
2021-07-03 03:50:22 +00:00
JeDaYoshi 426841e6b7
Add /umode support 2021-07-03 02:28:21 +00:00
Max Leiter 22801a629e
v4.3.0-pre.4 2021-07-01 12:20:56 -07:00
Max Leiter 47b151ab51
Add changelog entry for v4.3.0-pre.4 2021-07-01 12:20:41 -07:00
Max Leiter d05cf5fe62
Fix linter warnings for aria-label placement 2021-07-01 12:14:02 -07:00
Max Leiter 3e4b22255d
Merge pull request #4201 from thelounge/maxleiter/accessiblityImprovements
Initial accessibility improvements
2021-07-01 12:05:22 -07:00
Max Leiter cc97d91ef8
v4.3.0-pre.3 2021-06-29 23:31:44 -07:00
Max Leiter c5e18e3cdd Add changelog entry for v4.3.0-pre.3 2021-06-29 23:27:16 -07:00
Max Leiter 79c57ebf38
Merge pull request #4211 from Mstrodl/feature/socks-support
Add support for SOCKS (closes #1375)
2021-06-29 22:54:17 -07:00
Max Leiter d106889127
Merge branch 'master' into feature/socks-support 2021-06-29 22:48:00 -07:00
Max Leiter b33fd78ed7
Merge pull request #4266 from thelounge/maxleiter/bump-ircfw
Bump irc-framework to 4.11
2021-06-28 23:33:13 -07:00
Max Leiter bec25f6243 Bump irc-framework to 4.11 2021-06-28 23:22:38 -07:00
Max Leiter 646a98270a
Merge pull request #4265 from thelounge/maxleiter/dependencies
Update dependencies
2021-06-28 21:55:11 -07:00
Max Leiter 5a7781eabc
Merge pull request #4258 from bl1nk/recent-mentions-keybind
Toggle recent mentions popup with alt+m
2021-06-28 21:54:46 -07:00
Max Leiter cbe81968ee Update dependencies
Supersedes #4247, #4263, #4248
2021-06-28 21:50:36 -07:00
Max Leiter a42a1fc6a2
Merge pull request #4264 from BradleyShaw/differentiate-wallops
Differentiate WALLOPS from NOTICE
2021-06-28 21:48:05 -07:00
Bradley Shaw a2d23810bf
Differentiate WALLOPS from NOTICE 2021-06-22 11:50:22 +01:00
Max Leiter aa310fe877
Merge pull request #4260 from BradleyShaw/snotice-channel
Display server-originated notices to channels in the channel window
2021-06-22 00:30:42 -07:00
Max Leiter a046bfe8d1
Merge pull request #4262 from thelounge/maxleiter/fixConfigOverrides
Fix not overriding config options with -c
2021-06-22 00:12:53 -07:00
Max Leiter 6b852d14c8
Fix not overriding config options with -c 2021-06-22 00:00:41 -07:00
Max Leiter 5a9f3c5f70
Aria label for userlist 2021-06-21 22:26:43 -07:00
Max Leiter f23cc0712c
Aria-label improvements for chatuserlist/networklist 2021-06-21 22:21:36 -07:00
Max Leiter 7107372a6f
Fix channel alt text in user list to mention type 2021-06-21 22:14:00 -07:00
Max Leiter 867fff33c0
Adjust chat layout so messages directly above input 2021-06-21 21:55:54 -07:00
Max Leiter e5a6554c9a
Merge branch 'master' of github.com:thelounge/thelounge into maxleiter/accessiblityImprovements 2021-06-21 21:48:30 -07:00
Bradley Shaw 38c0c343c3
Send server->channel notices to the relevant channel 2021-06-19 19:49:04 +01:00
Max Leiter 53b7c46e69
Merge pull request #4259 from thelounge/maxleiter/revertLoadMoreBtn
Revert "Fix load more button hidden behind search form"
2021-06-15 11:27:11 -07:00
Mary Strodl e7a8476cfe
NetworkForm: lint 2021-06-15 13:55:54 -04:00
Mary Strodl a3f0314f6b
NetworkForm: only show proxy options if enabled 2021-06-15 13:52:39 -04:00
Max Leiter 3fdc42350e Revert "Fix load more button hidden behind search form"
This reverts commit 115d970604.
2021-06-13 16:21:27 -07:00
Markus Cisler 243f514243 Only toggle mentions popup if connected to network
The top bar is only shown if the user is connected to at least one
network. Only then it is possible to open the recent mentions popup.

Only toggle the recent mentions popup if the user is connected to at
least one network so the popup will not open over the connect view.
2021-06-13 01:49:23 +02:00
Markus Cisler a93ccd680f Toggle recent mentions popup with alt-m
This adds a keybind to toggle the recent mentions popup using alt+m (or
opt+m on macOS).

Relates to #4175
2021-06-13 01:08:40 +02:00
Max Leiter 53a7227e2e
v4.3.0-pre.2 2021-06-07 20:46:35 -07:00
Max Leiter 8bb2fbbf15
Add changelog entry for v4.3.0-pre.2 2021-06-07 20:46:18 -07:00
Max Leiter 0fa37a6a05
Merge branch 'master' into maxleiter/accessiblityImprovements 2021-06-06 23:49:34 -07:00
Max Leiter d5b6a8521f
Merge pull request #4251 from brunnre8/localPlugins
install: allow installation of local packages
2021-06-05 10:30:18 -07:00
Reto Brunner c5fcc5d72f install: allow installation of local packages
It may not be desirable to host all plugins on npm, allow for local packages to
be installed given a package name with a `file:` prefix.

This is still more restrictive than what yarn would support but allows us to still
verify the thelounge compatibility by reading the package.json file.

`yarn add` messes up with local filepaths and generates a lockfile that is
"outdated" as far as any other yarn commands go, which makes them error out.

For some reason `yarn install` fixes that and hence we run that after an install.
Here's the diff of yarn.lock between the broken state after `yarn add file:$path`
and `yarn install`
	--- yarn.lock.2.afterAdd	2021-06-02 00:10:52.365134018 +0200
	+++ yarn.lock.3.afterinstall	2021-06-02 00:13:27.122760442 +0200
	@@ -2194,7 +2194,7 @@
	     safe-buffer "^5.1.2"
	     yallist "^3.0.3"

	-thelounge-plugin-shortcuts@/home/reto/sourcecode/thelounge-plugin-shortcuts:
	+"thelounge-plugin-shortcuts@file:../../sourcecode/thelounge-plugin-shortcuts":
	   version "1.0.12"
	   dependencies:
	     thelounge "4.2.0"

The only thing it does is switch an absolute path to a relative one for whatever
reason.
2021-06-05 13:01:55 +02:00
Max Leiter 9ec02d1e91
Merge pull request #4252 from thelounge/maxleiter/deps
Update dependencies
2021-06-05 01:06:32 -07:00
Max Leiter beb9bcd8d4
Update dependencies
Closes #4250, #4249, #4248, #4247
2021-06-05 00:57:38 -07:00
Max Leiter 8fc7a6c0df
Merge pull request #4242 from brunnre8/master
MessageSearchForm: do not focus input if search is closed
2021-06-05 00:33:42 -07:00
Max Leiter 6182d23758
Merge pull request #4235 from angerson/patch-1
Render styling for colored host masks
2021-06-02 00:18:40 -07:00
Reto Brunner c369a764ed MessageSearchForm: do not focus input if search is closed
Else whenever the element gets created (switch from server chan to a normal one)
the browser tries to focus the hidden element
2021-05-27 09:44:44 +02:00
Austin Anderson 4d310cd545 Render styling for colored host masks
On some IRC networks, users have vanity host masks with colors or other text styling.
Rizon is one such network.

For example, a user connecting from 127.0.0.1 could instead have the host
angerson@this.is.my.host.mask. this.is.my.host.mask may have IRC color code
characters in it, which without this change would be displayed as a bunch of jumbled
garbage in the /whois response or join/part messages.

Resolves #4232.
2021-05-26 09:24:12 -07:00
Pavel Djundik fa854fde78
Merge pull request #4238 from mhajder/master
Change the IRC server to Libera.Chat
2021-05-26 15:03:23 +03:00
Mateusz Hajder 6f7fd80044
Fix length of the link in tests 2021-05-26 13:59:04 +02:00
Mateusz Hajder 28c413319f
Change IRC server and channels in tests to more generic 2021-05-26 13:43:06 +02:00
Mateusz Hajder af236dd280
Add the default IRC network for tests 2021-05-26 13:41:33 +02:00
Max Leiter 58217cffb1
Merge pull request #4219 from TheDecryptor/jpegxl_support
Add support for JPEG XL image previews
2021-05-25 21:36:37 -07:00
Max Leiter fc6c916e7c
Merge pull request #4213 from Nachtalb/na/search-command
Add new "/search query" command to open the search window
2021-05-25 21:31:17 -07:00
Max Leiter ad8a315cf9
Merge pull request #4206 from Nachtalb/na/fill-inputhistory-onload
Fill inputhistory on channel load and more message load
2021-05-25 21:30:47 -07:00
Mateusz Hajder 42bafe7165
Change the IRC server to Libera.Chat 2021-05-23 16:40:08 +02:00
Mary Strodl df5befb60e
Merge remote-tracking branch 'origin/master' into feature/socks-support 2021-05-19 19:01:42 -04:00
Max Leiter db807d0c56
Merge pull request #4223 from thelounge/renovate/npm-postcss-vulnerability
Update dependency postcss to v8.2.10 [SECURITY]
2021-05-12 16:33:12 -07:00
Renovate Bot ab0d9e6200
Update dependency postcss to v8.2.10 [SECURITY] 2021-05-12 01:05:43 +00:00
Max Leiter adf1b5abec
Merge pull request #4210 from Nachtalb/na/inline-audio-file-support
Improve inline audio file support
2021-05-11 17:05:28 -07:00
Max Leiter 2c30293ad2
Merge pull request #4221 from Nachtalb/na/fix-missing-userslist-entries-after-search
Fix missing users in userlist after removing searchinput
2021-05-11 17:05:04 -07:00
Nachtalb 042cfb7582
Fix missing users in userlist after removing searchinput
Because the "Username" components still had the same ":key" vue tried to in-place update them. This doesn't quite work for objects (in this case "user" or "user.original"). Thus we change the key for the search so that it actually inits a new component and thus evaluates its content correctly.
2021-05-09 23:33:35 +02:00
Alex Jones dbf6ff064b Add support for JPEG XL mimetype 2021-05-08 18:10:45 +10:00
Max Leiter 7b1cb88658
Merge pull request #4216 from thelounge/renovate/npm-ua-parser-js-vulnerability
Update dependency ua-parser-js to v0.7.24 [SECURITY]
2021-05-06 19:19:26 -07:00
Max Leiter 7b298cf439
Merge pull request #4212 from Nachtalb/na/allow-text-drag-n-drop
Allow text drag & drop into text fields
2021-05-06 19:18:48 -07:00
Renovate Bot a985d763d0
Update dependency ua-parser-js to v0.7.24 [SECURITY] 2021-05-07 02:13:33 +00:00
Max Leiter d097370316
Merge pull request #4214 from sha1sum/#4161_classes-on-unread-and-ping
Classes for channels in list with unread counts and highlights
2021-05-06 19:09:53 -07:00
Anthony Atkinson a3229f1cdf classes for unread and highlight 2021-05-05 21:51:35 -04:00
Nachtalb cadcc4b97c
Autofocus search input in case no query is present 2021-05-06 03:24:20 +02:00
Nachtalb 24a738d521
Add new command to open the search window 2021-05-06 03:22:09 +02:00
Nachtalb b95643e1a6
Allow text drag & drop into text fields
We only have to stop the defualt behaviour in case we drag & drop a file (for uploading)
2021-05-06 02:48:07 +02:00
Mary Strodl 3f984fad4b
network: fix test 2021-05-05 20:37:54 -04:00
Mary Strodl 9b4f55bdb6
Update lockfiles 2021-05-05 20:26:01 -04:00
Mary Strodl abcad094d1
network: add support for SOCKS (closes #1375) 2021-05-05 20:06:00 -04:00
Nachtalb 0bfcd955e3
Improve inline audio file support
Tested on latest Chromium / Firefox. In case of .m4a files they want audio/x-m4a and not audio/m4a, in case of .flac files they want audio/flac and not audio/x-flac. The module we useed to detect the types however detects them only as audio/x-m4a and audio/x-flac as they are not offical IANA supported mime types (not in IANA spec == "x-" prefix): https://www.iana.org/assignments/media-types/media-types.xhtml Though flac is not in the IANA spec many programs such as the file command (https://man7.org/linux/man-pages/man1/file.1.html) and Chromium (flac) / Firefox (x-flac and flac) support audio/flac only or both.
2021-05-06 02:02:23 +02:00
Nachtalb 04cf2277d9
Prevent possible error when findChannel can't find the wanted channel
Using ?. (optional chaining) requires ecma version 2020 as it is fairly new. Webpack / Babel can handle it.
2021-05-05 18:09:18 +02:00
Max Leiter 26a38b12ab
Merge pull request #4197 from Nachtalb/richrd/message-search
Message Search: Re-Rebase + Fixes / Adjustments
2021-05-03 16:18:15 -07:00
Max Leiter bc7a920de5
Merge pull request #4207 from thelounge/renovate/babel-monorepo 2021-04-30 20:47:57 -07:00
Max Leiter 78da0eb674
Merge pull request #4208 from thelounge/renovate/chalk-4.x 2021-04-30 20:47:08 -07:00
Renovate Bot db8102b058
Update dependency chalk to v4.1.1 2021-05-01 00:40:11 +00:00
Renovate Bot 4b96682d7f
Update babel monorepo to v7.14.0 2021-05-01 00:39:55 +00:00
Nachtalb 11aa52687c
Fill inputhistory on channel load and more message load 2021-05-01 01:46:55 +02:00
Nachtalb bd4e821614
Improve readability of more.js 2021-05-01 01:36:44 +02:00
Nachtalb c5f6b4617f
Preserve location on first and last line when scrolling through inputs 2021-05-01 00:51:55 +02:00
Nachtalb c66f9c885e
Only scroll history when cursor is on first or last row
Needs to be on first to go up and on last to go down
2021-05-01 00:51:25 +02:00
Nachtalb bb41871873
Add close search button 2021-04-30 01:53:08 +02:00
Nachtalb 115d970604
Fix load more button hidden behind search form 2021-04-30 01:53:08 +02:00
Nachtalb ef710a2631
Revert obsolete changes to vuex store 2021-04-30 01:53:07 +02:00
Nachtalb ddff3ac162
Stay in chan during search by searching on /chat-:id/search 2021-04-30 01:53:07 +02:00
Max Leiter 0aabacd549
Initial accessibility improvements
Set aria-hidden to true for cosmetic and repetitive elements
Improve channel/network aria labels in network list
Experiment with different aria-roles for landmarks
2021-04-20 18:04:40 -07:00
Max Leiter 0fb6dae8a6
Merge pull request #4186 from Nachtalb/na/remove-metadata-without-breaking-files
Support animated webp images
2021-04-13 13:14:55 -07:00
Max Leiter ee43e7bdf4
Merge pull request #4192 from Nachtalb/na/restrict-browser-autocomplete
Restrict what the browser should try to autocomplete
2021-04-13 12:58:24 -07:00
Nachtalb e010fe47cc
Respect metadata removal switch 2021-04-13 20:45:16 +02:00
Nachtalb 89390b3fc5
Ensure proper error handling when processing of file fails 2021-04-13 20:41:30 +02:00
Nachtalb c2c66031c0
Auto rotate images based on exif orientation
This makes sure that the orientation of the images work in all viewrs.
2021-04-13 18:24:33 +02:00
Nachtalb 846da41b01
Rename uploadCanvas to a more appropriate removeImageMetadata
Including backwards compatibility
2021-04-13 18:24:33 +02:00
Nachtalb 3a6ac4e5ec
Support animated webp images
We need to remove the metadata without breaking the animation.
For that we use sharp which incooperates libvips (binaries for most common distros included).

This also decreases client side upload complexity as we remove the metadata on the serverside.

Sharp: https://sharp.pixelplumbing.com/
libvips: https://libvips.github.io/libvips/
2021-04-13 18:24:32 +02:00
Nachtalb 1b13905195
Improved search header
- Highlight both channel and search query
- By moving search query to topic we can ensure nice behaviour for long search queries (eg. when searching for an url)
2021-04-13 01:56:53 +02:00
Nachtalb 13d4f035df
More consistent color scheme and usage
Only show search by click on search icon (desktop as well)
Improved color scheme
Keep search open search page
2021-04-13 01:56:53 +02:00
Nachtalb 3fb9c8523a
Enable searching for the same query again
Previously we got an error instead, but in the meantime new messages could have come in. As such we should allow to search again
2021-04-13 01:23:44 +02:00
Nachtalb 544594a7ad
Keep search term in search input after commiting 2021-04-13 00:43:52 +02:00
Nachtalb e36ae64c83
Replace search term path with query
A search term is dynamic and not a pointer to a resource such as a channel as such it should be a query.

For now the network as well as the channels are still in the path even though we should take them out of there as well (in the case we want a global / network search later on). As for now we can keep in as there is no such filter / facet yet.
2021-04-13 00:01:00 +02:00
Nachtalb be141bea65
Fix 2 line wrapped message timestamp 2021-04-12 23:46:44 +02:00
Nachtalb 40aaa17c9b
Fix user context menus in in search results view 2021-04-12 23:40:29 +02:00
Nachtalb d6a23061fc
Remove searchNicks for the time being
We should not have search implementation without a specced filter /
facet setup.
2021-04-12 23:23:04 +02:00
Nachtalb de86c144b5
Disable search if we have no message provider
If we have no message provider:
- Search input field not renderd
- Search endpoint retuns empty resultset

Also removed redundancy by setting a main message provider.
2021-04-12 23:12:11 +02:00
Mary Strodl fe0178c0d2
Remove stuff from gitignore 2021-04-12 22:00:51 +02:00
Mary Strodl 49cd90d0e9
SearchResults: lint 2021-04-12 22:00:51 +02:00
Mary Strodl e6856a9e7d
client: lay some groundwork for jump to message 2021-04-12 22:00:51 +02:00
Mary Strodl 283ef445e5
Merge remote-tracking branch 'origin/master' into richrd/message-search 2021-04-12 22:00:49 +02:00
Nachtalb 08f45eabb2
Restrict what the browser should try to autocomplete
Browsers often autocomplete into wrong fields. Eg. wanting to put the password in a fields in the settings screen and then use a sudo random other fields for username etc.
This is rather annoying and can break someones configuration, thus we should only enable it on fields where it somewhat makes sense (name, server & password fields).
2021-04-12 14:10:33 +02:00
Max Leiter db9eb05dfa
Merge pull request #4187 from Nachtalb/na/filename-in-contentDisposition
Add proper filename to the content-disposition header
2021-04-11 20:29:23 -07:00
Max Leiter df4f78098c
Merge pull request #4190 from Nachtalb/na/improved-handling-of-empty-userdata
Improved handling of empty userdata
2021-04-11 20:12:35 -07:00
Nachtalb 0ccbb90d98
Improved handling of empty userdata
Does not show gecos and account data in join message if they are an empty string
2021-04-11 15:59:42 +02:00
Nachtalb 3a42b5385e
Support inline flac audio 2021-04-11 15:43:42 +02:00
Nachtalb 14d76f8023
Add proper filename to the content-disposition header
By default we take the slug given in the request, if this is not set we try to give a filename from known types.
If we still have no filename we fallback to the previous method of setting no filename.

If the filename is non ascii we will only create the encoded "filename*" and not the ascii only "filename". This is to prevent other applications to save a file like "?????.png" if the filename contains non ascii chars.

For the browsers nothing will really change comapred to the behaviour before this change as good fallbacks if no content-disposition filename is set. But that is not the case for all application, thus it makes sense to include the proper way to set the filename.
2021-04-11 15:41:21 +02:00
Max Leiter a5e9463431
Merge pull request #4196 from thelounge/maxleiter/readme
`client/views` -> `client/components` in README
2021-04-06 15:39:08 -07:00
Max Leiter f213a8973c
client/views -> client/components in README 2021-04-06 14:56:23 -07:00
Max Leiter 9382beb3b1
Merge pull request #4195 from brunnre8/nickPostfix
Settings: show label for nick autocompletion postfix
2021-04-06 14:46:34 -07:00
Reto Brunner c6d7bd4b4a Settings: show label for nick autocompletion postfix 2021-04-06 15:27:23 +02:00
Max Leiter 8dd9bc0e98
Merge pull request #4185 from thelounge/renovate/commander-7.x
Update dependency commander to v7.2.0
2021-04-05 16:25:56 -07:00
Renovate Bot 7df94f01a7
Update dependency commander to v7.2.0 2021-04-05 23:18:45 +00:00
Max Leiter d94d09f4ba
Merge pull request #4076 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.23.0
2021-04-05 16:17:34 -07:00
Max Leiter d248f98618
Merge pull request #4182 from thelounge/renovate/babel-monorepo
Update babel monorepo
2021-04-05 16:17:06 -07:00
Max Leiter 5cef511469
Merge pull request #4184 from thelounge/renovate/chai-4.x
Update dependency chai to v4.3.4
2021-04-05 16:16:37 -07:00
Max Leiter c6282b0a50
Merge pull request #4193 from brunnre8/labelFix
NetworkForm: s/away message/leave message/
2021-04-05 16:16:20 -07:00
Renovate Bot 1913a3ade6
Update babel monorepo 2021-04-05 20:40:02 +00:00
Renovate Bot 386f90614b
Update dependency eslint to v7.23.0 2021-04-05 20:39:11 +00:00
Renovate Bot d1995a0f7d
Update dependency chai to v4.3.4 2021-04-05 20:38:41 +00:00
Max Leiter aede86bc98
Merge pull request #4183 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.15.3
2021-04-05 13:37:40 -07:00
Reto Brunner a496ba8cfc NetworkForm: s/away message/leave message/
877e4acf7d - Add network specific leave message
introduced the wrong label for the leave message.
2021-04-05 19:19:36 +02:00
Renovate Bot d600a10f48
Update dependency @fortawesome/fontawesome-free to v5.15.3 2021-04-01 00:22:11 +00:00
Max Leiter 500034ff5d
Merge pull request #4181 from thelounge/maxleiter/fixQueryModes
Fix client crash when opening a user context menu in query
2021-03-30 22:49:55 -07:00
Max Leiter e4069f8ce9
Fix client crash when opening a user context menu in query 2021-03-30 22:33:48 -07:00
Max Leiter 4f6659897f
Merge pull request #4176 from mitaka8/feature-admin-actions
Show give/revoke modes and kick on other modes than +o
2021-03-30 22:10:54 -07:00
Max Leiter 5329483a40
Merge pull request #4135 from brunnre8/master
Add prefetchMaxSearchSize to override limit for link previews
2021-03-16 18:18:31 -07:00
Mitaka dc0e233fe0 Show give/revoke modes and kick on other modes than +o
Fix #3965

Signed-off-by: Mitaka <jin@mitaka.nl>
2021-03-12 14:33:51 +01:00
Max Leiter 6b074a6660
Merge pull request #4174 from thelounge/v4.3.0-pre.1_release
v4.3.0-pre.1 release
2021-03-03 00:36:55 -08:00
Max Leiter 57bce195de
v4.3.0-pre.1 2021-03-03 00:24:15 -08:00
Max Leiter 34086369db Add changelog entry for v4.3.0-pre.1 2021-03-02 21:53:10 -08:00
Max Leiter 8ce947130b
Merge pull request #4142 from thelounge/renovate/sqlite3-5.x
Update dependency sqlite3 to v5.0.2
2021-03-02 21:47:32 -08:00
Max Leiter 70fcf6f3ee
Merge pull request #4167 from thelounge/renovate/babel-monorepo
Update babel monorepo
2021-03-02 21:47:15 -08:00
Renovate Bot f0a3611d1e
Update dependency sqlite3 to v5.0.2 2021-03-03 05:37:04 +00:00
Renovate Bot df6226c7ca
Update babel monorepo 2021-03-03 05:36:36 +00:00
Max Leiter 8dfa7305b3
Merge pull request #4168 from thelounge/renovate/commander-7.x
Update dependency commander to v7.1.0
2021-03-02 21:35:02 -08:00
Max Leiter 850b49d802
Merge pull request #4169 from thelounge/renovate/css-loader-5.x
Update dependency css-loader to v5.1.1
2021-03-02 21:34:42 -08:00
Max Leiter 8a14b75a47
Merge pull request #4170 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.21.0
2021-03-02 21:34:33 -08:00
Max Leiter c94ace5843
Slightly adjust config option wording 2021-03-02 15:03:39 -08:00
Renovate Bot 19c7a513f1
Update dependency eslint to v7.21.0 2021-03-02 22:12:30 +00:00
Renovate Bot fbad88f9da
Update dependency css-loader to v5.1.1 2021-03-02 22:12:15 +00:00
Renovate Bot 2f29089bbf
Update dependency commander to v7.1.0 2021-03-02 22:12:01 +00:00
Max Leiter afe136fee8
Merge pull request #4171 from emilyst/patch-1
Configure server ping timeout to 60 seconds
2021-03-02 13:10:03 -08:00
Max Leiter 9474cd96d3
Merge pull request #4112 from thelounge/socketio
Upgrade to socket.io v3
2021-03-02 13:07:29 -08:00
Emily Strickland c782ca5b93
Configure server ping timeout to 60 seconds
The default socket.io server-side ping timeout was changed from 60 seconds to 5 seconds. In browsers based on Chrome, this is not enough time to respond when the browser is idle. The end result is that the server sets the user away and then back approximately once every minute if the client window is idle, which is undesirable.

This change restores the previous timeout value.

See https://github.com/socketio/socket.io/issues/3259#issuecomment-474523271.
2021-02-28 18:53:36 -08:00
Pavel Djundik e6fc726c91 Upgrade to socket.io v3 2021-02-28 15:30:44 -08:00
Reto Brunner 7c17662fea Add prefetchMaxSearchSize to override limit for link previews
YouTube puts the opengraph tags needed for the preview after ~300KB in the body
instead of the beginning of the <head> tag.
Instead of hardcoding the value, allow the server admin to set the policy as
they prefer.
2021-02-27 00:10:53 +01:00
Max Leiter f99e4eef77
Merge pull request #4116 from Nachtalb/na/network-specific-leave-message 2021-02-13 17:22:12 -08:00
Max Leiter c974ecb14a
Adjust placeholder away message 2021-02-12 14:07:48 -08:00
Max Leiter ab66c3f487
Merge pull request #4129 from thelounge/renovate/actions-setup-node-2.x
Update actions/setup-node action to v2
2021-02-09 16:51:20 -08:00
Renovate Bot 32ec420763
Update actions/setup-node action to v2 2021-02-10 00:12:09 +00:00
Max Leiter 7fef41131a
Merge pull request #4155 from thelounge/depUpdates
Update dependencies
2021-02-09 16:11:14 -08:00
Max Leiter de12699fa6 Update dependencies 2021-02-09 14:52:55 -08:00
Max Leiter d09f6f6144
Merge pull request #4138 from thelounge/renovate/copy-webpack-plugin-6.x
Update dependency copy-webpack-plugin to v6.4.1
2021-02-09 13:19:53 -08:00
Max Leiter 27195dd34a
Merge pull request #4139 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.10.4
2021-02-09 13:19:38 -08:00
Max Leiter b29850b2d0
Merge pull request #4140 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.19.0
2021-02-09 13:19:07 -08:00
Max Leiter 02c0290ee3
Merge pull request #4141 from thelounge/renovate/eslint-plugin-vue-7.x
Update dependency eslint-plugin-vue to v7.5.0
2021-02-09 13:18:38 -08:00
Renovate Bot 43a70df1b1
Update dependency eslint-plugin-vue to v7.5.0 2021-02-09 00:01:45 +00:00
Renovate Bot 247f20c8ef
Update dependency eslint to v7.19.0 2021-02-09 00:01:29 +00:00
Renovate Bot 6fc72624aa
Update dependency dayjs to v1.10.4 2021-02-09 00:01:13 +00:00
Renovate Bot 3d1834cc5e
Update dependency copy-webpack-plugin to v6.4.1 2021-02-09 00:00:58 +00:00
Pavel Djundik eb056c4997
Merge pull request #4151 from Willamin/patch-1
Add gopher and gemini to the commonSchemes
2021-02-04 11:43:39 +02:00
Will Lewis 9aadf1a739
Add gopher and gemini to the commonSchemes 2021-02-01 10:43:06 -05:00
Nachtalb 03377c6ced
Also use the networks specific leave message on quit 2020-12-18 10:53:40 +01:00
Nachtalb 877e4acf7d
Add network specific leave message 2020-12-18 10:53:37 +01:00
Pavel Djundik aa84e13656
Merge pull request #4110 from Nachtalb/na/fix-breaking-gifs
Fix breaking GIFs while removing metadata
2020-11-25 22:57:55 +02:00
Nick Espig 0e7a5f5c9b Fix breaking GIFs while removing metadata
Closes #4109
GIFs can't contain EXIF data and do not contain any other metadata that isn't operationl
2020-11-25 21:55:33 +01:00
Pavel Djundik 8fa8eed1e5 Upgrade dependencies 2020-11-25 17:45:22 +02:00
Pavel Djundik b2d5cdd4fc
Merge pull request #4104 from GewoonYorick/4097/add-ignore-to-contextmenu
Add ignore option to contextmenu
2020-11-19 16:27:48 +02:00
Pavel Djundik 4529118fd9
Merge pull request #4105 from GewoonYorick/3700/add-extended-join-info
Add extended join information to join message
2020-11-19 16:26:16 +02:00
Yorick Bosman 651a7ac2e9 Add extended join information to join message. 2020-11-19 00:25:28 +01:00
Yorick Bosman 51b0ec1e98 Add ignore option to contextmenu 2020-11-18 23:57:20 +01:00
Pavel Djundik ee16d98a94
Merge pull request #4094 from thelounge/renovate/vue-monorepo
Update vue monorepo
2020-11-04 15:34:55 +02:00
Pavel Djundik 2bbad443c0
Merge pull request #4093 from thelounge/xpaw/fix-4092
Do not generate and send client certificate unless SASL EXTERNAL is requested
2020-11-04 15:30:53 +02:00
Renovate Bot a4f4d23693
Update vue monorepo 2020-11-02 08:20:01 +00:00
Pavel Djundik a76e75f609 Do not generate and send client certificate unless SASL EXTERNAL is requested
Fixes #4092
2020-10-30 23:03:57 +02:00
Pavel Djundik 69986b3ee5 Add node 15 to test matrix 2020-10-30 23:03:45 +02:00
Pavel Djundik c2e8eaf9df Fix test for production build
Webpack 5 minifies it now
2020-10-30 23:02:40 +02:00
Pavel Djundik 41831d18b1
Upgrade to webpack 5 and all deps 2020-10-30 15:10:50 +02:00
Pavel Djundik 800fc95278 Update dependencies 2020-10-23 11:52:04 +03:00
Pavel Djundik 3e9262a345 Update dependencies 2020-10-15 12:22:49 +03:00
Pavel Djundik a9fb563c01 Update mini-css-extract-plugin 2020-10-11 11:06:52 +03:00
Pavel Djundik e7a8258ac0 Update packages 2020-10-11 10:56:13 +03:00
Pavel Djundik 0322c043e3
Merge pull request #4090 from supertassu/scripable-user-password-change
Make `add` and `reset` CLI commands scriptable
2020-10-07 10:03:37 +03:00
Taavi Väänänen e790a72e59 Make add and reset CLI commands scriptable
Add CLI options `--password` and `--save-logs` (for `add` only) in
order to make adding users and changing user passwords scriptable.

Closes #3913
2020-10-07 09:00:00 +03:00
Pavel Djundik 6ca3bae73e
Merge pull request #4087 from thelounge/update-deps
Update dependencies
2020-09-30 17:54:29 +03:00
Pavel Djundik c89b2bb0d6 Update postcss-loader
postcss is required by cssnano, so no need to list is explicitly
2020-09-30 17:50:24 +03:00
Pavel Djundik 1c004cbd17 Upgrade eslint-plugin-vue and fix rules 2020-09-30 17:44:07 +03:00
Pavel Djundik 02357ab9de Upgrade dependencies 2020-09-30 17:41:56 +03:00
Pavel Djundik d4bf0e365f
Merge pull request #4060 from thelounge/xpaw/fix-323
Always use multi-prefix modes
2020-09-29 13:48:21 +03:00
Pavel Djundik 61ebd65367 Add depTypeList 2020-09-18 11:51:12 +03:00
Pavel Djundik e622662c16 Update dependencies 2020-09-18 11:49:09 +03:00
Pavel Djundik 7ee0732f56 Change renovate to monthly 2020-09-18 11:49:01 +03:00
Pavel Djundik 75926432d0 Update dependencies 2020-09-02 10:38:16 +03:00
Pavel Djundik 3fde2aa7b9 Always use multi-prefix modes
Fixes #323
2020-09-01 11:45:08 +03:00
Pavel Djundik 27b3e50a64
Merge pull request #4055 from thelounge/xpaw/enterkeyhint
Add enterkeyhint on chat input and topic save
2020-08-31 13:14:24 +03:00
Pavel Djundik b9540636de Update dependencies 2020-08-29 11:51:01 +03:00
Pavel Djundik eef782fd2c
Merge pull request #4051 from MaxLeiter/accessibilityErrors
Add HTML lang and labelled-by field to upload
2020-08-29 11:47:46 +03:00
Pavel Djundik 5b602c72dc Add enterkeyhint 2020-08-29 11:46:11 +03:00
Max Leiter 570890f2f9 Set lang to unknown for #chat-container 2020-08-28 14:23:02 -07:00
Max Leiter ea5c95ac94 Add HTML lang and labelled-by field to upload 2020-08-27 18:57:12 -07:00
Pavel Djundik b74b692391 Update prettier and apply formatting 2020-08-25 12:49:53 +03:00
Pavel Djundik ac842108f3
Merge pull request #4047 from thelounge/xpaw/avif
Detect `image/avif` as an image
2020-08-24 11:54:14 +03:00
Pavel Djundik 12ceb10c75
Merge pull request #4046 from thelounge/renovate/tlds-1.x
Update dependency tlds to v1.209.0
2020-08-23 20:49:39 +03:00
Pavel Djundik 037f09a22f
Merge pull request #4043 from thelounge/renovate/babel-monorepo
Update dependency @babel/core to v7.11.4
2020-08-23 20:49:29 +03:00
Pavel Djundik f66ee9473a
Merge pull request #4045 from thelounge/renovate/pretty-quick-3.x
Update dependency pretty-quick to v3
2020-08-23 20:49:02 +03:00
Pavel Djundik 2194f91a55
Merge pull request #4042 from thelounge/vscode
Add .vscode settings and suggested extensions
2020-08-23 20:46:57 +03:00
Pavel Djundik df115333ba
Merge pull request #4041 from thelounge/mcinkay/2333/version-compatibility
Add version support for packages.
2020-08-23 20:46:44 +03:00
Pavel Djundik 4307d2da9d
Merge pull request #4040 from thelounge/xpaw/media-embed
Skip video/audio embeds if og:type exists but does not specify it
2020-08-23 20:46:36 +03:00
Pavel Djundik c89dcca449 Detect image/avif as an image 2020-08-23 12:51:52 +03:00
Renovate Bot 1df4dfad4a
Update dependency tlds to v1.209.0 2020-08-22 03:58:34 +00:00
Renovate Bot 8fb6f291f8
Update dependency pretty-quick to v3 2020-08-22 01:26:16 +00:00
Renovate Bot fedaada5a9
Update dependency @babel/core to v7.11.4 2020-08-22 00:13:04 +00:00
Pavel Djundik 381b6904e6 Add .vscode settings and suggested extensions 2020-08-21 19:27:41 +03:00
Al McKinlay 86e570efb2 Add version support for packages. 2020-08-21 15:03:12 +01:00
Pavel Djundik 1e38262d69 Add missing return 2020-08-21 10:18:41 +03:00
Pavel Djundik 9e13694b21 Skip video/audio embeds if og:type exists but does not specify it 2020-08-21 10:16:54 +03:00
Pavel Djundik 7bf4f68ff8 Upgrade packages 2020-08-20 19:43:53 +03:00
Pavel Djundik 19e7017d31 v4.2.0 2020-08-20 19:13:57 +03:00
Pavel Djundik b398a0696b Add v4.2.0 changelog 2020-08-20 19:13:48 +03:00
Pavel Djundik 14ed73ed9b
Merge pull request #3998 from Jay2k1/highlight-exceptions
Highlight exceptions
2020-08-19 21:30:36 +03:00
Jay2k1 b97b145df1 add highlight exceptions 2020-08-19 00:00:56 +02:00
Pavel Djundik c29ae50392
Merge pull request #4037 from thelounge/xpaw/rm-graphql-request
Remove `graphql-request` dependency from changelog script
2020-08-18 11:00:29 +03:00
Pavel Djundik 3557bf00fd
Merge pull request #4038 from thelounge/xpaw/sync-network-name
Sync changed network name to open clients
2020-08-18 11:00:21 +03:00
Pavel Djundik 67e4a4bbb2 Sync changed network name to open clients 2020-08-17 12:57:37 +03:00
Pavel Djundik f63f1abb7c Remove graphql-request dependency from changelog script 2020-08-17 12:46:28 +03:00
Pavel Djundik 1ef7d5ed49 Update dependencies and yarn.lock 2020-08-17 12:21:23 +03:00
Pavel Djundik 928436a9ce
Merge pull request #4032 from thelounge/xpaw/help-unread
Increase unread counter for HELP and INFO messages
2020-08-15 11:34:42 +03:00
Pavel Djundik 5861ffadf2 Increase unread counter for HELP and INFO messages 2020-08-14 14:26:03 +03:00
Pavel Djundik 2d88ae7503
Merge pull request #4029 from thelounge/xpaw/emoji-dahes
Replace dashes to underscores in emoji autocompletion
2020-08-14 10:39:45 +03:00
Pavel Djundik 82c83c5f18
Merge pull request #4028 from thelounge/xpaw/nosync
Disable settings sync for browser notifications and notification sound
2020-08-14 10:39:29 +03:00
Pavel Djundik 19d6b7d98f Replace dashes to underscores in emoji autocompletion 2020-08-13 10:47:50 +03:00
Pavel Djundik d588ecea58 Disable settings sync for browser notifications and notification sound
Closes #3144
2020-08-12 18:36:07 +03:00
Pavel Djundik b6782da837
Merge pull request #4020 from thelounge/xpaw/obj
Use lodash where possible
2020-08-10 15:57:12 +03:00
Pavel Djundik 8bf55527ed Use lodash where possible 2020-08-07 19:52:50 +03:00
Pavel Djundik 2a11c07ba9
Merge pull request #4014 from thelounge/xpaw/no-schema-previews
Disable link prefetching for urls with no schema specified
2020-08-05 12:18:10 +03:00
Pavel Djundik 5720c98869
Merge pull request #4015 from thelounge/renovate/tlds-1.x
Update dependency tlds to v1.208.0
2020-08-04 23:05:03 +03:00
Renovate Bot 7fce28ad90
Update dependency tlds to v1.208.0 2020-08-04 19:32:47 +00:00
Pavel Djundik 8c6460b58a Disable link prefetching for urls with no schema specified 2020-08-04 20:21:12 +03:00
Pavel Djundik 70937d29e0 Update dependencies 2020-08-03 11:36:45 +03:00
Pavel Djundik f1fc7a8968
Merge pull request #4005 from thelounge/xpaw/fix-mode-users
Fix mode message only making last nick clickable
2020-07-29 16:54:58 +03:00
Pavel Djundik 40954c9a3a Fix mode message only making last nick clickable 2020-07-29 10:29:51 +03:00
Pavel Djundik 7d6f98d974
Merge pull request #4004 from thelounge/renovate/uuid-8.x
Update dependency uuid to v8.3.0
2020-07-28 18:01:16 +03:00
Renovate Bot d658b7dfd5
Update dependency uuid to v8.3.0 2020-07-28 11:03:02 +00:00
Pavel Djundik 87299bb893 v4.2.0-pre.2 2020-07-28 14:01:51 +03:00
Pavel Djundik 0dba477eb3
Add changelog entry for v4.2.0-pre.2 2020-07-28 14:01:27 +03:00
Richard Lewis 9a5d80cecc
Merge pull request #4003 from thelounge/xpaw/mentions-wrap
Add break-word on mentions popup content
2020-07-27 18:47:17 +03:00
Pavel Djundik 89165d798b Add break-word on mentions popup content 2020-07-27 17:27:06 +03:00
Pavel Djundik ec65fd17af
Merge pull request #3983 from SRCF/audio
Add option to disable media preview.
2020-07-27 11:04:06 +03:00
Pavel Djundik 5a1963647e
Merge pull request #3962 from thelounge/xpaw/help-info-blocks
Implement generic monospace blocks for INFO and HELP numerics
2020-07-27 11:02:14 +03:00
Pavel Djundik d6cace3959
Merge pull request #3999 from thelounge/xpaw/fix-chrome-perf
Fix layout trashing in Chrome causing typing lag
2020-07-27 11:02:01 +03:00
Pavel Djundik 9502b6adf0
Merge pull request #3987 from thelounge/xpaw/notif-state
Refresh notification permission state when push is enabled
2020-07-27 11:01:53 +03:00
Pavel Djundik a8a2bd7755
Merge pull request #3986 from thelounge/xpaw/upload-keepalive
Fix upload tokens expiring while uploading when TL is proxied
2020-07-27 11:01:44 +03:00
Pavel Djundik a0cfa4900e
Merge pull request #3995 from thelounge/xpaw/router-cleanup
Cleanup vue router route guards
2020-07-27 11:01:37 +03:00
Pavel Djundik 3e26611e9f Fix layout trashing in Chrome
https://bugs.chromium.org/p/chromium/issues/detail?id=1063575#c21
2020-07-27 10:27:52 +03:00
Pavel Djundik aaaf498ada
Merge pull request #4002 from thelounge/renovate/css-loader-4.x
Update dependency css-loader to v4
2020-07-27 10:27:31 +03:00
Renovate Bot bf12d7f4c3
Update dependency css-loader to v4 2020-07-27 07:22:07 +00:00
Pavel Djundik 07e4663e02
Merge pull request #4001 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.44.0
2020-07-27 10:20:53 +03:00
Pavel Djundik 1de351794d
Merge pull request #4000 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.30
2020-07-27 10:20:44 +03:00
Renovate Bot a7f4008ec8
Update dependency webpack to v4.44.0 2020-07-25 00:38:52 +00:00
Renovate Bot 9e77dc3cca
Update dependency dayjs to v1.8.30 2020-07-25 00:38:31 +00:00
Pavel Djundik 1ec728c2b0
Merge pull request #3992 from thelounge/renovate/commander-6.x
Update dependency commander to v6
2020-07-22 10:59:41 +03:00
Pavel Djundik ccea8a35f2
Merge pull request #3996 from thelounge/renovate/ldapjs-2.x
Update dependency ldapjs to v2.1.0
2020-07-22 10:59:31 +03:00
Renovate Bot d63a85a15c
Update dependency ldapjs to v2.1.0 2020-07-21 22:02:09 +00:00
Dexter Chua 2f434be75d Add option to disable media preview.
This disables image previews iff prefetchStorage is disabled. This
stops the client from making any requests to third-party sites.
2020-07-21 08:52:02 +08:00
Pavel Djundik a2c1d1175b Disallow navigating to invalid networks 2020-07-20 11:50:01 +03:00
Pavel Djundik 181a198994 Cleanup vue router route guards 2020-07-20 11:50:01 +03:00
Pavel Djundik 63a420ac21 Implement generic monospace blocks for INFO and MOTD numerics
Fixes #3961
2020-07-20 10:07:49 +03:00
Renovate Bot 3e5933bfd3
Update dependency commander to v6 2020-07-19 19:01:39 +00:00
Pavel Djundik 0ac1fcb471
Merge pull request #3993 from thelounge/xpaw/mentions-fix
Improvements to mentions window
2020-07-19 21:45:46 +03:00
Pavel Djundik 531ea920e0 Improvements to mentions window
- Add hide all button
- Only show overflow scroll when necessary
- Fix key in v-for loop
- Increase window height if browser size allows for it
2020-07-19 17:29:52 +03:00
Pavel Djundik cf4a776a93
Merge pull request #3989 from thelounge/renovate/babel-monorepo
Update dependency @babel/core to v7.10.5
2020-07-19 00:18:14 +03:00
Pavel Djundik 4f7dd37303
Merge pull request #3988 from thelounge/renovate/got-11.x
Update dependency got to v11.5.1
2020-07-19 00:18:05 +03:00
Pavel Djundik c0258b847e
Merge pull request #3991 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.5.0
2020-07-19 00:17:57 +03:00
Pavel Djundik af65f11b68
Merge pull request #3990 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.14.0
2020-07-19 00:17:49 +03:00
Renovate Bot 79beff1d8a
Update dependency eslint to v7.5.0 2020-07-18 19:23:18 +00:00
Renovate Bot 0b7c76ef49
Update dependency @fortawesome/fontawesome-free to v5.14.0 2020-07-18 00:14:30 +00:00
Renovate Bot 970208b470
Update dependency @babel/core to v7.10.5 2020-07-18 00:14:11 +00:00
Renovate Bot 21c496d534
Update dependency got to v11.5.1 2020-07-16 12:14:55 +00:00
Pavel Djundik b7c5f2031c Refresh notification permission state when push is enabled 2020-07-15 16:08:36 +03:00
Pavel Djundik b1115475bf Fix upload tokens expiring while uploading
Fixes #3982
2020-07-15 12:29:02 +03:00
Pavel Djundik f979c72ca7
Merge pull request #3984 from thelounge/renovate/irc-framework-4.x
Update dependency irc-framework to v4.9.0
2020-07-13 11:21:55 +03:00
Pavel Djundik 5e6b5f7400 Add certfp to whois 2020-07-13 10:25:54 +03:00
Pavel Djundik 4becb152bb Changes for irc-framework update 2020-07-13 10:25:54 +03:00
Renovate Bot 761d482572
Update dependency irc-framework to v4.9.0 2020-07-12 22:48:47 +00:00
Richard Lewis 189f7d84ba
Merge pull request #3981 from thelounge/xpaw/mixed-content
Change wording of prefetchStorage option
2020-07-10 14:08:05 +03:00
Pavel Djundik 020323ca45
Change wording of prefetchStorage option 2020-07-10 13:52:39 +03:00
Pavel Djundik d0d3a205b9
Merge pull request #3977 from thelounge/xpaw/allow-private-lock-name
Allow changing network name in private mode with lockNetwork
2020-07-09 11:17:31 +03:00
Pavel Djundik d01d39deda Update yarn.lock 2020-07-09 10:50:52 +03:00
Renovate Bot 70f45ab7f4
Update dependency lodash to v4.17.19 2020-07-08 17:17:20 +00:00
Pavel Djundik d7c641ffc7 Add password field for private+locknetwork 2020-07-08 15:42:34 +03:00
Pavel Djundik c21ccad823 Allow changing network name in private mode with lockNetwork 2020-07-08 15:42:34 +03:00
Pavel Djundik 5fcfcf4f23
Merge pull request #3976 from thelounge/node-v14
Test node v14
2020-07-08 15:41:25 +03:00
Pavel Djundik 5f3133a609 Update vuedraggable and yarn.lock 2020-07-08 15:29:17 +03:00
Renovate Bot 6a0708b676
Update dependency lodash to v4.17.17 2020-07-08 12:14:35 +00:00
Richard Lewis fae9d75d6d
Merge pull request #3978 from thelounge/xpaw/fix-date-marker
Fix date marker not displaying sometimes
2020-07-08 14:59:42 +03:00
Pavel Djundik 67d9317f20 Fix date marker not displaying sometimes 2020-07-08 14:43:43 +03:00
Pavel Djundik c5f9ef3e3d Test node v14 2020-07-08 14:19:22 +03:00
Pavel Djundik 9d7888814c Update yarn.lock 2020-07-08 14:17:22 +03:00
Pavel Djundik bba2f21f5e Merge remote-tracking branches 'origin/renovate/got-11.x', 'origin/renovate/lodash-monorepo' and 'origin/renovate/sqlite3-5.x' 2020-07-08 14:14:18 +03:00
Renovate Bot f8448c2521
Update dependency lodash to v4.17.16 2020-07-08 11:08:01 +00:00
Renovate Bot bc862698ea
Update dependency sqlite3 to v5 2020-07-08 10:30:09 +00:00
Renovate Bot cf64de66c9
Update dependency got to v11.5.0 2020-07-08 10:29:50 +00:00
Pavel Djundik dbe1427e7a
Merge pull request #3845 from thelounge/xpaw/native-badging
Implement native app badges for highlights (Chrome 81+)
2020-07-08 12:33:52 +03:00
Pavel Djundik b6bd869d5f
Merge pull request #3918 from ashwinikammar/ashwini/fix_username
Make usernames case-insensitive when logging in
2020-07-08 11:26:58 +03:00
Pavel Djundik a6ee6efb6a
Merge pull request #3967 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.10.4
2020-07-06 10:40:47 +03:00
Pavel Djundik 6ae3821e12
Merge pull request #3968 from thelounge/renovate/copy-webpack-plugin-6.x
Update dependency copy-webpack-plugin to v6.0.3
2020-07-06 10:40:40 +03:00
Pavel Djundik 9e278dc812
Merge pull request #3969 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.29
2020-07-06 10:40:32 +03:00
Pavel Djundik 2323afc0d3
Merge pull request #3970 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.4.0
2020-07-06 10:40:24 +03:00
Pavel Djundik 2ef16d5f9b
Merge pull request #3971 from thelounge/renovate/vue-monorepo
Update dependency vuex to v3.5.1
2020-07-06 10:40:16 +03:00
Pavel Djundik b605bf3a95
Merge pull request #3972 from thelounge/renovate/got-11.x
Update dependency got to v11.4.0
2020-07-06 10:40:07 +03:00
Renovate Bot 91a377b015
Update dependency got to v11.4.0 2020-07-04 11:53:46 +00:00
Renovate Bot 2544b19525
Update dependency vuex to v3.5.1 2020-07-04 02:47:28 +00:00
Renovate Bot 34abca6af7
Update dependency eslint to v7.4.0 2020-07-04 01:10:00 +00:00
Renovate Bot 111201f212
Update dependency dayjs to v1.8.29 2020-07-04 01:09:41 +00:00
Renovate Bot cb2a7d02ba
Update dependency copy-webpack-plugin to v6.0.3 2020-07-04 00:39:25 +00:00
Renovate Bot a3e4d4c99d
Update babel monorepo to v7.10.4 2020-07-04 00:39:04 +00:00
Pavel Djundik 32d39410da
Put the string in quotes
skip ci

Github rendered it as "\#"
2020-06-29 15:16:15 +03:00
Pavel Djundik 7ddfc63327
Escape channel name
skip ci
2020-06-29 15:15:13 +03:00
Pavel Djundik 21d872519f
Merge pull request #3963 from thelounge/issue-links
Create issues links
2020-06-29 15:14:13 +03:00
Pavel Djundik 8fe9add310 Create issues links 2020-06-29 12:48:14 +03:00
Pavel Djundik 0d9571e43e
Merge pull request #3960 from thelounge/renovate/vue-loader-15.x
Update dependency vue-loader to v15.9.3
2020-06-28 11:46:37 +03:00
Pavel Djundik 47624efd24
Merge pull request #3959 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.3.1
2020-06-28 11:46:29 +03:00
Pavel Djundik ff52ee58e4
Merge pull request #3957 from thelounge/renovate/uuid-8.x
Update dependency uuid to v8.2.0
2020-06-28 11:46:22 +03:00
Renovate Bot f635292dfe
Update dependency vue-loader to v15.9.3 2020-06-27 01:00:57 +00:00
Renovate Bot 99def3c0ef
Update dependency eslint to v7.3.1 2020-06-27 01:00:36 +00:00
Renovate Bot 91a32d9d51
Update dependency uuid to v8.2.0 2020-06-23 21:10:54 +00:00
Ashwini Kammar 9e8033e36e Fixing thelounge username case-sensitivity - issue#2943
Removing the duplicate user profiles
2020-06-23 13:01:06 +01:00
Pavel Djundik eebfb7a6c5
Merge pull request #3952 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.13.1
2020-06-22 23:00:12 +03:00
Pavel Djundik 8ec54169f3
Merge pull request #3953 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.3.0
2020-06-22 23:00:05 +03:00
Pavel Djundik 6f58a875de
Merge pull request #3954 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.6.1
2020-06-22 22:59:58 +03:00
Renovate Bot b7035d3cae
Update dependency stylelint to v13.6.1 2020-06-22 08:58:30 +00:00
Renovate Bot 7effc2f873
Update dependency eslint to v7.3.0 2020-06-22 08:58:14 +00:00
Renovate Bot 2b1d04938a
Update dependency @fortawesome/fontawesome-free to v5.13.1 2020-06-22 08:57:56 +00:00
Pavel Djundik fa0396a764
Merge pull request #3951 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.10.3
2020-06-22 11:56:18 +03:00
Pavel Djundik d2d3880f23
Merge pull request #3955 from thelounge/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.12
2020-06-22 11:56:04 +03:00
Renovate Bot cf842e8ebf
Update dependency webpack-cli to v3.3.12 2020-06-20 05:18:39 +00:00
Renovate Bot f50a40dfb4
Update babel monorepo to v7.10.3 2020-06-20 01:18:24 +00:00
Pavel Djundik e3f0cd4fc3
Update yarn.lock 2020-06-14 12:38:28 +03:00
Pavel Djundik 8f102f7316 Merge remote-tracking branch 'origin/renovate/chalk-4.x' 2020-06-14 12:34:36 +03:00
Pavel Djundik b904b4875c Merge remote-tracking branch 'origin/renovate/css-loader-3.x' 2020-06-14 12:34:30 +03:00
Pavel Djundik c3f96472c2 Merge remote-tracking branch 'origin/renovate/file-type-14.x' 2020-06-14 12:34:24 +03:00
Pavel Djundik 72b7906949 Merge branch 'renovate/mocha-8.x' 2020-06-14 12:34:07 +03:00
Renovate Bot 612b84ceb7
Update dependency css-loader to v3.6.0 2020-06-14 09:33:52 +00:00
Renovate Bot a4f0add6e1
Update dependency chalk to v4.1.0 2020-06-14 09:33:35 +00:00
Pavel Djundik 3ac2d2c22a
Merge pull request #3946 from thelounge/renovate/textcomplete-0.x
Update dependency textcomplete to v0.18.2
2020-06-14 12:33:23 +03:00
Pavel Djundik fd9863d919
Merge pull request #3906 from thelounge/xpaw/ctcp-server
Reply to the server if that's where CTCP VERSION originated
2020-06-14 12:33:08 +03:00
Pavel Djundik b3ad1b1419
Merge pull request #3947 from thelounge/renovate/vue-monorepo
Update dependency vue-router to v3.3.4
2020-06-14 12:33:00 +03:00
Pavel Djundik d29f2fb251 Got: "options.rejectUnauthorized" is now deprecated, please use "options.https.rejectUnauthorized" 2020-06-14 12:32:08 +03:00
Renovate Bot 1918485d8c
Update dependency file-type to v14.6.2 2020-06-14 08:59:43 +00:00
Renovate Bot eeac12fe49
Update dependency vue-router to v3.3.4 2020-06-13 09:09:06 +00:00
Renovate Bot 78426087d1
Update dependency mocha to v8 2020-06-13 03:37:39 +00:00
Renovate Bot 1eafb231af
Update dependency textcomplete to v0.18.2 2020-06-13 01:32:39 +00:00
Pavel Djundik 154ac3a8fa
Merge pull request #3937 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7.2.0
2020-06-07 17:32:20 +03:00
Pavel Djundik 4888f538d7
Merge pull request #3939 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.6.0
2020-06-07 17:32:13 +03:00
Pavel Djundik d23f7af439
Merge pull request #3938 from thelounge/renovate/nyc-15.x
Update dependency nyc to v15.1.0
2020-06-07 17:32:05 +03:00
Pavel Djundik bbd9bbeb46
Merge pull request #3936 from thelounge/renovate/copy-webpack-plugin-6.x
Update dependency copy-webpack-plugin to v6.0.2
2020-06-07 17:31:58 +03:00
Pavel Djundik 75ec058910
Merge pull request #3935 from thelounge/renovate/got-11.x
Update dependency got to v11.3.0
2020-06-07 17:31:50 +03:00
Pavel Djundik 05feff3d28
Merge pull request #3940 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.6.1
2020-06-07 17:31:35 +03:00
Renovate Bot 02da351c3c
Update dependency file-type to v14.6.1 2020-06-07 13:43:44 +00:00
Renovate Bot c7b78779e5
Update dependency stylelint to v13.6.0 2020-06-06 02:35:32 +00:00
Renovate Bot bc087ab74d
Update dependency nyc to v15.1.0 2020-06-06 02:35:13 +00:00
Renovate Bot 6315befa32
Update dependency eslint to v7.2.0 2020-06-06 00:36:51 +00:00
Renovate Bot 984bb76c5f
Update dependency copy-webpack-plugin to v6.0.2 2020-06-06 00:36:29 +00:00
Renovate Bot 1299e1e4c9
Update dependency got to v11.3.0 2020-06-05 16:33:22 +00:00
Richard Lewis f3236538a0 Fix searching channels with uppercase letters in name. 2020-06-03 14:17:53 +03:00
Richard Lewis b4d02c3c56 Fix context menus in search results. 2020-06-03 14:04:57 +03:00
Pavel Djundik 3194777b98
Merge pull request #3927 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.10.2
2020-06-02 17:34:28 +03:00
Pavel Djundik f593af9b12
Merge pull request #3932 from thelounge/renovate/got-11.x
Update dependency got to v11.2.0
2020-06-02 17:34:20 +03:00
Pavel Djundik 889d2f8482
Merge pull request #3934 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.6.0
2020-06-02 17:34:14 +03:00
Renovate Bot 7ea79d10a2
Update dependency file-type to v14.6.0 2020-06-02 04:36:29 +00:00
Renovate Bot cd1171b4c1
Update dependency got to v11.2.0 2020-06-01 07:38:47 +00:00
Renovate Bot 97a2805a8a
Update babel monorepo to v7.10.2 2020-05-31 19:43:18 +00:00
Pavel Djundik a119e5c4a0
Merge pull request #3931 from thelounge/renovate/ldapjs-2.x
Update dependency ldapjs to v2.0.0
2020-05-31 22:38:59 +03:00
Pavel Djundik ebcbabde95
Merge pull request #3929 from thelounge/renovate/vue-monorepo
Update dependency vue-router to v3.3.2
2020-05-31 22:38:51 +03:00
Pavel Djundik a863c2c553
Merge pull request #3928 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.28
2020-05-31 22:38:44 +03:00
Renovate Bot 253d820225
Update dependency ldapjs to v2.0.0 2020-05-31 13:22:05 +00:00
Renovate Bot f1c04be695
Update dependency vue-router to v3.3.2 2020-05-30 07:57:27 +00:00
Renovate Bot 7d418d6cbb
Update dependency dayjs to v1.8.28 2020-05-30 07:55:00 +00:00
Pavel Djundik e7b6fdf0c3
Merge pull request #3886 from thelounge/renovate/sqlite3-4.x
Update dependency sqlite3 to v4.2.0
2020-05-30 10:49:47 +03:00
Renovate Bot 56d5c77c76
Update dependency sqlite3 to v4.2.0 2020-05-26 07:47:03 +00:00
Pavel Djundik 336504c306
Merge pull request #3926 from thelounge/renovate/mocha-7.x
Update dependency mocha to v7.2.0
2020-05-26 10:45:06 +03:00
Renovate Bot c54e7f9f47
Update dependency mocha to v7.2.0 2020-05-23 12:58:24 +00:00
Pavel Djundik a740074fd9 Update eslint and yarn.lock 2020-05-23 12:29:25 +03:00
Pavel Djundik 1de0294642
Merge pull request #3922 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.5.0
2020-05-23 12:04:21 +03:00
Pavel Djundik 9443f4e848
Merge pull request #3923 from thelounge/renovate/vue-monorepo
Update dependency vue-router to v3.2.0
2020-05-23 12:04:02 +03:00
Pavel Djundik f4cbcee1b0
Merge pull request #3920 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.5.0
2020-05-23 12:03:53 +03:00
Pavel Djundik 56883f655a
Merge pull request #3919 from thelounge/renovate/uuid-8.x
Update dependency uuid to v8.1.0
2020-05-23 12:03:44 +03:00
Pavel Djundik 24b53c9ff6
Merge pull request #3917 from thelounge/renovate/linkify-it-3.x
Update dependency linkify-it to v3
2020-05-23 12:03:36 +03:00
Renovate Bot 230f26156a
Update dependency vue-router to v3.2.0 2020-05-23 00:34:19 +00:00
Renovate Bot 42715720b1
Update dependency stylelint to v13.5.0 2020-05-23 00:34:04 +00:00
Renovate Bot 31a7228509
Update dependency file-type to v14.5.0 2020-05-21 05:46:39 +00:00
Renovate Bot 6322132921
Update dependency uuid to v8.1.0 2020-05-20 19:29:47 +00:00
Renovate Bot 70bff5616d
Update dependency linkify-it to v3 2020-05-20 12:31:33 +00:00
Pavel Djundik 9bb0b02261 v4.2.0-pre.1 2020-05-17 16:23:53 +03:00
Pavel Djundik ff25f43eeb Add changelog entry for v4.2.0-pre.1 2020-05-17 16:23:46 +03:00
Pavel Djundik b8322d6aa7
Merge pull request #3914 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.4.0
2020-05-17 16:22:41 +03:00
Pavel Djundik 23bbdb08aa
Merge pull request #3915 from thelounge/renovate/vue-monorepo
Update vue monorepo to v1.0.3
2020-05-17 16:22:33 +03:00
Renovate Bot ac9bb8442b
Update vue monorepo to v1.0.3 2020-05-17 12:18:52 +00:00
Renovate Bot eadd225363
Update dependency stylelint to v13.4.0 2020-05-17 10:04:24 +00:00
Pavel Djundik a7c9cf5baa
Merge pull request #3909 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.4.0
2020-05-17 13:02:59 +03:00
Pavel Djundik 5de26dbee9
Merge pull request #3910 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.27
2020-05-17 13:02:53 +03:00
Pavel Djundik a9f5f72218
Merge pull request #3911 from thelounge/renovate/vue-monorepo
Update dependency vuex to v3.4.0
2020-05-17 13:02:43 +03:00
Pavel Djundik bd4c4414dd
Merge pull request #3905 from thelounge/renovate/got-11.x
Update dependency got to v11.1.4
2020-05-17 13:02:32 +03:00
Renovate Bot 1453c75c3f
Update dependency vuex to v3.4.0 2020-05-16 18:38:10 +00:00
Renovate Bot 1800c2a892
Update dependency got to v11.1.4 2020-05-16 18:37:49 +00:00
Renovate Bot 7747114b2e
Update dependency file-type to v14.4.0 2020-05-16 18:37:37 +00:00
Renovate Bot 48be6771b2
Update dependency dayjs to v1.8.27 2020-05-16 18:37:25 +00:00
Pavel Djundik abb8566ce7
Merge pull request #3912 from thelounge/renovate/copy-webpack-plugin-6.x
Update dependency copy-webpack-plugin to v6
2020-05-16 21:36:58 +03:00
Pavel Djundik bc7bf9870c Update CopyPlugin options 2020-05-16 21:33:25 +03:00
Renovate Bot c3d8855ec3 Update dependency copy-webpack-plugin to v6 2020-05-16 21:33:24 +03:00
Pavel Djundik dbc829b5f8 Reply to the server if that's where CTCP VERSION originated 2020-05-11 21:55:30 +03:00
Pavel Djundik c4ff314a12
Merge pull request #3903 from thelounge/renovate/eslint-7.x
Update dependency eslint to v7
2020-05-11 16:32:22 +03:00
Pavel Djundik 147d3b2a9d
Merge pull request #3904 from thelounge/renovate/web-push-3.x
Update dependency web-push to v3.4.4
2020-05-11 16:32:12 +03:00
Renovate Bot 0e2e26ef88
Update dependency web-push to v3.4.4 2020-05-10 11:25:47 +00:00
Renovate Bot a439102403
Update dependency eslint to v7 2020-05-09 17:06:10 +00:00
Pavel Djundik e1157642a8
Merge pull request #3902 from thelounge/renovate/vue-monorepo
Update vue monorepo to v1.0.2
2020-05-09 20:04:57 +03:00
Pavel Djundik 91333d9009
Merge pull request #3898 from thelounge/renovate/got-11.x
Update dependency got to v11.1.2
2020-05-09 20:04:47 +03:00
Renovate Bot 9f6c374d36
Update vue monorepo to v1.0.2 2020-05-09 00:34:37 +00:00
Renovate Bot aef952678a
Update dependency got to v11.1.2 2020-05-08 12:29:05 +00:00
Pavel Djundik b73755b0c4
Merge pull request #3869 from thelounge/renovate/got-11.x
Update dependency got to v11
2020-05-06 13:19:37 +03:00
Pavel Djundik 67378c7a48
Merge pull request #3896 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.3.0
2020-05-06 13:19:29 +03:00
Renovate Bot 10d58f2f19
Update dependency file-type to v14.3.0 2020-05-04 19:27:42 +00:00
Renovate Bot ca23475620
Update dependency got to v11 2020-05-04 15:40:30 +00:00
Pavel Djundik 5aaac56a1a
Merge pull request #3890 from thelounge/renovate/uuid-8.x
Update dependency uuid to v8
2020-05-04 11:55:10 +03:00
Pavel Djundik 6941b3cb7f
Merge pull request #3894 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.26
2020-05-04 11:55:00 +03:00
Pavel Djundik b5b5d5e7d4
Merge pull request #3893 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.9.6
2020-05-04 11:54:51 +03:00
Pavel Djundik 39a8bed4ca
Merge pull request #3895 from thelounge/renovate/vue-monorepo
Update dependency vue-loader to v15.9.2
2020-05-04 11:54:35 +03:00
Renovate Bot 033565bfc5
Update dependency vue-loader to v15.9.2 2020-05-02 01:49:09 +00:00
Renovate Bot b507b340a6
Update dependency dayjs to v1.8.26 2020-05-02 00:35:13 +00:00
Renovate Bot ae276a69eb
Update babel monorepo to v7.9.6 2020-05-02 00:34:56 +00:00
Renovate Bot 5310c90a83
Update dependency uuid to v8 2020-04-29 20:52:32 +00:00
Pavel Djundik 512fc5ca04
Merge pull request #3889 from timmw/timmw/vue3-prep
Changes required for vue 3
2020-04-28 22:10:55 +03:00
Tim Miller-Williams 13a7a4b5c1
Remove vue/no-use-v-if-with-v-for eslint override 2020-04-28 20:05:46 +01:00
Pavel Djundik fb9e3e6a53
Merge pull request #3888 from thelounge/xpaw/id-order
Fix msg id order when loading from sqlite
2020-04-28 21:42:44 +03:00
Tim Miller-Williams 06d6dbe3a3 Fix usage of v-for and v-if on same component 2020-04-28 17:00:08 +01:00
Tim Miller-Williams 8263b17861 Address warning in Vue 3
Property "unreadMarkerShown" was accessed during render but is not
defined on instance
2020-04-28 17:00:08 +01:00
Pavel Djundik 24d4276a7c Fix msg id order when loading from sqlite 2020-04-28 17:45:27 +03:00
Tim Miller-Williams 8d8183eabb Fix 'component' case sensitivity
Component does not work in Vue 3
2020-04-28 15:17:55 +01:00
Pavel Djundik 801c7a07c0
Merge pull request #3885 from thelounge/xpaw/eventbus-multi-arg
Support multiple arguments in eventbus emit
2020-04-28 16:02:24 +03:00
Pavel Djundik 61d8884bef Support multiple arguments in eventbus emit
Fixes confirm dialog callback
2020-04-27 21:45:47 +03:00
Richard Lewis 5d017b09b8 Append new results and keep scroll position. 2020-04-26 22:39:05 +03:00
Richard Lewis 9a1fb0c0a0 Search improvements. 2020-04-26 22:39:05 +03:00
Richard Lewis 88644314ce Use ellipsis
Co-Authored-By: Pavel Djundik <xPaw@users.noreply.github.com>
2020-04-26 22:39:05 +03:00
Richard Lewis 4ba458b9ea Use ellipsis
Co-Authored-By: Pavel Djundik <xPaw@users.noreply.github.com>
2020-04-26 22:39:05 +03:00
Richard Lewis 28c740ab67 Fix toggling search on mobile. 2020-04-26 22:39:05 +03:00
Richard Lewis 2591ae9e8e Disable searching nicks. 2020-04-26 22:39:05 +03:00
Richard Lewis 0f3c292098 Message search WIP. 2020-04-26 22:39:05 +03:00
Pavel Djundik 16646e1586 Fix eventbus 2020-04-26 12:34:22 +03:00
Pavel Djundik 8978be2fd7 Update yarn.lock 2020-04-26 12:21:02 +03:00
Renovate Bot 183226e190 Update dependency webpack to v4.43.0 2020-04-26 12:18:04 +03:00
Renovate Bot 185fcfef33 Update dependency vuex to v3.3.0 2020-04-26 12:18:04 +03:00
Renovate Bot 7a4ee6db27 Update dependency stylelint to v13.3.3 2020-04-26 12:18:04 +03:00
Renovate Bot ea11e5cfd9 Update dependency prettier to v2.0.5 2020-04-26 12:18:04 +03:00
Renovate Bot ae7426b6ff Update dependency mocha to v7.1.2 2020-04-26 12:18:03 +03:00
Renovate Bot f72d29b391 Update dependency mime-types to v2.1.27 2020-04-26 12:18:03 +03:00
Renovate Bot 9a7ae60392 Update dependency file-type to v14.2.0 2020-04-26 12:13:01 +03:00
Renovate Bot fc61500a29 Update dependency eslint-config-prettier to v6.11.0 2020-04-26 12:13:01 +03:00
Renovate Bot bcdd548238 Update dependency dayjs to v1.8.25 2020-04-26 12:13:01 +03:00
Renovate Bot 2e9d375f36 Update dependency css-loader to v3.5.3 2020-04-26 12:13:01 +03:00
Renovate Bot f436dfdd41
Update dependency commander to v5.1.0 2020-04-25 02:31:55 +00:00
Pavel Djundik 480a2576c3
Merge pull request #3872 from thelounge/xpaw/eventbus
Replace vue events with our own event bus
2020-04-24 14:24:29 +03:00
Pavel Djundik f0253075d8 Create an event bus 2020-04-24 14:09:09 +03:00
Pavel Djundik 96a983b310
Merge pull request #3875 from thelounge/xpaw/statusmsg-ui
Add an indicator to statusmsg messages
2020-04-24 14:07:48 +03:00
Pavel Djundik 53bd9c2f68
Merge pull request #3868 from thelounge/xpaw/mentions-fixes
Some fixes in mentions window
2020-04-24 14:07:40 +03:00
Pavel Djundik ad6569cf06 Add an indicator to statusmsg messages 2020-04-24 11:46:39 +03:00
Pavel Djundik 4ac25d4bc5
Merge pull request #3871 from ebardie/ebardie/dont_load_extinct_users
Filter user loading at startup for "advanced" LDAP
2020-04-24 10:21:42 +03:00
Jonathan Sambrook 878ac0d192 Filter user loading at startup for "advanced" LDAP
Users are loaded at startup. Currently when using "advanced" LDAP
authentication this is true even if they no longer have a
valid entry in the LDAP server.

This commit uses the existing LDAP filter (specified in config.js's searchDN
used by the "advanced" LDAP mechanism) to weed out any users that no
longer have the relevant LDAP entry.

Local and "simple" LDAP auth mechanisms continue to use the existing
load all users approach. In the "simple" LDAP case this is because we
only have access to the hashed password, and so can't bind to LDAP.
2020-04-23 15:54:09 +01:00
Jonathan Sambrook a0d10989ad Tidy up the auth plugin API mechanism to hide implementation details
The caller doesn't care which plugin is being used, so this commit
consolidates implementation details within auth.js

The motivation for this work is to prepare for extending the auth API
(to allow "advanced" LDAP to query user entry ontological state at start
up), by tidying up rather than duplicating the existing mechanism.
2020-04-23 15:11:35 +01:00
Pavel Djundik 36844f948c
Merge pull request #3870 from thelounge/xpaw/random-nick-safeguard
Safeguard nick randomizer up to allowed length
2020-04-23 12:47:44 +03:00
Pavel Djundik 2b0afcacf2 Safeguard nick randomizer up to allowed length 2020-04-22 15:18:55 +03:00
Pavel Djundik beb9fbd940 Set native app badge for highlights 2020-04-22 14:16:39 +03:00
Pavel Djundik 0642ae58ce
Merge pull request #3844 from thelounge/xpaw/certfp
CertFP support; separate SASL configuration
2020-04-22 14:05:34 +03:00
Pavel Djundik 635b8b3eef
Merge pull request #3866 from thelounge/renovate/vue-monorepo
Update dependency vuex to v3.2.0
2020-04-22 14:05:15 +03:00
Pavel Djundik bcd2e7cb08 Some fixes in mentions window 2020-04-20 13:40:45 +03:00
Renovate Bot 91b48e061d
Update dependency vuex to v3.2.0 2020-04-20 08:16:43 +00:00
Pavel Djundik 89edc6aa30
Merge pull request #3867 from maxpoulin64/fix-manifest-color
Fix off-by-one color error in webmanifest
2020-04-20 10:22:40 +03:00
Maxime Poulin be78a5809a Fix off-by-one color error in webmanifest 2020-04-19 20:46:37 -04:00
Pavel Djundik ce6f188acc
Merge pull request #3863 from thelounge/renovate/semver-7.x
Update dependency semver to v7.3.2
2020-04-16 14:21:47 +03:00
Pavel Djundik b8eaae3a50
Merge pull request #3864 from thelounge/xpaw/rm-polyfill
Remove intersection-observer polyfill
2020-04-16 14:21:35 +03:00
Pavel Djundik 9105a3db06 Remove intersection-observer polyfill 2020-04-15 16:14:21 +03:00
Renovate Bot 18a10a9efb
Update dependency semver to v7.3.2 2020-04-15 08:54:49 +00:00
Pavel Djundik e772c4eab5
Merge pull request #3861 from MaxLeiter/mentionsLoading
Move mentions loading text out of header
2020-04-15 11:23:35 +03:00
Max Leiter 177d4d78ba Move mentions loading text out of header 2020-04-15 01:20:16 -07:00
Pavel Djundik fce71f4a7c Use v-model in network form, auto size commands 2020-04-15 11:12:07 +03:00
Pavel Djundik 6ee71779d1 Combine displayNetwork into lockNetwork 2020-04-15 10:56:05 +03:00
Pavel Djundik 8a281bacd8 Preliminary SASL UI 2020-04-15 10:56:04 +03:00
Pavel Djundik f8f692af05 Generate client certificates and automatically do SASL EXTERNAL 2020-04-15 10:56:04 +03:00
Pavel Djundik 3900e9dd81 Consolidate irc-framework options 2020-04-15 10:56:03 +03:00
Pavel Djundik 58553d7691
Merge pull request #3798 from thelounge/richrd/image-viewer-navigation
Implement navigation in image viewer
2020-04-13 13:05:29 +03:00
Pavel Djundik f3d2dc1678
Merge pull request #3862 from thelounge/xpaw/mentions-zindex
Render sidebar over the mentions popup on mobile
2020-04-13 13:02:22 +03:00
Pavel Djundik 05ff8530cc Render sidebar over the mentions popup on mobile 2020-04-13 12:31:56 +03:00
Pavel Djundik 0fcaa46095
Merge pull request #3859 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.3.2
2020-04-13 11:45:51 +03:00
Pavel Djundik 1754c77517
Merge pull request #3857 from thelounge/xpaw/optimize-userlist-updates
Optimize user list updates for quit/part/kick events
2020-04-13 11:39:57 +03:00
Pavel Djundik 999095b7df
Merge pull request #3858 from thelounge/xpaw/mentions
Track mentions and add a window to view them
2020-04-13 10:49:06 +03:00
Renovate Bot d39a6dd012
Update dependency stylelint to v13.3.2 2020-04-11 15:25:00 +00:00
Pavel Djundik bc4f9b5f51 Track mentions and add a window to view them 2020-04-11 12:49:42 +03:00
Pavel Djundik 8e00e26054
Merge pull request #3826 from thelounge/xpaw/mocharc
Upgrade to mocha@7 and remove mochapack
2020-04-11 12:32:45 +03:00
Pavel Djundik c1607bd8e7
Merge pull request #3856 from thelounge/macos-10x
Exclude node 10 on macOS from build matrix
2020-04-11 12:31:07 +03:00
Pavel Djundik 4ce2efe86b Upgrade mocha and remove mochapack 2020-04-11 12:27:08 +03:00
Pavel Djundik 99bb58a7a7
Merge pull request #3848 from thelounge/renovate/css-loader-3.x
Update dependency css-loader to v3.5.2
2020-04-11 12:23:11 +03:00
Pavel Djundik 49189d5649
Merge pull request #3849 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.24
2020-04-11 12:23:03 +03:00
Pavel Djundik 74181d0783
Merge pull request #3847 from thelounge/renovate/babel-monorepo
Update dependency @babel/preset-env to v7.9.5
2020-04-11 12:22:54 +03:00
Pavel Djundik b885673341
Merge pull request #3843 from thelounge/renovate/semver-7.x
Update dependency semver to v7.2.2
2020-04-11 12:22:47 +03:00
Pavel Djundik 013e55a9a6 Exclude node 10 on macOS from build matrix 2020-04-11 12:19:05 +03:00
Renovate Bot 2e31325de6
Update dependency semver to v7.2.2 2020-04-11 08:59:26 +00:00
Renovate Bot 0ad907982d
Update dependency dayjs to v1.8.24 2020-04-11 08:59:16 +00:00
Renovate Bot 2156c6ba97
Update dependency css-loader to v3.5.2 2020-04-11 08:59:05 +00:00
Renovate Bot e7d0ad93f9
Update dependency @babel/preset-env to v7.9.5 2020-04-11 08:58:54 +00:00
Pavel Djundik a5bb486012
Merge pull request #3850 from thelounge/renovate/husky-4.x
Update dependency husky to v4.2.5
2020-04-11 11:57:29 +03:00
Pavel Djundik a3490af5a3
Merge pull request #3851 from thelounge/renovate/prettier-2.x
Update dependency prettier to v2.0.4
2020-04-11 11:57:19 +03:00
Pavel Djundik 484ec95f24
Merge pull request #3852 from thelounge/renovate/sinon-9.x
Update dependency sinon to v9.0.2
2020-04-11 11:57:12 +03:00
Pavel Djundik d8495bdc54
Merge pull request #3853 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.3.1
2020-04-11 11:57:03 +03:00
Pavel Djundik 2891c1e89c
Merge pull request #3854 from thelounge/renovate/vue-monorepo
Update vue monorepo to v1.0.0-beta.33
2020-04-11 11:56:56 +03:00
Pavel Djundik c8eee85b28
Merge pull request #3855 from thelounge/renovate/emoji-regex-9.x
Update dependency emoji-regex to v9
2020-04-11 11:56:48 +03:00
Renovate Bot f0d985637b
Update dependency emoji-regex to v9 2020-04-11 05:31:07 +00:00
Renovate Bot dc288c4c66
Update vue monorepo to v1.0.0-beta.33 2020-04-11 04:40:14 +00:00
Renovate Bot d810c3aec9
Update dependency stylelint to v13.3.1 2020-04-11 04:40:00 +00:00
Renovate Bot 5f06cfe483
Update dependency sinon to v9.0.2 2020-04-11 03:19:38 +00:00
Renovate Bot 9db3b009f3
Update dependency prettier to v2.0.4 2020-04-11 03:19:23 +00:00
Renovate Bot de6aa7df90
Update dependency husky to v4.2.5 2020-04-11 01:41:04 +00:00
Pavel Djundik b72e49c902
Merge pull request #3764 from thelounge/xpaw/canvas-upload
Render images in canvas before upload to remove exif
2020-04-07 21:51:46 +03:00
Pavel Djundik 63f412aab1
Merge pull request #3842 from thelounge/renovate/prettier-2.x
Update dependency prettier to v2.0.3
2020-04-06 10:23:59 +03:00
Renovate Bot b756de003e
Update dependency prettier to v2.0.3 2020-04-05 21:14:07 +00:00
Pavel Djundik aa96a2ad31
Merge pull request #3841 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.3.0
2020-04-05 10:25:24 +03:00
Pavel Djundik 37cbc1562c
Merge pull request #3840 from thelounge/renovate/nyc-15.x
Update dependency nyc to v15.0.1
2020-04-05 10:25:17 +03:00
Pavel Djundik 5b555835e2
Merge pull request #3839 from thelounge/renovate/chalk-4.x
Update dependency chalk to v4
2020-04-05 10:25:08 +03:00
Renovate Bot 119afbdf2f
Update dependency stylelint to v13.3.0 2020-04-04 00:31:30 +00:00
Renovate Bot 31f814d66d
Update dependency nyc to v15.0.1 2020-04-04 00:31:15 +00:00
Renovate Bot d584dd7e11
Update dependency chalk to v4 2020-04-02 08:25:29 +00:00
Pavel Djundik ee2e2608a3
Merge pull request #3838 from thelounge/renovate/irc-framework-4.x
Update dependency irc-framework to v4.8.1
2020-04-02 10:44:02 +03:00
Renovate Bot 7e321d399c
Update dependency irc-framework to v4.8.1 2020-04-02 07:07:28 +00:00
Pavel Djundik 86d84b70a1
Merge pull request #3837 from thelounge/renovate/uuid-7.x
Update dependency uuid to v7.0.3
2020-04-01 10:51:17 +03:00
Renovate Bot c5596f658e
Update dependency uuid to v7.0.3 2020-03-31 19:49:44 +00:00
Pavel Djundik 16ade38851
Merge pull request #3829 from thelounge/renovate/got-10.x
Update dependency got to v10.7.0
2020-03-30 23:27:06 +03:00
Renovate Bot 87d902d028
Update dependency got to v10.7.0 2020-03-30 20:19:46 +00:00
Pavel Djundik 600313fded
Merge pull request #3835 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.42.1
2020-03-30 23:18:57 +03:00
Pavel Djundik 4641cb4b8c
Merge pull request #3834 from thelounge/renovate/prettier-2.x
Update dependency prettier to v2.0.2
2020-03-30 23:18:48 +03:00
Pavel Djundik 599c4ce769
Merge pull request #3832 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.13.0
2020-03-30 23:18:39 +03:00
Pavel Djundik b14a8a267a Render uploaded images in canvas to remove exif 2020-03-30 23:16:26 +03:00
Renovate Bot 55e99b299a
Update dependency webpack to v4.42.1 2020-03-28 01:40:29 +00:00
Renovate Bot 20e47aaadb
Update dependency prettier to v2.0.2 2020-03-28 01:40:14 +00:00
Renovate Bot 55767d733d
Update dependency @fortawesome/fontawesome-free to v5.13.0 2020-03-28 00:20:40 +00:00
Pavel Djundik 56dfa5ef40
Merge pull request #3828 from stevenengler/patch-1
Fix body overscroll and overflow on iOS Safari
2020-03-23 22:01:45 +02:00
Steven Engler 39e70670b5
Fix body overscroll and overflow on iOS Safari
In order to prevent scrolling past the edges of the body (overscroll)
in Safari on iOS, the overflow must be hidden (not only overflow-y).
2020-03-23 15:15:30 -04:00
Pavel Djundik beac893dd0
Merge pull request #3825 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.10.1
2020-03-23 12:16:12 +02:00
Renovate Bot 6e655d457e
Update dependency eslint-config-prettier to v6.10.1 2020-03-23 08:21:01 +00:00
Pavel Djundik a7db950a52
Merge pull request #3824 from thelounge/renovate/prettier-2.x
Update dependency prettier to v2.0.1
2020-03-23 10:19:54 +02:00
Pavel Djundik f4528e6f00
Merge pull request #3810 from thelounge/xpaw/remove-fs-extra
Replace all uses of `fs-extra` with native methods
2020-03-23 10:19:44 +02:00
Pavel Djundik c35412625e
Merge pull request #3814 from thelounge/xpaw/fix-3813
Do not handle navigation keybinds in inputs if not empty
2020-03-23 10:19:37 +02:00
Renovate Bot 4c3594b832
Update dependency prettier to v2.0.1 2020-03-22 19:20:33 +00:00
Pavel Djundik 52bf7b116e Do not handle keybinds in inputs if not empty
Fix #3813
2020-03-22 20:44:14 +02:00
Pavel Djundik 6de6f8185e Clean up folders in after test runs 2020-03-22 20:43:06 +02:00
Pavel Djundik 487a438f02 Replace all uses of fs-extra with native methods 2020-03-22 20:42:16 +02:00
Pavel Djundik 4bf4b7baf0
Merge pull request #3797 from thelounge/xpaw/fix-changelog-check
Pass in client manager object in update checker
2020-03-22 20:41:39 +02:00
Pavel Djundik 9c2607df89
Merge pull request #3820 from thelounge/renovate/babel-monorepo
Update babel monorepo
2020-03-22 11:48:48 +02:00
Pavel Djundik 05e806d762
Merge pull request #3821 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.23
2020-03-22 11:48:41 +02:00
Pavel Djundik 68618da7f1
Merge pull request #3822 from thelounge/renovate/vue-monorepo
Update dependency vue-loader to v15.9.1
2020-03-22 11:48:33 +02:00
Pavel Djundik eb171c01f5
Merge pull request #3823 from thelounge/renovate/prettier-2.x
Update dependency prettier to v2
2020-03-22 11:48:24 +02:00
Pavel Djundik 881b3eda19 Run format after updating to prettier 2.0 2020-03-21 22:55:36 +02:00
Renovate Bot a46c9e8403
Update dependency prettier to v2 2020-03-21 20:10:25 +00:00
Renovate Bot 9da36b9966
Update dependency vue-loader to v15.9.1 2020-03-21 01:33:57 +00:00
Renovate Bot c2e6b13504
Update dependency dayjs to v1.8.23 2020-03-21 00:54:46 +00:00
Renovate Bot 42d568ad2c
Update babel monorepo 2020-03-21 00:54:32 +00:00
Pavel Djundik 4b29cdeb0c
Merge pull request #3817 from thelounge/xpaw/fix-2562
Separate active sessions section
2020-03-20 13:42:49 +02:00
Pavel Djundik a3c204f978
Merge pull request #3816 from thelounge/xpaw/public-connect
Remove "The Lounge" from connect in public
2020-03-19 21:39:29 +02:00
Pavel Djundik 0f1e7d5036
Merge pull request #3815 from thelounge/xpaw/esc-help
Add escape key description to help section
2020-03-19 21:39:10 +02:00
Pavel Djundik a6f70696f3 Separate active sessions section
Fixes #2562
2020-03-17 17:36:13 +02:00
Pavel Djundik 4c177b8d02 Remove "The Lounge" from connect in public 2020-03-17 12:20:32 +02:00
Pavel Djundik ecda9e225e Add escape key to help section 2020-03-17 12:06:50 +02:00
Pavel Djundik a9d2b30d96
Merge pull request #3808 from thelounge/xpaw/fix-part
Fix up first argument not being used as part message
2020-03-16 20:29:46 +02:00
Pavel Djundik 424bc4f7df Fix up first argument not being used as part message 2020-03-15 20:16:53 +02:00
Pavel Djundik 52002c3e22
Merge pull request #3807 from thelounge/renovate/commander-5.x
Update dependency commander to v5
2020-03-15 14:03:35 +02:00
Pavel Djundik 80b0e8ad12 Fix up commander changes 2020-03-15 14:00:02 +02:00
Renovate Bot b000a594f4 Update dependency commander to v5 2020-03-15 14:00:02 +02:00
Pavel Djundik 0ec242738f
Merge pull request #3801 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.1.4
2020-03-15 13:54:05 +02:00
Pavel Djundik 5e935189a3
Merge pull request #3804 from thelounge/renovate/sinon-9.x
Update dependency sinon to v9.0.1
2020-03-15 13:53:56 +02:00
Pavel Djundik 0035910765
Merge pull request #3805 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.2.1
2020-03-15 13:53:49 +02:00
Renovate Bot 9e6b6c582f
Update dependency stylelint to v13.2.1 2020-03-15 11:25:32 +00:00
Renovate Bot b7562362c1
Update dependency sinon to v9.0.1 2020-03-15 11:25:22 +00:00
Renovate Bot 9926c83ba7
Update dependency file-type to v14.1.4 2020-03-15 11:25:11 +00:00
Pavel Djundik 6c0acfcfb7
Merge pull request #3806 from thelounge/renovate/vue-monorepo
Update vue monorepo
2020-03-15 13:24:40 +02:00
Renovate Bot babadbd955
Update vue monorepo 2020-03-14 01:14:38 +00:00
Pavel Djundik 64aa510abf Bind events in image viewer only once 2020-03-10 15:36:58 +02:00
Pavel Djundik 0b38a88147
Merge pull request #3800 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.22.4
2020-03-10 15:21:45 +02:00
Pavel Djundik b3fa46ad10
Merge pull request #3799 from thelounge/xpaw/upgrade-lock
Upgrade yarn.lock
2020-03-10 15:02:34 +02:00
Renovate Bot 5e30d3698d
Update dependency yarn to v1.22.4 2020-03-10 12:42:59 +00:00
Pavel Djundik 04de1ebc30 Update yarn.lock 2020-03-10 12:58:49 +02:00
Richard Lewis ef473b0f53 Implement navigation in image viewer. 2020-03-09 18:54:21 +02:00
Pavel Djundik 0e62103010 Pass in client manager object in update checker 2020-03-09 17:58:40 +02:00
Pavel Djundik a4ef328d8d
Merge pull request #3778 from thelounge/xpaw/clear-history
Clear channel history (and a new confirmation dialog)
2020-03-09 15:59:37 +02:00
Pavel Djundik e5596d9d81
Merge pull request #3796 from thelounge/renovate/eslint-plugin-vue-6.x
Update dependency eslint-plugin-vue to v6.2.2
2020-03-09 10:37:13 +02:00
Pavel Djundik e47e54b934
Merge pull request #3787 from thelounge/xpaw/12h
Add an option to display 12h times
2020-03-09 10:36:07 +02:00
Pavel Djundik b8de7e68b5
Merge pull request #3790 from thelounge/xpaw/settings-group
Add role=group to status messages setting
2020-03-09 10:36:00 +02:00
Pavel Djundik 464c54b2cb
Merge pull request #3789 from thelounge/xpaw/fix-unhandled-chan
Fix sending unhandled numerics to target channel
2020-03-09 10:35:49 +02:00
Pavel Djundik 6b46a55def
Merge pull request #3795 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.42.0
2020-03-09 10:35:03 +02:00
Pavel Djundik 37f4e0ff93
Merge pull request #3792 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.22.1
2020-03-09 10:34:56 +02:00
Pavel Djundik 9277907c43
Merge pull request #3791 from thelounge/renovate/uuid-7.x
Update dependency uuid to v7.0.2
2020-03-09 10:34:49 +02:00
Pavel Djundik 960862df27
Merge pull request #3786 from thelounge/renovate/vue-monorepo
Update dependency vue-router to v3.1.6
2020-03-09 10:34:42 +02:00
Pavel Djundik 1376177a09
Merge pull request #3785 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.22
2020-03-09 10:34:34 +02:00
Pavel Djundik d2994d501f
Merge pull request #3784 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.8.7
2020-03-09 10:34:26 +02:00
Renovate Bot 1ea9d6c2ac
Update dependency yarn to v1.22.1 2020-03-09 08:06:10 +00:00
Renovate Bot 0a6f8a76ec
Update dependency webpack to v4.42.0 2020-03-09 08:06:00 +00:00
Renovate Bot c57b42c22b
Update dependency vue-router to v3.1.6 2020-03-09 08:05:49 +00:00
Renovate Bot f93061de29
Update dependency uuid to v7.0.2 2020-03-09 08:05:39 +00:00
Renovate Bot 06a15181c3
Update dependency eslint-plugin-vue to v6.2.2 2020-03-09 08:05:30 +00:00
Renovate Bot d11b704f22
Update dependency dayjs to v1.8.22 2020-03-09 08:05:20 +00:00
Renovate Bot 48f96e9ae0
Update babel monorepo to v7.8.7 2020-03-09 08:04:57 +00:00
Pavel Djundik 47b254a29e v4.1.0 2020-03-08 22:53:26 +02:00
Pavel Djundik 7ba2807b01 Add changelog for v4.1.0 2020-03-08 22:53:01 +02:00
Pavel Djundik 6121a3ab0b
Merge pull request #3793 from NotWoods/patch-1
Add maskable purpose & removes monochrome
2020-03-06 23:50:24 +02:00
Tiger Oakes d8ab40d8ee
Add maskable purpose to PNG icons 2020-03-06 13:37:27 -08:00
Pavel Djundik 8d119630eb Add role=group to status messages setting
Fixes #1909
2020-03-03 12:15:42 +02:00
Pavel Djundik 5233fb2dbb Fix sending unhandled numerics to target channel 2020-03-03 11:47:09 +02:00
Pavel Djundik 234938ed4b Fix up time width with AM/PM 2020-02-29 11:51:12 +02:00
Pavel Djundik 3630ab8519 Add an option to display 12h times 2020-02-29 11:37:45 +02:00
Pavel Djundik c463d1ddd3 Emit an event to clear history on all open clients 2020-02-28 17:01:28 +02:00
Pavel Djundik 44a8925b8c Create a generic confirmation dialog 2020-02-28 17:01:28 +02:00
Pavel Djundik 7216b8124b Add context menu to clear channel history 2020-02-28 17:01:28 +02:00
Pavel Djundik eb7f9ab298 Implement channel history clearing on the server 2020-02-28 17:01:28 +02:00
Pavel Djundik 6f04216af5 v4.1.0-rc.1 2020-02-27 21:07:35 +02:00
Pavel Djundik b79c91ff1e
Add changelog entry for v4.1.0-rc.1 2020-02-27 21:07:06 +02:00
Pavel Djundik d2e4f56219
Merge pull request #3783 from thelounge/xpaw/uninstall
Fix not being able to uninstall packages
2020-02-27 21:06:42 +02:00
Pavel Djundik ee0002fe6a
Merge pull request #3741 from RockyTV/rockytv/better-error-msgs
Write prettier error messages for certain errors
2020-02-27 21:00:16 +02:00
Alexandre Oliveira ab8593d3cd Write prettier error messages for IRC errors 2020-02-27 15:27:34 -03:00
Pavel Djundik 8f15548770 Fix not being able to uninstall packages 2020-02-27 19:21:01 +02:00
Pavel Djundik d99d56fe81
Merge pull request #3762 from thelounge/xpaw/sqlite-serialize
Change sqlite parallelize to serialize when loading messages
2020-02-27 14:02:29 +02:00
Pavel Djundik 365613f0ee
Merge pull request #3767 from thelounge/setname
Unprefix setname cap
2020-02-27 14:01:03 +02:00
Pavel Djundik bec6665044
Merge pull request #3770 from thelounge/sts
Implement strict transport security (STS) for IRC networks
2020-02-27 13:53:51 +02:00
Pavel Djundik 8976fa163e Do not reconnect if STS cap is received in CAP NEW 2020-02-27 13:48:48 +02:00
Pavel Djundik db866f9823 Refresh STS policy expiration on network quit 2020-02-27 13:48:48 +02:00
Pavel Djundik 568427ca98 Disable changing TLS if STS is enforced 2020-02-27 13:48:48 +02:00
Pavel Djundik d9985e7318 Enforce STS policies 2020-02-27 13:48:47 +02:00
Pavel Djundik 9b9db35e3c Implement basic STS reconnection 2020-02-27 13:48:47 +02:00
Pavel Djundik 77279675ec
Merge pull request #3780 from thelounge/renovate/uuid-7.x
Update dependency uuid to v7.0.1
2020-02-27 13:48:32 +02:00
Pavel Djundik 2127153b73
Merge pull request #3782 from thelounge/renovate/irc-framework-4.x
Update dependency irc-framework to v4.7.0
2020-02-27 13:48:22 +02:00
Renovate Bot 66b6517855
Update dependency irc-framework to v4.7.0 2020-02-27 11:00:53 +00:00
Renovate Bot c62d3c2f15
Update dependency uuid to v7.0.1 2020-02-26 22:28:32 +00:00
Richard Lewis abd414beb9
Merge pull request #3781 from thelounge/xpaw/wave
Improve wav audio detection
2020-02-26 23:19:12 +02:00
Richard Lewis 58003e1f59
Merge pull request #3779 from thelounge/xpaw/ios-notifs-note
Add an explanation why push notifications are not supported on iOS
2020-02-26 23:18:44 +02:00
Pavel Djundik 63fd0def6c Improve wav audio file support 2020-02-26 10:07:40 +02:00
Pavel Djundik 8a515a8a70 Add an explanation why push notifications are not supported on iOS 2020-02-25 20:04:33 +02:00
Pavel Djundik e20b1a55c3
Merge pull request #3776 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.1.3
2020-02-25 10:44:54 +02:00
Pavel Djundik b48adc434d
Merge pull request #3774 from thelounge/renovate/sinon-9.x
Update dependency sinon to v9
2020-02-25 10:44:45 +02:00
Pavel Djundik f213875d48
Merge pull request #3743 from thelounge/renovate/got-10.x
Update dependency got to v10.6.0
2020-02-25 10:44:38 +02:00
Pavel Djundik 7401174523
Merge pull request #3763 from thelounge/renovate/eslint-plugin-vue-6.x
Update dependency eslint-plugin-vue to v6.2.1
2020-02-25 10:44:28 +02:00
Renovate Bot 641cd951cb
Update dependency sinon to v9 2020-02-25 08:25:23 +00:00
Renovate Bot b734a8b983
Update dependency got to v10.6.0 2020-02-25 08:24:58 +00:00
Renovate Bot a8ffe7768e
Update dependency file-type to v14.1.3 2020-02-25 08:24:45 +00:00
Renovate Bot 9500edc89f
Update dependency eslint-plugin-vue to v6.2.1 2020-02-25 08:24:34 +00:00
Pavel Djundik 381cfb7099
Merge pull request #3777 from thelounge/renovate/uuid-7.x
Update dependency uuid to v7
2020-02-25 10:23:38 +02:00
Pavel Djundik e0d5f4c2ff Change uuidv4 require 2020-02-24 15:35:15 +02:00
Renovate Bot 0134276f01
Update dependency uuid to v7 2020-02-24 13:27:25 +00:00
Pavel Djundik 258db10ea9
Merge pull request #3765 from thelounge/xpaw/showinactive-unread
Fix incorrectly updating unread counter for 'show in active' messages
2020-02-18 18:01:32 +02:00
Pavel Djundik 3ca9fd2e80 Unprefix setname cap 2020-02-18 14:07:03 +02:00
Pavel Djundik 9db1d0f7c8 Fix incorrectly updating unread counter for 'show in active' messages 2020-02-17 17:02:34 +02:00
Pavel Djundik 5a0e0b6718 Change sqlite parallelize to serialize when loading messages 2020-02-15 11:50:07 +02:00
Pavel Djundik 44de1dd03f
Merge pull request #3761 from thelounge/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.11
2020-02-15 11:45:46 +02:00
Renovate Bot 2cf2c6d0e6
Update dependency webpack-cli to v3.3.11 2020-02-15 09:30:49 +00:00
Pavel Djundik 94978b334c
Merge pull request #3757 from thelounge/renovate/husky-4.x
Update dependency husky to v4.2.3
2020-02-15 11:29:30 +02:00
Pavel Djundik 96099694d6
Merge pull request #3758 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.2.0
2020-02-15 11:29:23 +02:00
Pavel Djundik 3f0afed3c9
Merge pull request #3759 from thelounge/renovate/vue-monorepo
Update dependency vue-loader to v15.9.0
2020-02-15 11:29:14 +02:00
Pavel Djundik c6054c3f40
Merge pull request #3760 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.41.6
2020-02-15 11:29:04 +02:00
Renovate Bot 22e7217e06
Update dependency webpack to v4.41.6 2020-02-15 01:32:33 +00:00
Renovate Bot 1c13ff7922
Update dependency vue-loader to v15.9.0 2020-02-15 01:32:18 +00:00
Renovate Bot 6c9d6d04de
Update dependency stylelint to v13.2.0 2020-02-15 00:22:31 +00:00
Renovate Bot bf3f593004
Update dependency husky to v4.2.3 2020-02-15 00:22:15 +00:00
Pavel Djundik 8e25fa8a3b
Merge pull request #3755 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14.1.2
2020-02-14 14:54:13 +02:00
Pavel Djundik 4aec23a6fc
Merge pull request #3754 from thelounge/renovate/semver-7.x
Update dependency semver to v7.1.3
2020-02-14 14:54:06 +02:00
Pavel Djundik 7fdb70d451
Merge pull request #3753 from thelounge/xpaw/yarn-stdout
Wrap stdout parsing from yarn into try/catch
2020-02-14 14:52:01 +02:00
Renovate Bot d95f1fa5b6
Update dependency file-type to v14.1.2 2020-02-12 20:13:44 +00:00
Renovate Bot 989ecdb2f6
Update dependency semver to v7.1.3 2020-02-11 22:38:43 +00:00
Pavel Djundik 469fe577f2 Wrap stdout parsing from yarn into try/catch 2020-02-11 11:48:02 +02:00
Richard Lewis 1fb78d7218
Merge pull request #3594 from thelounge/richrd/jump-to
Jump to channel switcher
2020-02-10 19:56:04 +02:00
Richard Lewis 9e76fe2a76 Fix race condition and remove redundant computed properties. 2020-02-10 19:43:44 +02:00
Richard Lewis 054760d49f Add keybind to help window. 2020-02-10 19:43:44 +02:00
Richard Lewis f5884957a5 Fix CSS styles, scroll to selected channel and improve bindings.
Co-Authored-By: Pavel Djundik <xPaw@users.noreply.github.com>
2020-02-10 19:43:44 +02:00
Richard Lewis 606c62dc70 Move styles to component. 2020-02-10 19:04:34 +02:00
Richard Lewis 0b5cbceffd Implement jump to channel feature. 2020-02-10 19:04:35 +02:00
Pavel Djundik dbdf98537c
Merge pull request #3751 from thelounge/renovate/stylelint-config-standard-20.x
Update dependency stylelint-config-standard to v20
2020-02-10 16:45:57 +02:00
Richard Lewis f12a13916b
Merge pull request #3712 from thelounge/xpaw/fix-3302
Add support for webirc secure option
2020-02-10 16:42:56 +02:00
Richard Lewis fbf6f48d7a
Merge pull request #3744 from thelounge/xpaw/icon-purpose
Add icon purpose in webmanifest
2020-02-10 16:41:36 +02:00
Richard Lewis f7f92c5f39
Merge pull request #3752 from thelounge/xpaw/fix-ci-timeout
Fix increasing test timeout on github actions
2020-02-10 16:41:23 +02:00
Richard Lewis 86abe1e2df
Merge pull request #3735 from thelounge/emoji-v13
Add emojis 13.0
2020-02-10 16:36:24 +02:00
Richard Lewis 1bdeae2b76
Merge pull request #3730 from thelounge/xpaw/user-search-fixes
Small fixes to user list search
2020-02-10 16:34:41 +02:00
Richard Lewis f8642dd2a5
Merge pull request #3711 from thelounge/xpaw/server-notice-host
Use hostname from notice if available
2020-02-10 16:34:22 +02:00
Pavel Djundik 1b2894bf99 Fix increasing test timeout on github actions 2020-02-09 14:21:45 +02:00
Renovate Bot 61305cd27c
Update dependency stylelint-config-standard to v20 2020-02-09 10:48:15 +00:00
Pavel Djundik 0f44c51b00
Merge pull request #3750 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13.1.0
2020-02-09 12:47:10 +02:00
Renovate Bot d0697d39d7
Update dependency stylelint to v13.1.0 2020-02-08 20:33:49 +00:00
Pavel Djundik 0639fdb410
Merge pull request #3749 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.20
2020-02-08 11:21:20 +02:00
Pavel Djundik 705261cdb5 Merge pull request #3748 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.12.1
2020-02-08 11:21:12 +02:00
Renovate Bot b5a7bc6be6
Update dependency dayjs to v1.8.20 2020-02-08 00:29:04 +00:00
Renovate Bot 1ccd910e14
Update dependency @fortawesome/fontawesome-free to v5.12.1 2020-02-08 00:28:48 +00:00
Pavel Djundik 25b870fcd1 Add icon purpose in webmanifest 2020-02-06 23:31:34 +02:00
Pavel Djundik 4ca97bc955
Merge pull request #3742 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.22.0
2020-02-06 14:07:39 +02:00
Pavel Djundik 1b9040deed
Merge pull request #3739 from thelounge/renovate/file-type-14.x
Update dependency file-type to v14
2020-02-06 14:07:27 +02:00
Pavel Djundik 87c9abe9da Change minimumBytes in file-type 2020-02-06 12:41:43 +02:00
Renovate Bot 4d5f15b32e
Update dependency file-type to v14 2020-02-05 19:27:57 +00:00
Renovate Bot 342b97f68b
Update dependency yarn to v1.22.0 2020-02-05 19:16:16 +00:00
Pavel Djundik 6aabd9bacb Optimize user list updates for quit/part/kick events 2020-02-01 19:15:46 +02:00
Pavel Djundik 15100c853c
Merge pull request #3738 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.10.0
2020-02-01 19:06:29 +02:00
Pavel Djundik 55f3f9ef13
Merge pull request #3733 from thelounge/renovate/semver-7.x
Update dependency semver to v7.1.2
2020-02-01 19:06:15 +02:00
Pavel Djundik 77c2fc0ea7
Merge pull request #3737 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.8.4
2020-02-01 19:06:09 +02:00
Pavel Djundik 60b398c6d8
Merge pull request #3734 from thelounge/renovate/got-10.x
Update dependency got to v10.4.0
2020-02-01 19:06:01 +02:00
Renovate Bot 6bedf78019
Update dependency eslint-config-prettier to v6.10.0 2020-02-01 00:56:11 +00:00
Renovate Bot c17e0a0813
Update babel monorepo to v7.8.4 2020-02-01 00:55:55 +00:00
Pavel Djundik 6422136d50 Add emojis 13.0 2020-01-31 10:36:06 +02:00
Renovate Bot 6fcfcb6219
Update dependency got to v10.4.0 2020-01-31 07:39:54 +00:00
Renovate Bot 99eab4ddcb
Update dependency semver to v7.1.2 2020-01-31 01:31:56 +00:00
Pavel Djundik 7afafdd25e Change mouseover to mouseenter 2020-01-27 13:27:52 +02:00
Pavel Djundik 66cdec0075 Fix right padding on user list search 2020-01-27 13:27:52 +02:00
Pavel Djundik 8b71e6a18e
Merge pull request #3717 from thelounge/xpaw/fix-username
Undo username pattern
2020-01-27 11:06:17 +02:00
Pavel Djundik 803fe930f8
Merge pull request #3718 from thelounge/xpaw/fix-3716
Fix "$1" when completing nicks outside of textcomplete menu
2020-01-27 11:06:10 +02:00
Pavel Djundik 34436f9a72
Merge pull request #3723 from thelounge/xpaw/context-position
Fix DOMRect coordinates in Safari
2020-01-27 11:06:02 +02:00
Pavel Djundik 965b50a341
Merge pull request #3728 from thelounge/xpaw/active-buffer
Increase buffer size for active and scrolled down channels
2020-01-27 11:05:55 +02:00
Pavel Djundik e4c01f7c2f
Merge pull request #3727 from thelounge/renovate/sinon-8.x
Update dependency sinon to v8.1.1
2020-01-27 11:05:30 +02:00
Pavel Djundik d8682126f3
Merge pull request #3725 from thelounge/renovate/mousetrap-1.x
Update dependency mousetrap to v1.6.5
2020-01-27 11:05:23 +02:00
Pavel Djundik 2f3f2c4d90
Merge pull request #3724 from thelounge/renovate/husky-4.x
Update dependency husky to v4.2.1
2020-01-27 11:05:14 +02:00
Pavel Djundik 6c20a59993
Merge pull request #3722 from thelounge/renovate/got-10.x
Update dependency got to v10.3.0
2020-01-27 11:05:07 +02:00
Pavel Djundik f92a442330 Increase buffer size for active and scrolled down channels 2020-01-25 11:00:55 +02:00
Renovate Bot d607111c86
Update dependency sinon to v8.1.1 2020-01-25 01:50:08 +00:00
Renovate Bot 245b44ab02
Update dependency mousetrap to v1.6.5 2020-01-25 00:51:35 +00:00
Renovate Bot 62171e13b3
Update dependency husky to v4.2.1 2020-01-25 00:51:19 +00:00
Renovate Bot b176d26302
Update dependency got to v10.3.0 2020-01-24 14:56:21 +00:00
Pavel Djundik 10cba8d9b0 Fix DOMRect coordinates in Safari 2020-01-24 16:55:29 +02:00
Pavel Djundik b890e7e976
Merge pull request #3721 from thelounge/xpaw/fix-3690
Fix escape key handling
2020-01-24 16:08:30 +02:00
Pavel Djundik bbe6b34371 Unfocus chat input when pressing escape 2020-01-24 15:50:01 +02:00
Pavel Djundik 2451f222e8 Bind esc key handler once
Fixes #3690
2020-01-24 15:50:01 +02:00
Pavel Djundik a9bad593b0
Merge pull request #3720 from thelounge/xpaw/fix-3719
Ignore Alt+<letter> keybinds when focused in chat input
2020-01-24 15:49:04 +02:00
Pavel Djundik 63540e102b Ignore Alt+<letter> keybinds when focused in chat input
Fixes #3719
2020-01-23 22:50:37 +02:00
Pavel Djundik 4e6bd9e943 Fix "$1" when completing nicks outside of textcomplete menu
Fixes #3716
2020-01-23 22:25:34 +02:00
Pavel Djundik 0dd0d8fb12 Undo username pattern 2020-01-23 22:14:30 +02:00
Pavel Djundik e8ba4f4fb9
Merge pull request #3658 from thelounge/xpaw/version-notify
Display icon when update is available, check on server start
2020-01-22 10:29:18 +02:00
Pavel Djundik 5b68fb5054
Merge pull request #3715 from thelounge/xpaw/nick-pattern
Disallow some invalid characters in nicknames and usernames
2020-01-22 10:28:51 +02:00
Pavel Djundik 8b04979eac
Merge pull request #3676 from thelounge/xpaw/csp
Remove `child-src` from CSP, add `base-uri 'none'`
2020-01-22 10:28:44 +02:00
Pavel Djundik 510b859df9
Merge pull request #3682 from thelounge/xpaw/fix-undefined-theme
Fix settings update when unknown theme is stored
2020-01-22 10:28:28 +02:00
Pavel Djundik f1a11d3a0b
Merge pull request #3696 from thelounge/xpaw/fix-2960
Trigger autocompletion only after whitespace
2020-01-22 10:28:11 +02:00
Pavel Djundik 08f77528d9
Merge pull request #3703 from thelounge/xpaw/fix-3699
Open last channel in the list when creating a network
2020-01-22 10:28:01 +02:00
Pavel Djundik 184936fa38
Merge pull request #3713 from thelounge/renovate/file-type-13.x
Update dependency file-type to v13.1.2
2020-01-22 10:27:40 +02:00
Pavel Djundik 4fbca2b219
Merge pull request #3706 from thelounge/renovate/husky-4.x
Update dependency husky to v4.2.0
2020-01-22 10:27:32 +02:00
Pavel Djundik 309b9027be
Merge pull request #3707 from thelounge/renovate/sinon-8.x
Update dependency sinon to v8.1.0
2020-01-22 10:27:24 +02:00
Pavel Djundik b025647ad9
Merge pull request #3705 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.8.3
2020-01-22 10:27:15 +02:00
Pavel Djundik cdd28ba2cb
Merge pull request #3708 from thelounge/renovate/vue-monorepo
Update vue monorepo
2020-01-22 10:27:07 +02:00
Renovate Bot 5b7abd6e02
Update dependency file-type to v13.1.2 2020-01-22 04:59:46 +00:00
Renovate Bot aa0df1d6b4
Update dependency husky to v4.2.0 2020-01-21 22:11:58 +00:00
Pavel Djundik 1a7135c5e0 Clean up strings on server 2020-01-21 15:47:59 +02:00
Pavel Djundik de6d6906f8 Disallow some invalid characters in nicknames and usernames
@, !, : and whitespace are disallowed due to being part of the protocol/hostmask
2020-01-21 15:44:48 +02:00
Renovate Bot 5657d9c221
Update vue monorepo 2020-01-20 14:30:54 +00:00
Pavel Djundik 26bf0850d7 Stub checkForUpdates in tests 2020-01-19 01:14:52 +02:00
Pavel Djundik 36f4284e07 Add support for webirc secure option
Fixes #3302
2020-01-19 00:56:07 +02:00
Pavel Djundik 4d3fd1c8f2 Use hostname from notice if available 2020-01-19 00:53:03 +02:00
Renovate Bot b9e7e401ca
Update dependency sinon to v8.1.0 2020-01-18 01:31:45 +00:00
Renovate Bot 6b9d2baf97
Update babel monorepo to v7.8.3 2020-01-18 00:29:03 +00:00
Pavel Djundik d5ac13f91c Notify all connected clients when new version is available 2020-01-17 12:17:37 +02:00
Pavel Djundik 3f928d8742 Check for updates every day 2020-01-17 12:09:42 +02:00
Pavel Djundik efc421c0a6 Display icon in footer when an update is available 2020-01-17 12:03:16 +02:00
Pavel Djundik 0bdac63953 Check for TL updates on server start 2020-01-17 12:03:14 +02:00
Pavel Djundik 304e8bf5b0
Merge pull request #3704 from thelounge/renovate/uuid-3.x
Update dependency uuid to v3.4.0
2020-01-17 11:47:41 +02:00
Pavel Djundik fd04a528f6
Merge pull request #3698 from thelounge/renovate/file-type-13.x
Update dependency file-type to v13.1.0
2020-01-17 11:47:32 +02:00
Renovate Bot 8842242fb4
Update dependency uuid to v3.4.0 2020-01-16 21:30:47 +00:00
Pavel Djundik 6dac3d122a Open last channel in the list when creating a network
Fixes #3699
2020-01-16 18:07:16 +02:00
Renovate Bot bcd7e7cfff
Update dependency file-type to v13.1.0 2020-01-15 03:27:23 +00:00
Pavel Djundik d7513b43dc
Merge pull request #3686 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.19
2020-01-13 12:46:15 +02:00
Pavel Djundik ba4ec355e1
Merge pull request #3693 from thelounge/renovate/babel-monorepo
Update babel monorepo
2020-01-13 12:46:06 +02:00
Pavel Djundik fe17324fef
Merge pull request #3694 from thelounge/renovate/stylelint-13.x
Update dependency stylelint to v13
2020-01-13 12:45:55 +02:00
Renovate Bot 7dfbf215db
Update babel monorepo 2020-01-12 22:57:21 +00:00
Renovate Bot 4bfd599393
Update dependency stylelint to v13 2020-01-12 14:32:28 +00:00
Renovate Bot 10e31a1ce0
Update dependency dayjs to v1.8.19 2020-01-12 14:32:06 +00:00
Pavel Djundik 46f9f6a6a3
Merge pull request #3689 from thelounge/renovate/got-10.x
Update dependency got to v10.2.2
2020-01-12 16:31:10 +02:00
Pavel Djundik fef4b8b93a
Merge pull request #3688 from thelounge/renovate/husky-4.x
Update dependency husky to v4
2020-01-12 16:30:50 +02:00
Pavel Djundik 8a2631f503
Merge pull request #3687 from thelounge/renovate/sinon-8.x
Update dependency sinon to v8.0.4
2020-01-12 16:30:43 +02:00
Pavel Djundik 858fe0185a
Merge pull request #3685 from thelounge/renovate/css-loader-3.x
Update dependency css-loader to v3.4.2
2020-01-12 16:30:36 +02:00
Pavel Djundik f1e6ada2d0
Merge pull request #3683 from thelounge/renovate/file-type-13.x
Update dependency file-type to v13.0.3
2020-01-12 16:30:26 +02:00
Pavel Djundik 4682a83827 Trigger autocompletion only after whitespace
Fixes #2960
Fixes #3695
2020-01-12 16:27:29 +02:00
Renovate Bot e83a1ac7d8
Update dependency husky to v4 2020-01-12 05:54:05 +00:00
Renovate Bot f0967ddf5f
Update dependency got to v10.2.2 2020-01-11 15:39:07 +00:00
Renovate Bot 144c4b7ce9
Update dependency sinon to v8.0.4 2020-01-11 01:40:40 +00:00
Renovate Bot 69bac8f517
Update dependency css-loader to v3.4.2 2020-01-11 00:32:30 +00:00
Renovate Bot f1a0ccbe13
Update dependency file-type to v13.0.3 2020-01-10 18:27:18 +00:00
Pavel Djundik fa57814678
Merge pull request #3684 from thelounge/richrd/style-loading
Load styles from vue components, fix hot reload
2020-01-10 20:03:24 +02:00
Pavel Djundik fbdd888c3d Disable SW caching in dev build 2020-01-09 22:40:10 +02:00
Pavel Djundik 2e49175840
Merge pull request #3681 from thelounge/xpaw/remove-vue-filter
Remove the only use of Vue.filter
2020-01-09 21:14:10 +02:00
Pavel Djundik 6164862af5
Merge pull request #3678 from thelounge/renovate/file-type-13.x
Update dependency file-type to v13
2020-01-08 16:22:37 +02:00
Pavel Djundik b5f5775cfc
Merge pull request #3665 from thelounge/renovate/css-loader-3.x
Update dependency css-loader to v3.4.1
2020-01-08 16:22:30 +02:00
Pavel Djundik 41e3762e57 Update file-type api usage 2020-01-08 16:11:30 +02:00
Renovate Bot 299a9324f6
Update dependency file-type to v13 2020-01-08 14:06:37 +00:00
Renovate Bot df5cb3081e
Update dependency css-loader to v3.4.1 2020-01-08 14:06:24 +00:00
Pavel Djundik 85bc4df1e2
Merge pull request #3674 from thelounge/renovate/mime-types-2.x
Update dependency mime-types to v2.1.26
2020-01-08 16:04:57 +02:00
Pavel Djundik 14f6032316
Merge pull request #3675 from thelounge/renovate/commander-4.x
Update dependency commander to v4.1.0
2020-01-08 16:04:48 +02:00
Richard Lewis 23ac0fef32 Load styles from vue components, fix hot reload
Fixes #3615
2020-01-08 16:02:09 +02:00
Pavel Djundik a2349f96cb Fix settings update when unknown theme is stored 2020-01-08 16:00:47 +02:00
Pavel Djundik 1c190d1adb Remove the only use of Vue.filter 2020-01-08 11:11:44 +02:00
Renovate Bot 5b34395587
Update dependency commander to v4.1.0 2020-01-06 09:20:40 +00:00
Renovate Bot 2266350f23
Update dependency mime-types to v2.1.26 2020-01-06 08:47:58 +00:00
Pavel Djundik 79cbe63067
Merge pull request #3670 from thelounge/xpaw/show-in-active
Show an icon for "show in active" messages
2020-01-06 10:39:38 +02:00
Pavel Djundik e73575a342 Remove showInActive if active network differs 2020-01-05 19:44:03 +02:00
Pavel Djundik 5c64eaf41e Show an icon for "show in active" messages 2020-01-05 19:44:03 +02:00
Pavel Djundik b93cae2e01
Merge pull request #3647 from thelounge/xpaw/mode-string
Display the original sets mode string and make nicks clickable
2020-01-04 15:22:27 +02:00
Pavel Djundik 00cdb6e808
Merge pull request #3663 from thelounge/xpaw/cyclical-dep
Remove cyclical dependency in router<->webpush
2020-01-04 15:20:23 +02:00
Pavel Djundik e0cd9cbcdf
Merge pull request #3662 from thelounge/xpaw/fix-ref
Check that usernameInput ref exists
2020-01-04 15:20:16 +02:00
Pavel Djundik 5fe0710724 Remove cyclical dependency in router<->webpush 2020-01-03 20:02:22 +02:00
Pavel Djundik c4ddf6d93e Check that usernameInput ref exists 2020-01-03 19:51:38 +02:00
Pavel Djundik 7b507e5248
Merge pull request #3660 from thelounge/xpaw/keybinds
Add keybinds to toggle sidebar, user list, and networks
2020-01-03 16:59:41 +02:00
Pavel Djundik 1870145674 Add keybinds to expand and collapse networks
Fixes #3523
2020-01-02 22:45:21 +02:00
Pavel Djundik ff4fd0a13d Add keyboard shortcuts to toggle server/user list
Fixes #2345
2020-01-02 22:24:20 +02:00
Pavel Djundik 98aef9b6ad
Merge pull request #3659 from thelounge/xpaw/footer-active
Fix active styles on footer buttons
2020-01-02 22:02:03 +02:00
Pavel Djundik bf0a8c4e4d Fix active styles on footer buttons 2020-01-02 12:57:36 +02:00
Pavel Djundik ba3e0dae79
Merge pull request #3645 from thelounge/xpaw/default-port
Switch default ports when toggling TLS
2020-01-02 10:46:38 +02:00
Pavel Djundik 5c8c854d18
Merge pull request #3651 from thelounge/xpaw/install-readme
Link to official docs for stable releases
2020-01-02 10:46:26 +02:00
Pavel Djundik 5921bb1ee1
Merge pull request #3650 from thelounge/xpaw/webpack-hints
Turn off webpack hints
2020-01-02 10:46:17 +02:00
Pavel Djundik c6f77f0668
Merge pull request #3656 from thelounge/xpaw/self-ctcp
Ignore echoed ctcp requests that aren't targeted at us
2020-01-02 10:46:03 +02:00
Pavel Djundik 27e08baf25
Merge pull request #3653 from thelounge/xpaw/utf8-mistake
Fix passing utf-8 to readFileSync
2020-01-02 10:45:52 +02:00
Pavel Djundik 05c69dfb6d
Merge pull request #3654 from thelounge/xpaw/fix-ua
Fix url in useragent when fetching releases from github
2020-01-02 10:45:44 +02:00
Pavel Djundik e9db7e5f82
Merge pull request #3657 from thelounge/renovate/got-10.x
Update dependency got to v10.2.1
2020-01-02 10:45:34 +02:00
Renovate Bot 8ba286a308
Update dependency got to v10.2.1 2020-01-01 18:33:56 +00:00
Pavel Djundik 7ef88523ca Ignore echoed ctcp requests that aren't targeted at us
Fixes #3655
2020-01-01 18:06:42 +02:00
Pavel Djundik 42ee21bfb8 Fix url in useragent when fetching releases from github 2020-01-01 01:15:45 +02:00
Pavel Djundik 0c246f0bbe Fix passing utf-8 to readFileSync 2020-01-01 01:11:04 +02:00
Pavel Djundik 84107ab516
Merge pull request #3644 from thelounge/renovate/nyc-15.x
Update dependency nyc to v15
2019-12-31 21:47:39 +02:00
Pavel Djundik 66302d25e0
Merge pull request #3640 from thelounge/renovate/eslint-plugin-vue-6.x
Update dependency eslint-plugin-vue to v6.1.2
2019-12-31 21:47:32 +02:00
Pavel Djundik 093ef2ff55
Merge pull request #3628 from thelounge/xpaw/prefetch-error
Collapse prefetch errors by default, and correctly track user toggle
2019-12-31 21:31:35 +02:00
Pavel Djundik a8e7cfd2cd
Merge pull request #3649 from Mikaela/network-command-helptext
NetworkForm.vue: clarify autoconnect command help text
2019-12-31 21:31:27 +02:00
Renovate Bot d288cbb625
Update dependency nyc to v15 2019-12-31 19:31:09 +00:00
Renovate Bot 242b068cdd
Update dependency eslint-plugin-vue to v6.1.2 2019-12-31 19:30:57 +00:00
Pavel Djundik 0a9157b935
Merge pull request #3643 from thelounge/renovate/babel-plugin-istanbul-6.x
Update dependency babel-plugin-istanbul to v6
2019-12-31 21:30:06 +02:00
Pavel Djundik 34939d9961
Merge pull request #3642 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.41.5
2019-12-31 21:29:57 +02:00
Pavel Djundik 6109bf3faa
Merge pull request #3624 from thelounge/renovate/sinon-8.x
Update dependency sinon to v8
2019-12-31 21:29:49 +02:00
Pavel Djundik be7f2c3c84
Merge pull request #3641 from thelounge/renovate/stylelint-12.x
Update dependency stylelint to v12.0.1
2019-12-31 21:29:29 +02:00
Pavel Djundik b52ae5414e
Merge pull request #3639 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.9.0
2019-12-31 21:29:20 +02:00
Pavel Djundik 53934bcbca
Merge pull request #3630 from thelounge/renovate/got-10.x
Update dependency got to v10.2.0
2019-12-31 21:29:04 +02:00
Pavel Djundik 9e84328748
Link to official docs for stable releases 2019-12-31 18:51:49 +02:00
Renovate Bot 9d3a5d4d86
Update dependency sinon to v8 2019-12-31 16:12:48 +00:00
Renovate Bot 29d188f927
Update dependency babel-plugin-istanbul to v6 2019-12-31 16:12:27 +00:00
Renovate Bot 6301a0012a
Update dependency webpack to v4.41.5 2019-12-31 16:12:16 +00:00
Renovate Bot 9568316cd0
Update dependency stylelint to v12.0.1 2019-12-31 16:12:06 +00:00
Renovate Bot 1ce6821585
Update dependency got to v10.2.0 2019-12-31 16:11:55 +00:00
Renovate Bot f056cce641
Update dependency eslint-config-prettier to v6.9.0 2019-12-31 16:11:33 +00:00
Pavel Djundik 5a3f17b647 v4.0.0 2019-12-31 18:10:30 +02:00
Pavel Djundik af1da708a3
Merge pull request #3625 from thelounge/changelog/v4.0.0
Add changelog for v4.0.0
2019-12-31 18:09:25 +02:00
Pavel Djundik 08871ef75a Update yarn.lock 2019-12-31 18:02:40 +02:00
Pavel Djundik 78bf87f29c Add changelog for v4.0.0 2019-12-31 18:02:26 +02:00
Pavel Djundik 8d17bbd6a2
Merge pull request #3632 from thelounge/xpaw/upgrade-cmd-check
Check if there are any packages installed in upgrade command
2019-12-31 17:55:38 +02:00
Pavel Djundik b1f5ba87cf
Merge pull request #3633 from thelounge/xpaw/viewer-position
Restrict image viewer bounds while moving
2019-12-30 19:29:43 +02:00
Pavel Djundik 280018e052
Merge pull request #3638 from thelounge/xpaw/control-space
Replace control codes with a space
2019-12-30 19:29:34 +02:00
Pavel Djundik 99175bef82 Check if there are any packages installed in upgrade command 2019-12-30 19:28:28 +02:00
Pavel Djundik 6e0ab062c5 Turn off webpack hints 2019-12-30 19:14:15 +02:00
Mikaela Suomalainen 17588560e6
NetworkForm.vue: clarify autoconnect command help text 2019-12-30 18:39:56 +02:00
Pavel Djundik 813b49d7b1 Make nicks in sets mode clickable 2019-12-30 12:10:21 +02:00
Pavel Djundik 30595ed23f Display the original sets mode string
Fixes #675
2019-12-30 12:06:54 +02:00
Pavel Djundik c055a07f45 Switch default ports when toggling TLS 2019-12-28 23:08:46 +02:00
Pavel Djundik a12a24adbe Replace control codes with a space 2019-12-27 20:39:28 +02:00
Pavel Djundik 56cc6d0b68
Merge pull request #3637 from thelounge/xpaw/node-ver
Lower node version to 10.15
2019-12-26 22:36:22 +02:00
Pavel Djundik e4a6aa3160
Lower node version to 10.15 2019-12-26 20:59:25 +02:00
Pavel Djundik 10932abb87
Merge pull request #3635 from thelounge/xpaw/webkit-tap
Set `-webkit-tap-highlight-color` to `transparent`
2019-12-26 19:30:52 +02:00
Pavel Djundik b18cb15f7d
Set -webkit-tap-highlight-color to transparent 2019-12-26 13:01:37 +02:00
Pavel Djundik dbfa5c5746 Restrict image viewer bounds while moving 2019-12-24 18:36:12 +02:00
Pavel Djundik 55e5c69958
Merge pull request #3627 from thelounge/xpaw/connect-after-defaults
Create networks after setting user defaults
2019-12-23 16:05:32 +02:00
Pavel Djundik d2932ccea8 Correctly track user toggle of previews 2019-12-23 12:26:57 +02:00
Pavel Djundik 769585e72d Collapse prefetch errors by default 2019-12-23 12:15:23 +02:00
Pavel Djundik fe031c8b12 Connect networks after setting user defaults 2019-12-23 10:27:12 +02:00
Pavel Djundik 27986f5811 Remove child-src from CSP, add base-uri none 2019-12-22 21:24:46 +02:00
Pavel Djundik 8696f03e8d v4.0.0-rc.1 2019-12-21 12:13:36 +02:00
Pavel Djundik d79b6e6c2f
Add changelog entry for v4.0.0-rc.1 2019-12-21 12:12:55 +02:00
Pavel Djundik 6c0fc8dbb4 Update yarn.lock 2019-12-21 12:12:36 +02:00
Pavel Djundik b808c89322
Merge pull request #3619 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.8.0
2019-12-21 11:56:44 +02:00
Pavel Djundik 7effbd35a7
Merge pull request #3620 from thelounge/renovate/mini-css-extract-plugin-0.x
Update dependency mini-css-extract-plugin to v0.9.0
2019-12-21 11:56:38 +02:00
Pavel Djundik 2a02f96017
Merge pull request #3618 from thelounge/renovate/dayjs-1.x
Update dependency dayjs to v1.8.18
2019-12-21 11:56:32 +02:00
Renovate Bot 40ea6e85ce
Update dependency mini-css-extract-plugin to v0.9.0 2019-12-21 09:28:05 +00:00
Renovate Bot a5b9773eac
Update dependency eslint to v6.8.0 2019-12-21 09:27:54 +00:00
Renovate Bot 92b2f6220d
Update dependency dayjs to v1.8.18 2019-12-21 09:27:42 +00:00
Pavel Djundik 5db6714718
Merge pull request #3623 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.41.4
2019-12-21 11:26:40 +02:00
Pavel Djundik 5f7f56d8ef
Merge pull request #3622 from thelounge/renovate/vue-loader-15.x
Update dependency vue-loader to v15.8.3
2019-12-21 11:26:33 +02:00
Pavel Djundik 0f90f6b7c2
Merge pull request #3621 from thelounge/renovate/textcomplete-0.x
Update dependency textcomplete to v0.18.1
2019-12-21 11:26:26 +02:00
Pavel Djundik 8f2eebf3e4
Merge pull request #3617 from thelounge/renovate/css-loader-3.x
Update dependency css-loader to v3.4.0
2019-12-21 11:26:20 +02:00
Pavel Djundik c71c9135ab
Merge pull request #3616 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.7.7
2019-12-21 11:26:13 +02:00
Renovate Bot e1e8dae02b
Update dependency webpack to v4.41.4 2019-12-21 03:31:01 +00:00
Renovate Bot fd9ed3335f
Update dependency vue-loader to v15.8.3 2019-12-21 03:30:46 +00:00
Renovate Bot c7338e9e11
Update dependency textcomplete to v0.18.1 2019-12-21 02:34:14 +00:00
Renovate Bot 44f25324ff
Update dependency css-loader to v3.4.0 2019-12-21 00:40:25 +00:00
Renovate Bot e9458f0a65
Update babel monorepo to v7.7.7 2019-12-21 00:40:11 +00:00
Pavel Djundik 146b6d02f4
Merge pull request #3612 from thelounge/renovate/file-type-12.x
Update dependency file-type to v12.4.2
2019-12-20 12:03:32 +02:00
Pavel Djundik 67aba10e34
Merge pull request #3611 from thelounge/renovate/ua-parser-js-0.x
Update dependency ua-parser-js to v0.7.21
2019-12-20 12:03:23 +02:00
Pavel Djundik 0ac698e0bb
Merge pull request #3614 from thelounge/xpaw/condense-tooltip
Update status messages tooltip
2019-12-20 12:03:14 +02:00
Pavel Djundik 25b65b39db
Merge pull request #3613 from thelounge/richrd/fix-lobby-draft-icon
Don't show draft icon on lobbies.
2019-12-20 12:00:29 +02:00
Pavel Djundik 7c5f4c404d Update status messages tooltip 2019-12-20 11:59:26 +02:00
Richard Lewis 18bfd32704 Don't show draft icon on lobbies. 2019-12-20 09:55:49 +00:00
Renovate Bot 74e8c7e51c
Update dependency file-type to v12.4.2 2019-12-20 00:31:02 +00:00
Renovate Bot c12e7bcebb
Update dependency ua-parser-js to v0.7.21 2019-12-19 18:17:50 +00:00
Pavel Djundik 356a896fe2
Merge pull request #3603 from thelounge/xpaw/condensed-more
Send 100 actual messages when requesting history with hidden or condensed status messages
2019-12-19 17:32:41 +02:00
Pavel Djundik bc6017aed7
Merge pull request #3534 from thelounge/richrd/issue-659-previous-source
Add `previous-source` class to messages with same sender
2019-12-19 17:32:34 +02:00
Pavel Djundik 126bf1794e
Merge pull request #3610 from thelounge/renovate/file-type-12.x
Update dependency file-type to v12.4.1
2019-12-19 17:32:27 +02:00
Richard Lewis 7a8bb0376c Add previous-source class to messages when previous message has same sender. 2019-12-19 15:04:31 +00:00
Renovate Bot 03dd00284c
Update dependency file-type to v12.4.1 2019-12-19 14:38:07 +00:00
Pavel Djundik 749e7f4469
Merge pull request #3609 from thelounge/xpaw/topic-edit
Remove querySelector in topic edit, fix save button style
2019-12-19 15:26:59 +02:00
Pavel Djundik 7a350ac69a
Merge pull request #3602 from thelounge/xpaw/ua
Pretend to be facebook and twitter bots in link prefetcher
2019-12-19 15:25:39 +02:00
Pavel Djundik c04beb8b08
Merge pull request #3606 from thelounge/xpaw/text-plain-preview
Add preview for text/plain urls
2019-12-19 15:25:22 +02:00
Pavel Djundik f1eee6c9b2
Merge pull request #3608 from thelounge/richrd/channel-list-item-classes
Relocate not-secure and not-connected classes and make sure channel exists in jumpToChannel
2019-12-19 15:25:15 +02:00
Richard Lewis d2f0590c73 Fix class targeting 2019-12-19 13:22:04 +00:00
Pavel Djundik 72a954b865 Add preview for text/plain urls 2019-12-19 15:12:02 +02:00
Pavel Djundik 60ca8850d9 Focus topic edit input by using $refs 2019-12-19 15:06:33 +02:00
Pavel Djundik 456cdb2f54 Fix save button style in topic edit 2019-12-19 15:05:49 +02:00
Richard Lewis d9f8f45169 Make sure channel exists. 2019-12-19 13:02:21 +00:00
Richard Lewis 8cb49ae56a Relocate not-secure and not-connected classes. 2019-12-19 13:00:24 +00:00
Pavel Djundik b16d023657
Merge pull request #3589 from thelounge/xpaw/user-fs
Optimize user file updates
2019-12-19 14:58:37 +02:00
Pavel Djundik cd6821a196
Merge pull request #3604 from thelounge/xpaw/beforeunload
Fix beforeunload not working
2019-12-19 14:57:44 +02:00
Pavel Djundik 117dd0faed
Merge pull request #3601 from thelounge/renovate/semver-7.x
Update dependency semver to v7.1.1
2019-12-19 14:50:27 +02:00
Pavel Djundik 0ee52e47e4
Merge pull request #3605 from thelounge/renovate/irc-framework-4.x
Update dependency irc-framework to v4.6.0
2019-12-19 14:50:19 +02:00
Renovate Bot 5da8d089c3
Update dependency semver to v7.1.1 2019-12-19 12:45:14 +00:00
Renovate Bot 6cc2471f4e
Update dependency irc-framework to v4.6.0 2019-12-19 12:45:00 +00:00
Pavel Djundik a414563eae
Merge pull request #3607 from thelounge/renovate/got-10.x
Update dependency got to v10.1.0
2019-12-19 14:44:28 +02:00
Renovate Bot 5611f98a4e
Update dependency got to v10.1.0 2019-12-19 12:33:05 +00:00
Pavel Djundik 03d5fab794 Fix beforeunload not working 2019-12-18 11:28:39 +02:00
Pavel Djundik 0d7b980f90 Remove unnecessary client.sockets ref 2019-12-18 11:22:11 +02:00
Pavel Djundik 6091514630 Do not write to disk if the json data hasn't actually changed 2019-12-18 11:06:20 +02:00
Pavel Djundik 2365c9489e Enforce user file types at runtime 2019-12-18 10:47:09 +02:00
Pavel Djundik f269ac3bee Update user file without reading, debounce all saves 2019-12-18 10:47:08 +02:00
Pavel Djundik def56dc694 Update user file once on auth 2019-12-18 10:47:08 +02:00
Pavel Djundik c1920eb566 When updating user file, write to temp file first 2019-12-18 10:47:07 +02:00
Pavel Djundik a9f97ddf22 Send 100 actual messages when requesting history with hidden or condensed status messages 2019-12-18 00:14:36 +02:00
Pavel Djundik 4a345eb6d9 Convert constants.js to commonjs 2019-12-18 00:14:36 +02:00
Pavel Djundik c108c20c91 Pretend to be facebook and twitter bots 2019-12-17 22:35:15 +02:00
Pavel Djundik 86341f063c
Merge pull request #3596 from thelounge/xpaw/friendly-size
Use `friendlysize` helper consistently
2019-12-17 15:43:58 +02:00
Pavel Djundik f1d806a80f
Merge pull request #3600 from thelounge/xpaw/condensed-set
Use Set() for condensed types
2019-12-17 15:43:50 +02:00
Pavel Djundik e2c74a1014
Merge pull request #3598 from thelounge/renovate/semver-7.x
Update dependency semver to v7.1.0
2019-12-17 15:43:41 +02:00
Renovate Bot 0ba11d8a0e
Update dependency semver to v7.1.0 2019-12-17 12:21:53 +00:00
Pavel Djundik c6b568c165
Merge pull request #3599 from thelounge/xpaw/fix-keys
Fix page and arrow keys not working correctly
2019-12-17 14:09:59 +02:00
Pavel Djundik f3b383ce63 Use Set() for condensed types 2019-12-17 12:48:12 +02:00
Pavel Djundik 408eb75a88 Fix page and arrow keys not working correctly 2019-12-17 11:21:22 +02:00
Pavel Djundik f2bf1fa90a Use friendlysize helper consistently
Co-Authored-By: fnutt <fnutt@users.noreply.github.com>
2019-12-16 22:00:35 +02:00
Pavel Djundik f0b0c53536
Merge pull request #3597 from bepvte/fix-ipv6
Fix format of IPv6 URI
2019-12-16 20:33:22 +02:00
Paul Oppenheimer dcf08ecac6 Fix format of IPv6 URI 2019-12-16 12:24:30 -05:00
Pavel Djundik 8fb8b94650
Merge pull request #3593 from thelounge/xpaw/fix-keepnick
Fix keep nick setting nick to undefined on socket close
2019-12-16 11:39:33 +02:00
Pavel Djundik a8dd85d21e Fix keep nick setting nick to undefined on socket close 2019-12-16 10:56:25 +02:00
Pavel Djundik 6a920fd4eb
Merge pull request #3587 from thelounge/renovate/ldapjs-2.x
Update dependency ldapjs to v2.0.0-pre.5
2019-12-15 19:11:41 +02:00
Pavel Djundik 61369b3e5a
Merge pull request #3586 from thelounge/xpaw/msg-data-type
Use data-type attribute on .msg
2019-12-15 19:10:30 +02:00
Pavel Djundik 98708a2ebd
Merge pull request #3588 from thelounge/xpaw/hide-settings
Hide awaymessage/highlights settings in public mode
2019-12-15 19:10:21 +02:00
Pavel Djundik 5b55ac7d02
Merge pull request #3590 from thelounge/xpaw/fix-sync
Fix synchronizing settings on page load
2019-12-15 19:10:15 +02:00
Pavel Djundik 52ce1aebbd
Merge pull request #3591 from thelounge/xpaw/fix-width
Fix sign in being full width
2019-12-15 19:10:07 +02:00
Pavel Djundik dc93bc0f1e
Merge pull request #3592 from thelounge/xpaw/fix-image-blur
Round down image transform in image viewer to fix blurry images
2019-12-15 19:10:00 +02:00
Pavel Djundik 935b193a64 Round down image transform in image viewer to fix blurry images 2019-12-15 18:13:52 +02:00
Pavel Djundik 5b4a5fd4b1 Fix sign in being full width 2019-12-15 18:06:20 +02:00
Pavel Djundik 309be48906 Fix synchronizing settings on page load 2019-12-15 18:03:13 +02:00
Pavel Djundik 317f4fb991 Hide awaymessage/highlights settings in public mode 2019-12-15 17:31:03 +02:00
Renovate Bot f806c32c49
Update dependency ldapjs to v2.0.0-pre.5 2019-12-15 14:27:28 +00:00
Pavel Djundik 6731e584da Use data-type on .msg 2019-12-15 13:46:43 +02:00
Pavel Djundik 58a558247b
Merge pull request #3454 from Zarthus/gh_labels
Automatically apply GitHub labels on issue creation
2019-12-15 13:29:03 +02:00
Pavel Djundik 578f5fa1c9 v4.0.0-pre.1 2019-12-14 22:49:12 +02:00
Pavel Djundik ebbe798c16
Add changelog entry for v4.0.0-pre.1 2019-12-14 22:48:44 +02:00
Pavel Djundik 0486f43f9f
Merge pull request #3557 from thelounge/xpaw/premature-close
Fix "premature close" on link previews
2019-12-14 22:45:49 +02:00
Pavel Djundik bfffc8d0df
Merge pull request #3584 from thelounge/renovate/semver-7.x
Update dependency semver to v7
2019-12-14 22:37:59 +02:00
Pavel Djundik ead372e6e6
Merge pull request #3574 from thelounge/xpaw/normalize.css
Remove bootstrap.css, use flexbox
2019-12-14 22:37:37 +02:00
Renovate Bot a59af9b941
Update dependency semver to v7 2019-12-14 20:34:20 +00:00
Pavel Djundik 05af830a15 Remove experimental warning from sync 2019-12-14 22:30:34 +02:00
Pavel Djundik f00c71c81b Use v-show to hide load more button 2019-12-14 22:30:34 +02:00
Pavel Djundik 1495ce3772 Remove bootstrap classes from settings 2019-12-14 22:30:34 +02:00
Pavel Djundik 0e9fdf9e08 Remove float from changelog and version link 2019-12-14 22:29:50 +02:00
Pavel Djundik b592657f7d Style connect window without bootstrap 2019-12-14 22:29:50 +02:00
Pavel Djundik 0e8b9fdd5c Use normalize.css and remove a lot of unused styles from bootstrap.css 2019-12-14 22:29:49 +02:00
Pavel Djundik c23c786c58
Merge pull request #3585 from thelounge/xpaw/fix-preview-settings
Fix link previews settings not being visible
2019-12-14 22:11:08 +02:00
Pavel Djundik e8ed36bfd6 Fix link previews settings not being visible 2019-12-14 21:53:13 +02:00
Pavel Djundik 6f7444dfe3
Merge pull request #3577 from thelounge/xpaw/thumb-assign
Assign `preview.thumb` only after it is processed
2019-12-14 21:34:07 +02:00
Pavel Djundik d99b6d0a17
Merge pull request #3579 from thelounge/xpaw/watch-packages
Automatically load new packages (fs.watch package.json)
2019-12-14 21:33:56 +02:00
Pavel Djundik e3a2fa7dd1 Create packages/package.json on server start 2019-12-14 20:48:25 +02:00
Pavel Djundik 7fbba14b69 Watch package.json and load new packages 2019-12-14 20:47:52 +02:00
Pavel Djundik 059cedcf7a
Merge pull request #3578 from thelounge/xpaw/yarn-home
Provide fake $HOME env to Yarn commands
2019-12-14 20:47:09 +02:00
Pavel Djundik 8840a80209
Merge pull request #3583 from thelounge/renovate/vue-monorepo
Update vue monorepo to v2.6.11
2019-12-14 10:59:05 +02:00
Pavel Djundik eda437f40d Update yarn.lock 2019-12-14 10:53:36 +02:00
Renovate Bot a0f684c0d9
Update vue monorepo to v2.6.11 2019-12-14 08:44:37 +00:00
Pavel Djundik bc3e6292b6
Merge pull request #3582 from thelounge/renovate/css-loader-3.x
Update dependency css-loader to v3.3.2
2019-12-14 10:43:48 +02:00
Pavel Djundik a91f5dfb49
Merge pull request #3580 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.12.0
2019-12-14 10:43:41 +02:00
Renovate Bot dccdd4869c
Update dependency css-loader to v3.3.2 2019-12-14 08:31:31 +00:00
Renovate Bot 9ac1257e76
Update dependency @fortawesome/fontawesome-free to v5.12.0 2019-12-14 08:31:20 +00:00
Pavel Djundik d550c6e11c
Merge pull request #3581 from thelounge/renovate/copy-webpack-plugin-5.x
Update dependency copy-webpack-plugin to v5.1.1
2019-12-14 10:30:10 +02:00
Pavel Djundik 0582303f3b
Merge pull request #3575 from thelounge/renovate/got-10.x
Update dependency got to v10.0.4
2019-12-14 10:30:03 +02:00
Renovate Bot 1ede6c8463
Update dependency copy-webpack-plugin to v5.1.1 2019-12-14 00:35:30 +00:00
Pavel Djundik 24e41327a3 Provide fake $HOME env to Yarn commands 2019-12-13 17:45:10 +02:00
Pavel Djundik bbf92f1aa0 Assign preview.thumb only after it is processed 2019-12-13 11:43:13 +02:00
Renovate Bot 15d7f2f224
Update dependency got to v10.0.4 2019-12-12 13:15:23 +00:00
Pavel Djundik 51360711c9
Merge pull request #3573 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.21.1
2019-12-11 19:36:25 +02:00
Pavel Djundik 87244fb4d5
Merge pull request #3570 from thelounge/xpaw/fix-3568
Allow empty parameter overrides
2019-12-11 19:34:04 +02:00
Pavel Djundik 0e3d7bb5bd
Merge pull request #3571 from thelounge/xpaw/fix-3569
Disable protocol register button if lockNetwork is enabled
2019-12-11 19:33:56 +02:00
Renovate Bot 53a34a0509
Update dependency yarn to v1.21.1 2019-12-11 13:14:18 +00:00
Pavel Djundik 7eaf6fb58f
Merge pull request #3572 from thelounge/xpaw/css
Remove unnecessary selectors
2019-12-11 14:33:27 +02:00
Pavel Djundik f5103ac4b4 Remove unnecessary selectors 2019-12-11 13:35:18 +02:00
Pavel Djundik 2bc78e24a2
Merge pull request #3566 from thelounge/xpaw/css
Fix up css refactor
2019-12-11 11:43:45 +02:00
Pavel Djundik 74cc1722ea Disable protocol register button if lockNetwork is enabled
Fixes #3569
2019-12-10 23:24:54 +02:00
Pavel Djundik 58545353f7 Allow empty parameter overrides
Fixes #3568
2019-12-10 23:20:45 +02:00
Pavel Djundik fd6bc3ecb6 Fix up css refactoring 2019-12-10 19:25:32 +02:00
Pavel Djundik 2a84d8239b
Merge pull request #3553 from thelounge/richrd/css-refactoring
Refactor some CSS styling
2019-12-10 14:52:18 +02:00
Richard Lewis c022377c49 Refactor some CSS selectors. 2019-12-10 14:19:31 +02:00
Pavel Djundik e9cbea9569 Update link prefetch stream handling
Fixes #3564
2019-12-09 21:05:33 +02:00
Pavel Djundik 8d227ee37e
Merge pull request #3560 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.13
2019-12-09 21:03:00 +02:00
Pavel Djundik 2d983b94eb
Merge pull request #3565 from thelounge/renovate/babel-monorepo
Update dependency @babel/preset-env to v7.7.6
2019-12-09 21:02:53 +02:00
Pavel Djundik 371f676fd5
Merge pull request #3563 from thelounge/renovate/got-10.x
Update dependency got to v10.0.3
2019-12-09 21:02:28 +02:00
Renovate Bot f7391f252b
Update dependency got to v10.0.3 2019-12-09 18:48:39 +00:00
Renovate Bot 636c4a6204
Update dependency @babel/preset-env to v7.7.6 2019-12-07 23:52:23 +00:00
Renovate Bot 3426ee31c0
Update dependency mochapack to v1.1.13 2019-12-07 18:10:04 +00:00
Pavel Djundik cf0a222cf9
Merge pull request #3562 from thelounge/renovate/ldapjs-2.x
Update dependency ldapjs to v2.0.0-pre.4
2019-12-07 20:09:07 +02:00
Pavel Djundik bfbb6627d0
Merge pull request #3559 from thelounge/renovate/css-loader-3.x
Update dependency css-loader to v3.2.1
2019-12-07 20:08:58 +02:00
Pavel Djundik a9f0b1d5ff
Merge pull request #3558 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.7.5
2019-12-07 20:08:51 +02:00
Renovate Bot 1501566824
Update dependency ldapjs to v2.0.0-pre.4 2019-12-07 17:43:17 +00:00
Renovate Bot 4ad7dc1ad3
Update dependency css-loader to v3.2.1 2019-12-07 17:43:06 +00:00
Renovate Bot bdfbfdd475
Update babel monorepo to v7.7.5 2019-12-07 17:42:54 +00:00
Pavel Djundik 21bbfffb21
Merge pull request #3561 from thelounge/richrd/fix-video-overflow
Fix video element overflowing on chrome
2019-12-07 19:13:27 +02:00
Pavel Djundik 320832dfd9
Merge pull request #3539 from thelounge/xpaw/emoji
Add Unicode 12.1 emojis
2019-12-07 19:12:33 +02:00
Richard Lewis 45d7b0531a Fix video element overflowing on chrome. 2019-12-07 14:11:16 +00:00
Pavel Djundik 6032bd16a5
Merge pull request #3556 from thelounge/renovate/web-push-3.x
Update dependency web-push to v3.4.3
2019-12-06 21:55:19 +02:00
Renovate Bot 21c8e7cd62
Update dependency web-push to v3.4.3 2019-12-06 19:50:26 +00:00
Pavel Djundik 3224c988b8
Merge pull request #3555 from thelounge/xpaw/apply-theme
Apply user theme as soon as possible on page load
2019-12-06 17:56:06 +02:00
Pavel Djundik e64f53ad33 Apply user theme as soon as possible 2019-12-06 11:56:12 +02:00
Pavel Djundik 07ea17b180
Merge pull request #3548 from thelounge/xpaw/remove-upgrades
Remove code that aided upgrade to v3
2019-12-05 12:14:08 +02:00
Pavel Djundik 278595df1f
Merge pull request #3554 from thelounge/renovate/sqlite3-4.x
Update dependency sqlite3 to v4.1.1
2019-12-05 12:13:36 +02:00
Renovate Bot e60a8e8bff
Update dependency sqlite3 to v4.1.1 2019-12-05 02:45:53 +00:00
Pavel Djundik f2ea562d16
Merge pull request #3552 from thelounge/renovate/tlds-1.x
Update dependency tlds to v1.207.0
2019-12-03 19:31:39 +02:00
Renovate Bot 0bd676355e
Update dependency tlds to v1.207.0 2019-12-03 15:19:35 +00:00
Pavel Djundik c260e1a82f
Merge pull request #3549 from thelounge/xpaw/client-setting-awayMessage
Make client awayMessage a client setting
2019-12-03 10:30:21 +02:00
Pavel Djundik 4ef5c66fd6
Merge pull request #3546 from thelounge/xpaw/fix-sidebar-closing
Fix sidebar not opening when lounge is open in a background tab
2019-12-03 10:23:59 +02:00
Pavel Djundik 3dae767937 Make client awayMessage a client setting 2019-12-02 12:24:22 +02:00
Pavel Djundik db4b292a38 Remove code that aided upgrade to v3 2019-12-02 12:10:17 +02:00
Pavel Djundik d90a81240f
Merge pull request #3544 from thelounge/renovate/got-10.x
Update dependency got to v10
2019-12-02 11:56:48 +02:00
Pavel Djundik ad39229867
Merge pull request #3547 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.21.0
2019-12-02 11:56:39 +02:00
Renovate Bot 6199c3defc
Update dependency got to v10 2019-12-02 09:41:11 +00:00
Renovate Bot f945c29cda
Update dependency yarn to v1.21.0 2019-12-02 08:29:20 +00:00
Pavel Djundik cf0a4999e9 Fix sidebar not opening when lounge is open in a background tab 2019-12-01 20:20:37 +02:00
Pavel Djundik 674d9cfbd8
Merge pull request #3543 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.7.2
2019-12-01 17:42:39 +02:00
Pavel Djundik dd2b15b7af
Merge pull request #3545 from thelounge/renovate/web-push-3.x
Update dependency web-push to v3.4.2
2019-12-01 17:42:19 +02:00
Renovate Bot 5d1eb385e6
Update dependency web-push to v3.4.2 2019-12-01 15:34:32 +00:00
Renovate Bot 801f56b168
Update dependency eslint to v6.7.2 2019-11-30 17:24:52 +00:00
Pavel Djundik 7425033fdb
Merge pull request #3542 from thelounge/renovate/vue-monorepo
Update vue monorepo to v1.0.0-beta.30
2019-11-30 12:10:08 +02:00
Pavel Djundik 36b105021b Use async in parser tests 2019-11-30 11:36:19 +02:00
Renovate Bot 6b46097479
Update vue monorepo to v1.0.0-beta.30 2019-11-30 09:07:40 +00:00
Pavel Djundik f6a432da32
Merge pull request #3541 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.12
2019-11-30 11:06:42 +02:00
Pavel Djundik 69840cd8c1
Merge pull request #3540 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.7.1
2019-11-30 11:06:35 +02:00
Renovate Bot 661d4a9ba4
Update dependency mochapack to v1.1.12 2019-11-30 01:27:53 +00:00
Renovate Bot 1d8bd7acb5
Update dependency eslint to v6.7.1 2019-11-30 01:27:39 +00:00
Pavel Djundik 09ddbd156c Add Unicode 12.1 emojis 2019-11-29 20:26:33 +02:00
Pavel Djundik 320b3ea98f
Merge pull request #3434 from thelounge/xpaw/no-variant-emoji-map
Remove \uFE0F emoji variant from emoji name map
2019-11-28 20:15:42 +02:00
Pavel Djundik 6d342b9847
Merge pull request #3538 from thelounge/xpaw/test-timeout
Increase test timeout due to unpredictable I/O on CI services
2019-11-28 12:57:41 +02:00
Pavel Djundik f0dfb909dd
Merge pull request #3537 from thelounge/xpaw/fix-kick-spacing
Fix spacing in kick reason
2019-11-28 12:24:17 +02:00
Pavel Djundik 45f2576e96
Merge pull request #3536 from thelounge/xpaw/no-compute-filtered
Do not compute filteredUsers if there's no search input
2019-11-28 12:19:31 +02:00
Pavel Djundik 8b7fb33627 Increase test timeout due to unpredictable I/O on CI services 2019-11-27 20:25:29 +02:00
Pavel Djundik e923696bb0 Fix spacing in kick reason 2019-11-27 20:18:20 +02:00
Pavel Djundik c19cbd7ffd Do not compute filteredUsers if there's no search input 2019-11-27 19:56:21 +02:00
Pavel Djundik 446f99f62a
Merge pull request #3535 from thelounge/richrd/fix-keybinds-in-input
Fix keybinds when chat input is focused.
2019-11-27 16:49:04 +02:00
Richard Lewis b089b92b1e Fix keybinds when chat input is focused. 2019-11-27 14:28:54 +00:00
Pavel Djundik 2ee30abd56
Merge pull request #3533 from thelounge/richrd/draft-icon
Show which channels have drafts in the network list
2019-11-27 10:59:49 +02:00
Richard Lewis eb0094618e Show pen icon if channel has a pending message, unless it's the active channel 2019-11-26 22:29:35 +00:00
Pavel Djundik bbbaf128bb
Merge pull request #3521 from thelounge/github-action-release
Create release github action workflow
2019-11-26 22:45:19 +02:00
Pavel Djundik c0b8f6f86a Create release github action workflow 2019-11-26 22:39:38 +02:00
Pavel Djundik c813d6ee2a Delete .travis.yml 2019-11-26 22:39:38 +02:00
Pavel Djundik 85400ed9c2
Merge pull request #3532 from thelounge/xpaw/context-menu-transition
Remove transition from context menu items
2019-11-26 18:02:17 +02:00
Pavel Djundik 5f2651a252
Merge pull request #3531 from thelounge/xpaw/ignore-unknown-settings
Ignore unknown settings
2019-11-26 18:02:09 +02:00
Pavel Djundik fa68d74f9e Remove transition from context menu items 2019-11-26 16:39:56 +02:00
Pavel Djundik c790d9fadf Ignore unknown settings 2019-11-26 16:20:33 +02:00
Pavel Djundik d6923d0c6d Regenerate fullnamemap.json 2019-11-26 14:17:37 +02:00
Pavel Djundik 10b1cedbb6 Remove \uFE0F emoji variant from emoji name map 2019-11-26 14:17:37 +02:00
Pavel Djundik 12cdf280fc v3.4.0-pre.1 2019-11-26 13:57:07 +02:00
Pavel Djundik 9784423808 Add changelog entry for v3.4.0-pre.1 2019-11-26 13:56:56 +02:00
Pavel Djundik e74c35687e
Merge pull request #3524 from thelounge/vue
Complete porting The Lounge client to the Vue.js framework
2019-11-26 13:51:45 +02:00
Pavel Djundik a3be259567 Fix opening channel when clicking a push notification 2019-11-25 21:51:04 +02:00
Pavel Djundik c2ed3fae56 Improve link preview loading 2019-11-25 21:37:51 +02:00
Pavel Djundik c70d0fb224 Display a badge when built in development mode 2019-11-25 20:53:22 +02:00
Pavel Djundik 9051861f4d Replace history entry if current route is null 2019-11-25 20:13:20 +02:00
Pavel Djundik 049e9a1680 Prevent cursor moving when navigating user list 2019-11-25 20:13:20 +02:00
Pavel Djundik 57ba119edb Hide auto completion menu when channel changes 2019-11-25 20:13:19 +02:00
Pavel Djundik 83f3fe772a Remove user/pass support from irc://, support multiple channels
Other clients and specs explicitly don't support user:pass
2019-11-25 20:13:19 +02:00
Pavel Djundik ec85372132 Fix uri handling and add tests 2019-11-25 20:13:18 +02:00
Pavel Djundik 90ec37ce82 Replace confirm() with context menu
window.confirm() blocks the javascript thread and will cause the socket connection to drop
2019-11-25 20:13:18 +02:00
Pavel Djundik 9b9c547e8c Remove UsernameFiltered and fix colored mentions 2019-11-25 20:13:17 +02:00
Richard Lewis dca6543070 Implement closeChannel method. 2019-11-25 20:13:17 +02:00
Richard Lewis 0c49f025b4 Fix Vue error when navigating to channels via InlineChannel. 2019-11-25 20:13:17 +02:00
Pavel Djundik 2a6c57abaa Fix context menu generation 2019-11-25 20:13:16 +02:00
Pavel Djundik de76a86757 Remove css.escape as it is no longer used 2019-11-25 20:13:16 +02:00
Pavel Djundik 49dc6ffd8f Fix client tests 2019-11-25 20:13:15 +02:00
Pavel Djundik 0ac9601a3a Remove some data attributes 2019-11-25 20:13:15 +02:00
Pavel Djundik e76d5d2ef9 Port keybinds to vue state; remove jQuery 2019-11-25 20:13:14 +02:00
Pavel Djundik d0444d7d7f Improve disconnected message in public mode 2019-11-25 20:13:14 +02:00
Pavel Djundik f00dfc7524 Move upload-overlay to viewport 2019-11-25 20:13:14 +02:00
Pavel Djundik 21bbe7d4c3 Make sense out of settings sync and force sync 2019-11-25 20:13:13 +02:00
Pavel Djundik 85907f54ba Improve context menus 2019-11-25 20:13:13 +02:00
Pavel Djundik 9147772cb2 Use mousetrap for escape binds 2019-11-25 20:13:12 +02:00
Pavel Djundik 0cb8dc73bb Use es6 import syntax 2019-11-25 20:13:12 +02:00
Pavel Djundik b2cc8d9531 Fix web app install button 2019-11-25 20:13:11 +02:00
Pavel Djundik fcf7488e1e Remove jquery from autocompletion 2019-11-25 20:13:11 +02:00
Richard Lewis a71472a427 Port contextmenus to Vue 2019-11-25 20:13:11 +02:00
Pavel Djundik 111c3665f9 Replace moment with dayjs
Drop in replacement, but smaller
2019-11-25 20:13:10 +02:00
Pavel Djundik 7584f47c7d Cleanup webpush code 2019-11-25 20:13:10 +02:00
Pavel Djundik 17365d9967 Remove references to vue.js 2019-11-25 20:13:09 +02:00
Pavel Djundik 54a1e11f50 Move some init code around 2019-11-25 20:13:09 +02:00
Pavel Djundik 033f565c0e Remove isFileUploadEnabled 2019-11-25 20:13:08 +02:00
Pavel Djundik a4490bf1d6 Fix up connect uri parsing, use direct router references
Co-Authored-By: Tim Miller-Williams <timmw@users.noreply.github.com>
2019-11-25 20:13:08 +02:00
Pavel Djundik 91e0349486 Use global Vue calls instead of vueApp reference 2019-11-25 20:13:07 +02:00
Pavel Djundik f2309c7c89 Improve router experience 2019-11-25 20:13:07 +02:00
Pavel Djundik 5a0f1c1f4e Replace getActiveWindowComponent with an event 2019-11-25 20:13:07 +02:00
Pavel Djundik 3a6b075745 Do not focus channel when close button clicked
This fixes leaving channels and removing networks jumping to it before being removed
2019-11-25 20:13:06 +02:00
Pavel Djundik 2044bc88dd Switch channels on quit only if current network is being quit 2019-11-25 20:13:06 +02:00
Tim Miller-Williams d5ebdc943c Replace isNotified state with getter 2019-11-25 20:13:05 +02:00
Tim Miller-Williams cbaf4db339 Replace synchronizeNotifiedState with a getter & watcher 2019-11-25 20:13:05 +02:00
Tim Miller-Williams 16f8304c4e Refactor title to rely on Vuex state reactivity 2019-11-25 20:13:04 +02:00
Tim Miller-Williams 6a15fd95f0 Refactor userList behaviour to not use methods in root Vue instance 2019-11-25 20:13:04 +02:00
Tim Miller-Williams dd9efad23c Refactor sidebar behaviour to no longer use methods in root Vue instance 2019-11-25 20:13:04 +02:00
Tim Miller-Williams 1adbbdda2a Fix bug with joining new channels 2019-11-25 20:13:03 +02:00
Tim Miller-Williams 347802a4b6 Refactor Apple keyboard logic to be more explicit 2019-11-25 20:13:03 +02:00
Richard Lewis 94bdff4fa0 Implement mirroring nick to username field in vue. 2019-11-25 20:13:02 +02:00
Richard Lewis 0c7db6dffe Move url parameter handling to vue 2019-11-25 20:13:02 +02:00
Richard Lewis 897f238c38 Disallow navigating to invalid channels 2019-11-25 20:13:01 +02:00
Richard Lewis 5c0a7722a4 Disallow navigating to non-existing routes 2019-11-25 20:13:01 +02:00
Richard Lewis d232ef1557 Improve vue routing. 2019-11-25 20:13:00 +02:00
Pavel Djundik 916da73108 Remove jquery from input focus event 2019-11-25 20:13:00 +02:00
Pavel Djundik 80c6cfbd7c Use switchToChannel instead of click 2019-11-25 20:13:00 +02:00
Tim Miller-Williams 25da9dd63e Rework settings such that all behavior for each setting is kept together
Behavior includes: default value, whether setting should be synced, and
an optional 'apply' callback which is called when setting is changed in
Vuex.
2019-11-25 20:12:59 +02:00
Tim Miller-Williams 703848919c Separate connection event handlers from socket.js 2019-11-25 20:12:59 +02:00
Tim Miller-Williams a2a2aff2bc Remove unnecessary options.initialized switch 2019-11-25 20:12:58 +02:00
Pavel Djundik a1f183f216 Cleanup auth flow 2019-11-25 20:12:58 +02:00
Pavel Djundik fc1c9568e2 Rename helpers folder, move some vue filters 2019-11-25 20:12:58 +02:00
Pavel Djundik b164e95290 Remove DOM access from webpush 2019-11-25 20:12:57 +02:00
Pavel Djundik 8972242863 Remove jquery from msg event 2019-11-25 20:12:57 +02:00
Pavel Djundik 6b8fea8afc Avoid emitting multiple events to the server when collapsing/expanding channel previews
Fixes #1377
2019-11-25 20:12:56 +02:00
Pavel Djundik c26de4cf6a Move options to vuex
Co-Authored-By: Tim Miller-Williams <timmw@users.noreply.github.com>
2019-11-25 20:12:56 +02:00
Pavel Djundik 743ae987ec Fix up login and initial window 2019-11-25 20:12:55 +02:00
Pavel Djundik 2b5a13a043 Register routes after init 2019-11-25 20:12:55 +02:00
Pavel Djundik aba2487126 Make findChannel and findNetwork getters 2019-11-25 20:12:54 +02:00
Pavel Djundik 742cd8d4bf Move most things out of utils 2019-11-25 20:12:54 +02:00
Pavel Djundik 2f635069e0 Move vuex state to a separate file and reorganize some code
Co-Authored-By: Tim Miller-Williams <timmw@users.noreply.github.com>
2019-11-25 20:12:54 +02:00
Pavel Djundik 3c43a2bfd3 Delete renderPreview.js 2019-11-25 20:12:53 +02:00
Pavel Djundik c4d6afe3d6 Fix removing networks 2019-11-25 20:12:53 +02:00
Pavel Djundik c8b22b2df3 Fix up network editing 2019-11-25 20:12:52 +02:00
Richard Lewis 8fa42c5c48 Fix network editing in vue and use absolute urls in router links. 2019-11-25 20:12:52 +02:00
Richard Lewis 2049a16d64 Implement switchToChannel method. 2019-11-25 20:12:51 +02:00
Pavel Djundik e845e17a63 Convert some clicks to router push 2019-11-25 20:12:51 +02:00
Richard Lewis c6dca616e6 Remove jQuery from InlineChannel.vue and JoinChannel.vue. 2019-11-25 20:12:50 +02:00
Richard Lewis c393dd1a11 Fixes to vue routing and activeWindow. 2019-11-25 20:12:50 +02:00
Pavel Djundik f76ad57c63 Move confirmExit 2019-11-25 20:12:50 +02:00
Pavel Djundik b74cc4387a Remove old path from eslintignore 2019-11-25 20:12:49 +02:00
Pavel Djundik 431221c21e Add rel=noopener on changelog links 2019-11-25 20:12:49 +02:00
Richard Lewis 737afc759b Implement vue-router. 2019-11-25 20:12:48 +02:00
Pavel Djundik 7355c91839 Move context menu events to factory 2019-11-25 20:12:48 +02:00
Pavel Djundik af0d48de72 Create InlineChannel component 2019-11-25 20:12:48 +02:00
Pavel Djundik 4f6565c24a Add active class to footer buttons 2019-11-25 20:12:47 +02:00
Pavel Djundik 5c4b402341 Fancy image interactions in the image viewer
Desktop:
- Mousewheel to zoom in/out (hold ctrl to move up/down)
- If zoomed, drag around with mouse to move

Mobile:
- Move around with one finger
- Change zoom with two fingers
2019-11-25 20:12:47 +02:00
Pavel Djundik af777106bf Remove handlebars and html-minifier 2019-11-25 20:12:46 +02:00
Pavel Djundik 70a795dced Start porting image viewer to Vue 2019-11-25 20:12:33 +02:00
Richard Lewis 2d8417cd8b Patch changelog html in Vue. 2019-11-25 20:12:33 +02:00
Pavel Djundik cd36555b63 Import socket in changelog 2019-11-25 20:12:32 +02:00
Richard Lewis ef500f12a1 Implement changelog in Vue. 2019-11-25 20:12:32 +02:00
Richard Lewis 055ba5caff Remove sidebar wrapper div. 2019-11-25 20:12:31 +02:00
Richard Lewis b95f89c4c2 Implement version checker in Vue. 2019-11-25 20:12:31 +02:00
Richard Lewis 2b602ca333 Move slideout menu logic to Vue. 2019-11-25 20:12:31 +02:00
Richard Lewis ee92de0ff7 Fix changing theme color and properly sync settings. 2019-11-25 20:12:30 +02:00
Richard Lewis b5f2e7f0cc Fix lint and format with prettier. 2019-11-25 20:12:30 +02:00
Richard Lewis e0ec340de8 Fix oversights during rebase. 2019-11-25 20:12:29 +02:00
Richard Lewis b994ecd1f1 Fix hash navigation for sidebar footer buttons. 2019-11-25 20:12:29 +02:00
Richard Lewis addd4124bf Close sidebar when opening settings, help etc. 2019-11-25 20:12:29 +02:00
Richard Lewis 7fd48d8155 Fix enabling and disabling push notifications. 2019-11-25 20:12:28 +02:00
Richard Lewis 467ebab31f Move most side bar and user list interactions to vue. 2019-11-25 20:12:28 +02:00
Richard Lewis e73bf1e9a7 Move closeChan functionality to vue. 2019-11-25 20:12:27 +02:00
Richard Lewis 5b17a2fbe4 Port session list to vue. 2019-11-25 20:12:27 +02:00
Richard Lewis 111beb5f12 Run updateSetting from Settings component and get rid of unused code. 2019-11-25 20:12:26 +02:00
Richard Lewis 2ef3e3e5b4 Add success: false to change-password error emits. 2019-11-25 20:12:26 +02:00
Richard Lewis 6c10a2a6cf Port part of the settings functionality to vue. 2019-11-25 20:12:26 +02:00
Richard Lewis 08635beb61 Fix lint 2019-11-25 20:12:25 +02:00
Richard Lewis 5a3ad194e8 Move connect and network edit views to vue. 2019-11-25 20:12:25 +02:00
Richard Lewis c4a3108dc0 Move activeWindow to vuex. 2019-11-25 20:12:24 +02:00
Richard Lewis 0da059118d Move isNotified to vuex. 2019-11-25 20:12:24 +02:00
Richard Lewis 69cb891b1a Add vuex and move isConnected to vuex state. 2019-11-25 20:12:23 +02:00
Richard Lewis e71360ad39 Move sidebar to its own component. 2019-11-25 20:12:23 +02:00
Pavel Djundik 3f7889e534 Move changelog window to Vue 2019-11-25 20:12:22 +02:00
Pavel Djundik 70d9d8d226 Move help window to Vue 2019-11-25 20:12:22 +02:00
Pavel Djundik 71f54f6a5d Move some settings to Vue 2019-11-25 20:12:22 +02:00
Pavel Djundik 09e12affe8 Begin moving windows to Vue 2019-11-25 20:12:21 +02:00
Pavel Djundik bdb0a2efca
Merge pull request #3530 from thelounge/xpaw/cheerio
Bump cheerio to latest version
2019-11-25 20:11:51 +02:00
Pavel Djundik 98f75a5a1c Upgrade yarn.lock 2019-11-25 18:08:46 +02:00
Pavel Djundik bd2a6be257 Bump cheerio to latest version 2019-11-25 17:58:04 +02:00
Pavel Djundik 72b0edabf9
Merge pull request #3528 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.7.0
2019-11-23 16:27:54 +02:00
Renovate Bot 9fe218a625
Update dependency eslint-config-prettier to v6.7.0 2019-11-23 10:41:41 +00:00
Pavel Djundik f614ebd712
Merge pull request #3529 from thelounge/renovate/husky-3.x
Update dependency husky to v3.1.0
2019-11-23 12:40:51 +02:00
Pavel Djundik 1a53635d1a
Merge pull request #3527 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.7.0
2019-11-23 12:40:46 +02:00
Pavel Djundik 83648c0571
Merge pull request #3526 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.7.4
2019-11-23 12:40:37 +02:00
Pavel Djundik 8e1ce206e1
Merge pull request #3525 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.19.2
2019-11-23 12:40:29 +02:00
Renovate Bot 3c0754e6df
Update dependency husky to v3.1.0 2019-11-23 04:52:50 +00:00
Renovate Bot 1b47d0fe90
Update dependency eslint to v6.7.0 2019-11-23 02:51:29 +00:00
Renovate Bot b90db81025
Update babel monorepo to v7.7.4 2019-11-23 02:51:15 +00:00
Renovate Bot 2b60730532
Update dependency yarn to v1.19.2 2019-11-22 13:58:03 +00:00
Pavel Djundik c64a8728b5
Merge pull request #3520 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.6.0
2019-11-18 12:11:25 +02:00
Renovate Bot 3cbf67bacb
Update dependency eslint-config-prettier to v6.6.0 2019-11-17 00:40:58 +00:00
Pavel Djundik f51bb4ea65
Merge pull request #3517 from thelounge/renovate/stylelint-12.x
Update dependency stylelint to v12
2019-11-16 19:20:23 +02:00
Pavel Djundik f0d37d7e08
Merge pull request #3519 from thelounge/renovate/ldapjs-2.x
Update dependency ldapjs to v2.0.0-pre.3
2019-11-16 19:20:16 +02:00
Renovate Bot 2c280685ef
Update dependency ldapjs to v2.0.0-pre.3 2019-11-16 15:57:26 +00:00
Renovate Bot 67a84fe2f1
Update dependency stylelint to v12 2019-11-16 12:31:19 +00:00
Pavel Djundik 6a75ab3f27
Merge pull request #3516 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.11
2019-11-16 11:14:35 +02:00
Pavel Djundik b8ae278fba
Merge pull request #3514 from thelounge/renovate/eslint-plugin-vue-6.x
Update dependency eslint-plugin-vue to v6.0.1
2019-11-16 11:14:27 +02:00
Renovate Bot 7a34c661cf
Update dependency mochapack to v1.1.11 2019-11-16 02:26:44 +00:00
Renovate Bot 20d40d2a32
Update dependency eslint-plugin-vue to v6.0.1 2019-11-16 00:27:12 +00:00
Pavel Djundik aaedaffa83
Merge pull request #3513 from thelounge/renovate/mime-types-2.x
Update dependency mime-types to v2.1.25
2019-11-15 21:26:35 +02:00
Renovate Bot 59dd897099
Update dependency mime-types to v2.1.25 2019-11-12 15:56:23 +00:00
Pavel Djundik 586ceacd2a
Merge pull request #3512 from thelounge/renovate/commander-4.x
Update dependency commander to v4.0.1
2019-11-11 14:47:22 +02:00
Renovate Bot 3471413a00 Update dependency commander to v4.0.1 2019-11-11 14:42:47 +02:00
Pavel Djundik 1410256e42
Merge pull request #3489 from thelounge/xpaw/fix-3221
Enable some user commands for LDAP
2019-11-11 12:01:48 +02:00
Pavel Djundik 74fd296d61
Merge pull request #3490 from thelounge/xpaw/showinactive-fix
Fix potential issue of history not loading when `showInActive` is the first message
2019-11-11 12:01:41 +02:00
Pavel Djundik 36002757be
Merge pull request #3509 from thelounge/renovate/chalk-3.x
Update dependency chalk to v3
2019-11-11 12:01:33 +02:00
Pavel Djundik a78a7fbc92
Merge pull request #3511 from thelounge/renovate/prettier-1.x
Update dependency prettier to v1.19.1
2019-11-11 12:01:22 +02:00
Renovate Bot b26b73e994
Update dependency prettier to v1.19.1 2019-11-09 12:43:30 +00:00
Renovate Bot 1f2e69a550
Update dependency chalk to v3 2019-11-09 10:37:26 +00:00
Pavel Djundik 3d9f185494
Merge pull request #3510 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.9
2019-11-09 12:36:35 +02:00
Renovate Bot d84a2dbecc
Update dependency mochapack to v1.1.9 2019-11-09 10:09:22 +00:00
Pavel Djundik b550591262
Merge pull request #3501 from thelounge/renovate/irc-framework-4.x
Update dependency irc-framework to v4.5.2
2019-11-09 11:18:05 +02:00
Pavel Djundik 17b2d2fc32
Merge pull request #3506 from thelounge/renovate/prettier-1.x
Update dependency prettier to v1.19.0
2019-11-09 11:17:57 +02:00
Pavel Djundik a13bcb8e93 Format prettier after update 2019-11-09 10:55:50 +02:00
Renovate Bot f87bb85f37
Update dependency prettier to v1.19.0 2019-11-09 08:52:09 +00:00
Renovate Bot 38dd077bdf
Update dependency irc-framework to v4.5.2 2019-11-09 08:51:59 +00:00
Pavel Djundik 295ed871c7
Merge pull request #3503 from thelounge/renovate/babel-monorepo
Update babel monorepo
2019-11-09 10:51:08 +02:00
Pavel Djundik 86f3baae90
Merge pull request #3504 from thelounge/renovate/copy-webpack-plugin-5.x
Update dependency copy-webpack-plugin to v5.0.5
2019-11-09 10:51:01 +02:00
Pavel Djundik 7ff508ca4e
Merge pull request #3505 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.8
2019-11-09 10:50:53 +02:00
Pavel Djundik d3ccf17953
Merge pull request #3507 from thelounge/renovate/pretty-quick-2.x
Update dependency pretty-quick to v2.0.1
2019-11-09 10:50:47 +02:00
Pavel Djundik f98a70d58d
Merge pull request #3508 from thelounge/renovate/eslint-plugin-vue-6.x
Update dependency eslint-plugin-vue to v6
2019-11-09 10:50:39 +02:00
Renovate Bot de25fdbf87
Update dependency eslint-plugin-vue to v6 2019-11-09 02:09:03 +00:00
Renovate Bot 89e14d6ddc
Update dependency pretty-quick to v2.0.1 2019-11-09 02:08:49 +00:00
Renovate Bot 1822a67ef1
Update dependency mochapack to v1.1.8 2019-11-09 01:44:39 +00:00
Renovate Bot 0f3a088404
Update dependency copy-webpack-plugin to v5.0.5 2019-11-09 00:00:29 +00:00
Renovate Bot 561cb5cfa8
Update babel monorepo 2019-11-09 00:00:15 +00:00
Pavel Djundik 874385814d
Merge pull request #3502 from thelounge/webpack-hmr
Add webpack hot module reloading for development
2019-11-08 17:26:10 +02:00
Pavel Djundik 61f86b1557
Merge pull request #2821 from Raqbit/show-link-filesize
Add file size to link preview
2019-11-08 16:53:56 +02:00
Tim Miller-Williams 19d8178606 Add webpack hot module reloading for development
Co-Authored-By: Tim Miller-Williams <timmw@users.noreply.github.com>
2019-11-08 15:02:44 +02:00
Raqbit 95cc9a47fb Add file size to link preview 2019-11-07 16:58:28 +01:00
Pavel Djundik 8a224809dd
Merge pull request #3498 from MiniDigger/feature/package-versions
Print package versions on startup
2019-11-07 11:32:43 +02:00
MiniDigger 6f8364b1dd Print package versions on startup 2019-11-06 18:02:28 +01:00
Pavel Djundik 901d96c8cc
Merge pull request #3494 from thelounge/xpaw/remove-away-chan
Remove away messages from channels
2019-11-05 12:43:43 +02:00
Pavel Djundik 3ed54a3e11 Remove away/back condense as it won't be in channels 2019-11-05 12:37:55 +02:00
Pavel Djundik 39213bc4e7
Merge pull request #3493 from thelounge/xpaw/tooltips-import
Import primer-tooltips css
2019-11-05 12:35:06 +02:00
Pavel Djundik c62305039e
Merge pull request #3496 from thelounge/renovate/irc-framework-4.x
Update dependency irc-framework to v4.5.1
2019-11-04 16:33:54 +02:00
Renovate Bot b7b3717e3b
Update dependency irc-framework to v4.5.1 2019-11-04 14:21:47 +00:00
Pavel Djundik 14c0e4071a
Merge pull request #3491 from thelounge/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.10
2019-11-03 20:24:53 +02:00
Pavel Djundik 0ede916b78
Merge pull request #3492 from thelounge/renovate/vue-loader-15.x
Update dependency vue-loader to v15.7.2
2019-11-03 20:24:46 +02:00
Pavel Djundik 14c2cf6b0b Remove away message from channels
Fixes #3026
2019-11-02 16:53:41 +02:00
Pavel Djundik 79e0558b73 Add some tests for built css 2019-11-02 12:53:38 +02:00
Pavel Djundik 48713428b7 Import primer-tooltips css 2019-11-02 12:45:41 +02:00
Renovate Bot d5224a9d01
Update dependency vue-loader to v15.7.2 2019-11-02 08:29:51 +00:00
Renovate Bot 1de39524f7
Update dependency webpack-cli to v3.3.10 2019-11-02 01:31:07 +00:00
Pavel Djundik 064d36a6cc
Merge pull request #3488 from thelounge/renovate/commander-4.x
Update dependency commander to v4
2019-11-01 19:56:54 +02:00
Pavel Djundik adef07f6d8
Merge pull request #3487 from thelounge/renovate/file-type-12.x
Update dependency file-type to v12.4.0
2019-11-01 19:56:46 +02:00
Renovate Bot aaf2a563c8
Update dependency file-type to v12.4.0 2019-11-01 15:26:34 +00:00
Pavel Djundik 75eb812f05 Fix potential issue of history not loading when showInActive is set 2019-11-01 16:43:55 +02:00
Pavel Djundik 959ec5b598
Merge pull request #3471 from MiniDigger/feature/plugin-messages
Add message type for plugins
2019-11-01 16:41:41 +02:00
Pavel Djundik a35675ddc1
Merge pull request #3449 from thelounge/xpaw/postcss
Use postcss to optimize css
2019-11-01 16:40:13 +02:00
Pavel Djundik 5b3399f95a Use postcss to optimize css 2019-11-01 13:47:18 +02:00
Pavel Djundik e58a895293 Enable some user commands for LDAP
Fixes #3221
2019-11-01 13:37:32 +02:00
Pavel Djundik ddebb22afe
Merge pull request #3482 from thelounge/xpaw/fix-3219
Load existing users on startup when LDAP is enabled
2019-11-01 13:32:24 +02:00
Pavel Djundik fb250682a1
Merge pull request #3483 from thelounge/xpaw/improve-user-startup
Improvements to network connections on startup
2019-11-01 13:26:24 +02:00
Pavel Djundik eb971a7d23
Merge pull request #3485 from thelounge/xpaw/upload-baseurl
Allow configuring base url for uploads
2019-11-01 13:16:13 +02:00
Pavel Djundik 1f2ca91d89
Merge pull request #3486 from thelounge/xpaw/firefox-copy-hack
Disable copy hack in Firefox
2019-11-01 13:15:23 +02:00
Pavel Djundik e09599aeae Fix running commands 2019-11-01 12:51:57 +02:00
Renovate Bot 32e86dc699
Update dependency commander to v4 2019-11-01 10:11:28 +00:00
Pavel Djundik 97cfd1a2bc Disable copy hack in Firefox 2019-10-31 16:49:09 +02:00
Pavel Djundik 372f9f7ce4
Merge pull request #3345 from Jay2k1/patch-1
improve RTL text support
2019-10-31 16:32:01 +02:00
Pavel Djundik 6c57339668 Allow configure base url for uploads
Fixes #3484
2019-10-31 13:21:22 +02:00
Pavel Djundik a0c2495c42 Improvements to network connections on startup 2019-10-31 11:31:37 +02:00
Pavel Djundik fe4e0343a4 Load existing users on startup when LDAP is enabled
Fixes #3219
2019-10-31 11:01:44 +02:00
Pavel Djundik d8a6b137fe
Merge pull request #3450 from thelounge/xpaw/renovate-config
Create devDependencies update prs on weekends
2019-10-29 12:04:28 +02:00
Pavel Djundik 64efa0cf7b
Merge pull request #3480 from thelounge/renovate/irc-framework-4.x
Update dependency irc-framework to v4.5.0
2019-10-29 12:04:08 +02:00
Renovate Bot 4d55614db1
Update dependency irc-framework to v4.5.0 2019-10-29 00:12:39 +00:00
Pavel Djundik 27a06b533c
Merge pull request #3477 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.5.0
2019-10-28 12:54:41 +02:00
Renovate Bot 479d2eadf0
Update dependency eslint-config-prettier to v6.5.0 2019-10-28 10:42:11 +00:00
Pavel Djundik 59e37cf73d
Merge pull request #3476 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.6.0
2019-10-28 12:41:33 +02:00
Renovate Bot 46e6b7282e
Update dependency eslint to v6.6.0 2019-10-28 10:23:57 +00:00
Pavel Djundik e26abb07fb v3.3.0 2019-10-28 12:22:20 +02:00
Pavel Djundik 0e5d64e027 Add changelog entry for v3.3.0 2019-10-28 12:21:58 +02:00
Pavel Djundik 298aa4c664
Merge pull request #3475 from thelounge/astorije/badges
Make badges a little more consistent with each other
2019-10-25 10:29:46 +03:00
Jérémie Astori 8f46f101b8
Make badges a little more consistent with each other 2019-10-25 00:01:24 -04:00
Jay2k1 c89aea3c1e add bidi support to messages, actions, previews etc 2019-10-24 16:53:29 +02:00
Pavel Djundik 2aa5ed44ad v3.3.0-rc.2 2019-10-23 12:48:06 +03:00
Pavel Djundik 0c97a8e48e
Add changelog entry for v3.3.0-rc.2 2019-10-23 12:47:48 +03:00
Pavel Djundik c72fce75de
Merge pull request #3473 from thelounge/xpaw/hide-user-load-log
Hide user loaded message in tests
2019-10-23 12:47:11 +03:00
Pavel Djundik 49fb6cc049 Hide user loaded message in tests 2019-10-23 12:42:01 +03:00
Pavel Djundik 5ba5505ba4
Merge pull request #3470 from thelounge/xpaw/first-run-crash
Do not crash on first run due to config.js not existing
2019-10-23 12:33:33 +03:00
Pavel Djundik 3df6e00b70
Merge pull request #3467 from thelounge/xpaw/fix-upload-init
Fix uploader being initialized more than once
2019-10-23 12:10:03 +03:00
MiniDigger dbec8330ce Pass package info around so it can be used as identifier 2019-10-22 20:03:54 +02:00
MiniDigger 8f7bee8dd3 Add icon to message 2019-10-22 19:38:13 +02:00
Pavel Djundik 8379a46c53
Merge pull request #3472 from thelounge/renovate/web-push-3.x
Update dependency web-push to v3.4.1
2019-10-22 20:15:00 +03:00
Renovate Bot 41b78f1ae2
Update dependency web-push to v3.4.1 2019-10-22 17:02:24 +00:00
MiniDigger 19d69ba4c3 Add message type for plugins 2019-10-22 18:44:05 +02:00
Pavel Djundik 41e5090fb0 Do not crash on first run due to config.js not existing 2019-10-22 15:00:05 +03:00
Pavel Djundik 5595b17060
Merge pull request #3468 from thelounge/renovate/file-type-12.x
Update dependency file-type to v12.3.1
2019-10-22 11:44:18 +03:00
Renovate Bot 7988a0d006
Update dependency file-type to v12.3.1 2019-10-22 04:06:49 +00:00
Pavel Djundik 63c638e9ad Fix uploader being initialized more than once 2019-10-21 18:57:01 +03:00
Pavel Djundik d237647f8a v3.3.0-rc.1 2019-10-21 18:01:05 +03:00
Pavel Djundik c47dd965c9
Add changelog entry for v3.3.0-rc.1 2019-10-21 18:00:43 +03:00
Pavel Djundik d4198e4360
Merge pull request #3461 from thelounge/xpaw/reconnect-unread
Synchronize open channel on client on reconnection
2019-10-21 17:59:34 +03:00
Pavel Djundik c4a637ab49
Merge pull request #3455 from thelounge/xpaw/fix-changelog-monorepos
Extract updated packages from pull request body
2019-10-21 15:59:18 +03:00
Pavel Djundik 28949fb5e2
Merge pull request #3458 from thelounge/xpaw/remove-warn
Do not print "no packages" warning when opening help on client
2019-10-21 15:59:11 +03:00
Pavel Djundik 2273c913ac
Merge pull request #3451 from thelounge/xpaw/heading-contrast
Increase contrast of headers in windows
2019-10-21 11:01:47 +03:00
Pavel Djundik 00e59000fd
Merge pull request #3460 from thelounge/xpaw/history-reconnect
Fix history not loading in certain cases after reconnect
2019-10-21 11:01:41 +03:00
Pavel Djundik ee91217d98
Merge pull request #3453 from thelounge/xpaw/sync-users-reconnect
Synchronize user list correctly on reconnection
2019-10-21 11:01:33 +03:00
Pavel Djundik 93cb395b75
Merge pull request #3466 from thelounge/xpaw/menu-styles
Update context and auto complete menu styles
2019-10-20 22:28:10 +03:00
Pavel Djundik d020875556
Merge pull request #3465 from thelounge/xpaw/bump-ecma-version
Bump ecmaVersion to 2018
2019-10-20 22:28:05 +03:00
Pavel Djundik 2447c00a61
Merge pull request #3462 from thelounge/xpaw/rm-david
Update badges in readme
2019-10-20 22:27:58 +03:00
Pavel Djundik 6d4ee6e76a
Merge pull request #3463 from thelounge/xpaw/travis
Remove Windows/OSX builds from Travis
2019-10-20 22:27:50 +03:00
Pavel Djundik 0daf985dff
Merge pull request #3464 from thelounge/renovate/mocha-6.x
Update dependency mocha to v6.2.2
2019-10-20 22:27:44 +03:00
Pavel Djundik 4e17067a07 Update context and auto complete menu styles 2019-10-20 00:36:40 +03:00
Pavel Djundik f22cfe0547 Bump ecmaVersion to 2018 2019-10-20 00:00:15 +03:00
Renovate Bot ddfadcd326
Update dependency mocha to v6.2.2 2019-10-18 20:12:09 +00:00
Pavel Djundik 5dfd1b8809 Update badges in readme
Remove david-dm badge
2019-10-17 16:18:15 +03:00
Pavel Djundik ed8f4c6c57
Update next node version in travis 2019-10-17 16:17:26 +03:00
Pavel Djundik 4d34a837a5
Remove Windows/OSX builds from Travis 2019-10-17 16:15:45 +03:00
Pavel Djundik 1ca16816c2 Synchronize open channel on client on reconnection 2019-10-17 13:54:30 +03:00
Pavel Djundik ebfecc3e9d Fix client not loading messages after reconnect in channels with less than 100 total messages 2019-10-17 13:27:15 +03:00
Pavel Djundik 51147f35b2 Do not try loading history while disconnected 2019-10-17 12:38:46 +03:00
Pavel Djundik 8aa8768dcc Do not print no packages warning when opening help on client 2019-10-16 17:07:25 +03:00
Pavel Djundik e3e0495a9f Extract updated packages from pull request body
Fixes getting package list when PR updates multiple packages (monorepos)
2019-10-15 21:30:43 +03:00
Jos Ahrens 8c3c426998
Automatically apply GitHub labels on issue creation 2019-10-15 17:48:36 +00:00
Pavel Djundik ee4de6a871
Merge pull request #3452 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.41.2
2019-10-15 20:35:55 +03:00
Pavel Djundik 7687c90edc Synchronize user list correctly on reconnection 2019-10-15 20:33:03 +03:00
Renovate Bot 5027b9bf47
Update dependency webpack to v4.41.2 2019-10-15 13:16:39 +00:00
Pavel Djundik ba517bbac9 Increase contrast of headers in windows 2019-10-14 17:30:35 +03:00
Pavel Djundik 44d207f5f5 Create devDependencies update prs on weekends 2019-10-14 12:28:05 +03:00
Pavel Djundik e66f47904e
Merge pull request #3448 from thelounge/renovate/pretty-quick-2.x
Update dependency pretty-quick to v2
2019-10-13 11:39:47 +03:00
Pavel Djundik b0264f0725
Merge pull request #3447 from thelounge/renovate/husky-3.x
Update dependency husky to v3.0.9
2019-10-13 11:39:41 +03:00
Renovate Bot dedc70d3ff
Update dependency pretty-quick to v2 2019-10-13 04:30:17 +00:00
Renovate Bot 13f728e2c0
Update dependency husky to v3.0.9 2019-10-13 00:14:10 +00:00
Pavel Djundik 76ff5dfef5
Merge pull request #3441 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.19.1
2019-10-11 16:12:20 +03:00
Pavel Djundik 742cf433d9
Merge pull request #3440 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.4.0
2019-10-11 16:12:13 +03:00
Pavel Djundik 2dfebda8a1
Merge pull request #3442 from thelounge/renovate/babel-monorepo
Update babel monorepo
2019-10-11 16:12:06 +03:00
Pavel Djundik ebd894e915
Merge pull request #3443 from thelounge/renovate/stylelint-11.x
Update dependency stylelint to v11.1.1
2019-10-11 16:11:58 +03:00
Pavel Djundik bc3b1875cd
Merge pull request #3445 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.41.1
2019-10-11 16:11:51 +03:00
Renovate Bot b0969d9856
Update dependency webpack to v4.41.1 2019-10-11 11:55:00 +00:00
Renovate Bot 4f1fe2b6f1
Update dependency stylelint to v11.1.1 2019-10-10 15:27:20 +00:00
Renovate Bot a39a49f711
Update babel monorepo 2019-10-10 14:35:39 +00:00
Renovate Bot 5fead0d909
Update dependency yarn to v1.19.1 2019-10-10 07:44:12 +00:00
Renovate Bot b0cff121ec
Update dependency eslint-config-prettier to v6.4.0 2019-10-10 07:43:54 +00:00
Pavel Djundik 4c684ecad4
Merge pull request #3438 from FryDay/issue-1154
Prefix channel before join
2019-10-10 10:06:47 +03:00
Pavel Djundik 9c6c03b23a
Merge pull request #3436 from thelounge/xpaw/random-port
Let OS generate a port in link prefetch tests
2019-10-10 10:06:11 +03:00
Pavel Djundik b3a13f1aa5
Merge pull request #3416 from thelounge/xpaw/add-user-chown
Set correct file owner for created user files and warn about it
2019-10-10 10:05:45 +03:00
Jordan Day 493f9b1b6c Prefix channel before join 2019-10-05 14:12:22 -05:00
Pavel Djundik 8c19613bce
Merge pull request #3437 from FryDay/issue-1801
Allow tab completion in middle of input
2019-10-04 18:53:03 +03:00
Jordan Day 037fa6d114 Allow tab completion in middle of input 2019-10-04 10:34:07 -05:00
Pavel Djundik c39f0d01e6
Merge pull request #3435 from FryDay/issue-3139
Key condensed messages by first message in array
2019-10-04 16:44:19 +03:00
Jordan Day deb5d2d090 Key condensed messages by first message in array 2019-10-04 08:37:12 -05:00
Pavel Djundik 390a0b8e83 Let OS generate a port in link prefetch tests 2019-10-04 14:19:04 +03:00
Pavel Djundik 75f7666548
Merge pull request #3432 from thelounge/mcinkay/1537-plugin-files
Add public files for plugins
2019-10-03 16:30:09 +03:00
Pavel Djundik 48060e9743
Merge pull request #3425 from thelounge/renovate/husky-3.x
Update dependency husky to v3.0.8
2019-10-03 16:15:14 +03:00
Pavel Djundik a552a36a4a
Merge pull request #3426 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.19.0
2019-10-03 16:15:06 +03:00
Pavel Djundik 577b1a5952
Merge pull request #3427 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.5.1
2019-10-03 16:14:59 +03:00
Pavel Djundik a26976918b
Merge pull request #3428 from thelounge/renovate/mocha-6.x
Update dependency mocha to v6.2.1
2019-10-03 16:14:53 +03:00
Pavel Djundik e7f1cff44d
Merge pull request #3431 from thelounge/renovate/vuedraggable-2.x
Update dependency vuedraggable to v2.23.2
2019-10-03 16:14:44 +03:00
Al McKinlay f163e20a93 Add public files for plugins 2019-10-02 10:33:08 +01:00
Renovate Bot e3abd5db48
Update dependency vuedraggable to v2.23.2 2019-10-02 03:22:19 +00:00
Renovate Bot 59ad41aea4
Update dependency husky to v3.0.8 2019-10-02 01:53:14 +00:00
Renovate Bot 667f203476
Update dependency eslint to v6.5.1 2019-10-01 01:55:12 +00:00
Renovate Bot 7d53aa145a
Update dependency mocha to v6.2.1 2019-09-29 13:54:05 +00:00
Renovate Bot 086237a06a
Update dependency yarn to v1.19.0 2019-09-28 16:43:10 +00:00
Pavel Djundik 048a272a61
Merge pull request #3424 from thelounge/renovate/web-push-3.x
Update dependency web-push to v3.4.0
2019-09-27 15:49:04 +03:00
Pavel Djundik b115f07c35
Merge pull request #3413 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.11.2
2019-09-27 15:48:57 +03:00
Renovate Bot 3bfd84ad5d
Update dependency web-push to v3.4.0 2019-09-27 12:21:02 +00:00
Renovate Bot 28f6971a27
Update dependency @fortawesome/fontawesome-free to v5.11.2 2019-09-26 12:02:14 +00:00
Pavel Djundik 7827d16571
Merge pull request #3417 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.41.0
2019-09-26 15:01:28 +03:00
Pavel Djundik a81a1e6c44
Merge pull request #3422 from thelounge/renovate/commander-3.x
Update dependency commander to v3.0.2
2019-09-26 15:00:53 +03:00
Renovate Bot 71841bf721
Update dependency commander to v3.0.2 2019-09-26 09:13:06 +00:00
Pavel Djundik 5d13e4c97d Check config owner synchronously
Fixes async warning printing during prompt when adding a user
2019-09-24 22:06:04 +03:00
Renovate Bot d6abc96a30
Update dependency webpack to v4.41.0 2019-09-24 16:19:10 +00:00
Pavel Djundik a24c03a35c Set correct file owner for created user files 2019-09-24 17:42:14 +03:00
Pavel Djundik fb8290399f
Merge pull request #3410 from thelounge/renovate/vuedraggable-2.x
Update dependency vuedraggable to v2.23.1
2019-09-24 14:00:37 +03:00
Pavel Djundik d90b3a5f0f
Merge pull request #3411 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.5
2019-09-24 14:00:31 +03:00
Pavel Djundik 36508faa2e
Merge pull request #3412 from thelounge/renovate/sinon-7.x
Update dependency sinon to v7.5.0
2019-09-24 14:00:25 +03:00
Pavel Djundik f8bc2ec285
Merge pull request #3414 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.6.2
2019-09-24 14:00:16 +03:00
Renovate Bot 4dd0f71843
Update babel monorepo to v7.6.2 2019-09-23 21:37:17 +00:00
Renovate Bot a2d636ff2d
Update dependency sinon to v7.5.0 2019-09-23 15:23:17 +00:00
Renovate Bot a69bb80500
Update dependency mochapack to v1.1.5 2019-09-23 07:39:18 +00:00
Renovate Bot d4e89127d0
Update dependency vuedraggable to v2.23.1 2019-09-21 15:20:33 +00:00
Pavel Djundik f293bdbdc3
Merge pull request #3408 from thelounge/renovate/socket.io-packages
Update socket.io packages to v2.3.0
2019-09-20 13:55:52 +03:00
Renovate Bot faf684576c
Update socket.io packages to v2.3.0 2019-09-20 10:26:43 +00:00
Pavel Djundik 2b4ee0d6c6
Merge pull request #3407 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.11.1
2019-09-19 13:47:14 +03:00
Renovate Bot 13e4af3600
Update dependency @fortawesome/fontawesome-free to v5.11.1 2019-09-19 08:39:14 +00:00
Pavel Djundik bfa97390be
Merge pull request #3406 from thelounge/xpaw/package-load-stack
Print error and stacktrace when package fails to load
2019-09-19 11:37:18 +03:00
Pavel Djundik b96e5cc042 Print error and stacktrace when package fails to load 2019-09-17 19:57:21 +03:00
Pavel Djundik a658d768d2
Merge pull request #3405 from thelounge/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.9
2019-09-17 12:40:09 +03:00
Renovate Bot fe0dc97197
Update dependency webpack-cli to v3.3.9 2019-09-17 08:28:20 +00:00
Pavel Djundik eba043d0b3
Merge pull request #3404 from thelounge/xpaw/keepnick
Implement keep nick when client gets "nick in use" on connection
2019-09-17 11:19:14 +03:00
Pavel Djundik 7e27f2d058
Merge pull request #3364 from thelounge/xpaw/active-chan-cursor
Use default cursor for active channels
2019-09-17 11:00:48 +03:00
Pavel Djundik 600115b8d1 Implement keep nick 2019-09-16 17:12:05 +03:00
Pavel Djundik d09a35b129 Use default cursor for active channels 2019-09-16 11:30:30 +03:00
Pavel Djundik c4236e0e12
Merge pull request #3391 from thelounge/xpaw/fix-3381
Move back to ldapjs dependency
2019-09-16 11:18:03 +03:00
Pavel Djundik cee3a50ddc Move back to ldapjs dependency
Fixes #3381
2019-09-15 22:42:27 +03:00
Pavel Djundik eb1d9079a3
Merge pull request #3403 from thelounge/renovate/stylelint-config-standard-19.x
Update dependency stylelint-config-standard to v19
2019-09-15 22:40:16 +03:00
Pavel Djundik 526cc126a0
Merge pull request #3397 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.3.0
2019-09-15 22:40:10 +03:00
Renovate Bot 34b0985587
Update dependency stylelint-config-standard to v19 2019-09-15 18:26:41 +00:00
Renovate Bot edc3e498e6
Update dependency eslint-config-prettier to v6.3.0 2019-09-15 18:26:29 +00:00
Pavel Djundik 0052390c4c
Merge pull request #3386 from thelounge/renovate/husky-3.x
Update dependency husky to v3.0.5
2019-09-15 21:25:44 +03:00
Pavel Djundik c728bcb95e
Merge pull request #3400 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.40.2
2019-09-15 21:25:36 +03:00
Pavel Djundik 860ea0b3f6
Merge pull request #3401 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.4.0
2019-09-15 21:25:30 +03:00
Pavel Djundik 959f2a7786
Merge pull request #3402 from thelounge/renovate/stylelint-11.x
Update dependency stylelint to v11
2019-09-15 21:25:22 +03:00
Renovate Bot 9a07a8f96c
Update dependency stylelint to v11 2019-09-15 14:33:10 +00:00
Renovate Bot bb604f2a2b
Update dependency eslint to v6.4.0 2019-09-14 01:50:04 +00:00
Renovate Bot 5d873d42a3
Update dependency webpack to v4.40.2 2019-09-13 14:35:44 +00:00
Renovate Bot 3b1c3f9c13
Update dependency husky to v3.0.5 2019-09-07 20:46:43 +00:00
Pavel Djundik 0e6252ef4d
Merge pull request #3395 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.6.0
2019-09-07 23:45:45 +03:00
Pavel Djundik b89aaf1030
Merge pull request #3394 from thelounge/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.8
2019-09-07 23:45:39 +03:00
Renovate Bot 8745910452
Update babel monorepo to v7.6.0 2019-09-06 17:40:42 +00:00
Renovate Bot 0567c38a4c
Update dependency webpack-cli to v3.3.8 2019-09-06 08:30:01 +00:00
Pavel Djundik 07da692c2b
Merge pull request #3392 from thelounge/node10
Bump minimum node version to v10
2019-09-06 11:29:14 +03:00
Pavel Djundik a564d2f8cb
Merge pull request #3393 from thelounge/github-ci
Add GitHub actions for CI
2019-09-06 11:25:36 +03:00
Pavel Djundik 9eff3b51d7 Allow extra slash on Windows in expandHome tests
Github CI on Windows ends up formatting it as 'd:\\tmp', this causes no issues besides the failing test
2019-09-04 22:51:58 +03:00
Pavel Djundik c546279d89 Setup github actions CI 2019-09-04 22:51:58 +03:00
Pavel Djundik 25dc6b52b4
Merge pull request #3388 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.2.0
2019-09-04 22:50:57 +03:00
Pavel Djundik 5a94727d79 Bump minimum node version to v10 2019-09-04 22:50:25 +03:00
Renovate Bot 3f52e15444
Update dependency eslint-config-prettier to v6.2.0 2019-09-04 10:21:23 +00:00
Pavel Djundik f62f92b36f
Merge pull request #3383 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.39.3
2019-09-04 13:20:11 +03:00
Pavel Djundik 70772ec92d
Merge pull request #3384 from thelounge/renovate/commander-3.x
Update dependency commander to v3.0.1
2019-09-04 13:19:59 +03:00
Pavel Djundik 5e4b8605b2
Merge pull request #3389 from thelounge/renovate/file-type-12.x
Update dependency file-type to v12.3.0
2019-09-04 13:19:54 +03:00
Pavel Djundik ecb0bb7590
Merge pull request #3387 from thelounge/renovate/sinon-7.x
Update dependency sinon to v7.4.2
2019-09-04 13:19:48 +03:00
Pavel Djundik d902a0ad67
Merge pull request #3385 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.3.0
2019-09-04 13:19:39 +03:00
Renovate Bot 9f76b83fa7
Update dependency file-type to v12.3.0 2019-09-03 16:11:00 +00:00
Renovate Bot d35b0fdb34
Update dependency sinon to v7.4.2 2019-09-02 11:53:23 +00:00
Renovate Bot f6b6a9138c
Update dependency eslint to v6.3.0 2019-08-30 20:20:07 +00:00
Renovate Bot 70ac60857c
Update dependency commander to v3.0.1 2019-08-30 08:58:45 +00:00
Renovate Bot 8bb7da8a55
Update dependency webpack to v4.39.3 2019-08-27 12:25:30 +00:00
Pavel Djundik 5ccd6b76c0
Merge pull request #3382 from thelounge/xpaw/upload-fixes
Some fixes in file uploading
2019-08-27 12:21:03 +03:00
Pavel Djundik 6f95e3769d
Merge pull request #3378 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.10.2
2019-08-25 21:25:00 +03:00
Pavel Djundik 05510c0694
Merge pull request #3375 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6.1.0
2019-08-25 21:24:54 +03:00
Pavel Djundik 58ad80c3bb
Merge pull request #3366 from gunnvaldr/patch-1
Adds data-current-channel to #chat-container
2019-08-25 20:38:08 +03:00
Renovate Bot 54c8e5bfc2
Update dependency eslint-config-prettier to v6.1.0 2019-08-25 17:27:10 +00:00
Renovate Bot c083b2ce19
Update dependency @fortawesome/fontawesome-free to v5.10.2 2019-08-25 17:26:59 +00:00
Pavel Djundik 0939adad0a
Merge pull request #3377 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.4
2019-08-25 20:26:02 +03:00
Pavel Djundik ccb1595ed3
Merge pull request #3369 from thelounge/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.7
2019-08-25 20:25:56 +03:00
Pavel Djundik d9844720f9
Merge pull request #3379 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.2.2
2019-08-25 20:25:47 +03:00
Pavel Djundik 554c602230 Do not request upload token if there's an upload in process 2019-08-25 20:23:32 +03:00
Pavel Djundik 04e1e004da Reset upload input so the same file can be selected again 2019-08-25 20:14:46 +03:00
Pavel Djundik 16d070c19e Update error messages for uploads 2019-08-25 20:14:34 +03:00
Renovate Bot 9e8e138359
Update dependency eslint to v6.2.2 2019-08-24 02:47:36 +00:00
Renovate Bot 846ab3ece3
Update dependency mochapack to v1.1.4 2019-08-22 10:05:57 +00:00
Renovate Bot 62ed80010f
Update dependency webpack-cli to v3.3.7 2019-08-21 20:15:43 +00:00
Pavel Djundik aadfede911
Merge pull request #3365 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.39.2
2019-08-21 23:14:11 +03:00
Pavel Djundik 7615f18536
Merge pull request #3371 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.2.1
2019-08-21 23:13:51 +03:00
Pavel Djundik cc1620b5c6
Merge pull request #3374 from thelounge/renovate/uuid-3.x
Update dependency uuid to v3.3.3
2019-08-21 23:13:44 +03:00
Pavel Djundik c4095c28b1
Merge pull request #3370 from thelounge/renovate/file-type-12.x
Update dependency file-type to v12.2.0
2019-08-21 23:13:35 +03:00
Pavel Djundik c244d1b9ed
Merge pull request #3368 from thelounge/renovate/husky-3.x
Update dependency husky to v3.0.4
2019-08-21 23:13:25 +03:00
Pavel Djundik 57aa286ab4
Merge pull request #3367 from thelounge/renovate/sqlite3-4.x
Update dependency sqlite3 to v4.1.0
2019-08-21 23:13:09 +03:00
Renovate Bot 4fffb4af2b
Update dependency eslint to v6.2.1 2019-08-20 11:50:08 +00:00
Renovate Bot 0a0626cc5f
Update dependency uuid to v3.3.3 2019-08-19 13:29:38 +00:00
Renovate Bot 3be0285467
Update dependency webpack to v4.39.2 2019-08-19 07:50:03 +00:00
Renovate Bot b864674e84
Update dependency sqlite3 to v4.1.0 2019-08-19 07:49:52 +00:00
Renovate Bot 4b7fdc85cc
Update dependency husky to v3.0.4 2019-08-19 07:49:42 +00:00
Renovate Bot 2d88116e5a
Update dependency file-type to v12.2.0 2019-08-19 07:49:31 +00:00
Pavel Djundik 4d665b6a5e v3.2.0 2019-08-18 21:53:20 +03:00
Pavel Djundik 7169622d01 Add changelog entry for v3.2.0 2019-08-18 21:52:32 +03:00
Gunnvaldr Pope 2b91bf0374
Adds data-current-channel to #chat-container
Allows for broader customization of CSS per channel(s).
2019-08-13 16:24:34 -05:00
Pavel Djundik 3c9ba130a0 Allow saving github token to a file 2019-08-13 12:43:47 +03:00
Pavel Djundik 9ad785c44e v3.2.0-rc.2 2019-08-13 12:41:59 +03:00
Pavel Djundik 0f72b57d36 Changelog entry for v3.2.0-rc.2 2019-08-13 12:41:19 +03:00
Pavel Djundik d7dcdd1cd4
Merge pull request #3361 from thelounge/xpaw/unknown-cmd-error
Display an error on unknown command
2019-08-13 10:28:30 +03:00
Pavel Djundik 4753d58c0b Display an error on unknown command 2019-08-12 11:03:52 +03:00
Pavel Djundik 68925a532d
Merge pull request #3360 from thelounge/renovate/commander-3.x
Update dependency commander to v3
2019-08-12 10:48:40 +03:00
Renovate Bot f4ce7ecd1e
Update dependency commander to v3 2019-08-11 14:50:00 +00:00
Pavel Djundik aa6475e83d
Merge pull request #3356 from thelounge/renovate/husky-3.x
Update dependency husky to v3.0.3
2019-08-11 17:49:29 +03:00
Pavel Djundik 217cfb4701
Merge pull request #3349 from ollipa/ollipa/edit-topic
Add functionality to edit channel topic from the user interface
2019-08-11 17:48:50 +03:00
ollipa e69e448396 add save button to edit topic input field 2019-08-11 17:34:01 +03:00
ollipa 49652fc40a make channel topic editable from user interface 2019-08-11 15:29:08 +03:00
Renovate Bot e241d31c15
Update dependency husky to v3.0.3 2019-08-09 17:48:37 +00:00
Pavel Djundik fc9e20c09d
Merge pull request #3359 from plett/activity-shortcut
Add keybind for cycling to the next unread window
2019-08-09 20:29:05 +03:00
Paul Lettington c6b19d5144 Add keybind for cycling to the next unread window 2019-08-09 15:16:44 +01:00
Pavel Djundik b4eb538903
Merge pull request #3347 from thelounge/xpaw/focus-on-special-upd
Open list channel on data updates
2019-08-07 15:16:24 +03:00
Pavel Djundik 51fb42c379
Merge pull request #3352 from thelounge/xpaw/truly-raw
Make /raw actually write to network as-is
2019-08-07 15:15:36 +03:00
Pavel Djundik d1e7392b97
Merge pull request #3353 from thelounge/renovate/textcomplete-0.x
Update dependency textcomplete to v0.18.0
2019-08-07 15:10:21 +03:00
Pavel Djundik 91f944f5f5
Merge pull request #3355 from thelounge/renovate/sinon-7.x
Update dependency sinon to v7.4.1
2019-08-07 15:10:02 +03:00
Renovate Bot c2c94025f3
Update dependency sinon to v7.4.1 2019-08-06 09:33:42 +00:00
Renovate Bot 8b904fa136
Update dependency textcomplete to v0.18.0 2019-08-06 04:12:54 +00:00
Pavel Djundik 858f8425fd Make /raw actually write to network as-is 2019-08-05 20:16:39 +03:00
Pavel Djundik 96efaed07a Open list channel on data updates 2019-08-04 14:03:04 +03:00
Pavel Djundik 1f27c4fad4 v3.2.0-rc.1 2019-08-04 12:04:56 +03:00
Pavel Djundik ac2165bdca Add changelog entry for v3.2.0-rc.1 2019-08-04 12:04:43 +03:00
Pavel Djundik 72bebd8681
Merge pull request #3326 from thelounge/xpaw/theme-color
Allow themes to change theme-color
2019-08-03 22:28:20 +03:00
Pavel Djundik 85025a6840
Merge pull request #3329 from thelounge/xpaw/sw-error
Send service worker fetch errors to client
2019-08-03 22:27:53 +03:00
Pavel Djundik ecb4dd9675
Merge pull request #3335 from thelounge/xpaw/upload-sanity
Handle upload token requesting in a better way
2019-08-03 22:23:20 +03:00
Pavel Djundik b8948856f3
Merge pull request #3343 from thelounge/xpaw/fix-formatting
Fix formatting hotkeys on non english locales
2019-08-03 22:14:31 +03:00
Pavel Djundik 65713e5509
Merge pull request #3339 from thelounge/xpaw/statusmsg
Parse target group for sent messages when echo-message is not enabled
2019-08-03 22:14:24 +03:00
Pavel Djundik f4cf33da4b
Merge pull request #3342 from thelounge/xpaw/disable-io-cookie
Disable io cookie
2019-08-03 22:14:15 +03:00
Pavel Djundik c8819e9a13 Fix formatting hotkeys on non english locales 2019-08-03 14:16:13 +03:00
Pavel Djundik 7bb588be5d
Merge pull request #3341 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.10.1
2019-08-03 12:06:42 +03:00
Pavel Djundik 59d2d6fec8 Disable io cookie
Ref https://github.com/socketio/socket.io/issues/2276
2019-08-03 12:03:02 +03:00
Renovate Bot 914ad9f11a
Update dependency @fortawesome/fontawesome-free to v5.10.1 2019-08-02 19:53:47 +00:00
Pavel Djundik 580d83858c
Merge pull request #3327 from thelounge/renovate/package-json-6.x
Update dependency package-json to v6.5.0
2019-08-02 21:58:59 +03:00
Renovate Bot 5345d84d1f
Update dependency package-json to v6.5.0 2019-08-02 17:12:44 +00:00
Pavel Djundik db126dc75c
Merge pull request #3328 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.39.1
2019-08-02 20:11:50 +03:00
Pavel Djundik e0879ed075
Merge pull request #3333 from thelounge/renovate/copy-webpack-plugin-5.x
Update dependency copy-webpack-plugin to v5.0.4
2019-08-02 20:11:36 +03:00
Pavel Djundik 9c7e27ada4
Merge pull request #3330 from thelounge/renovate/semver-6.x
Update dependency semver to v6.3.0
2019-08-02 20:11:20 +03:00
Pavel Djundik d4a729d316
Merge pull request #3334 from thelounge/renovate/file-type-12.x
Update dependency file-type to v12.1.0
2019-08-02 20:11:10 +03:00
Pavel Djundik 1650383846
Merge pull request #3336 from thelounge/renovate/font-awesome
Update dependency @fortawesome/fontawesome-free to v5.10.0
2019-08-02 20:10:59 +03:00
Pavel Djundik b3c9a57d0c
Merge pull request #3337 from thelounge/renovate/husky-3.x
Update dependency husky to v3.0.2
2019-08-02 20:10:50 +03:00
Pavel Djundik 734699de5b
Merge pull request #3340 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.3
2019-08-02 20:10:38 +03:00
Renovate Bot ea8d734fc2
Update dependency webpack to v4.39.1 2019-08-02 12:40:27 +00:00
Renovate Bot 2cdfe5ee7a
Update dependency mochapack to v1.1.3 2019-08-02 10:44:22 +00:00
Pavel Djundik ad984fa377 Parse target group for sent messages when echo-message is not enabled 2019-08-01 00:16:00 +03:00
Renovate Bot 805a86c67e
Update dependency husky to v3.0.2 2019-07-29 17:22:56 +00:00
Renovate Bot 5f9a706fe0
Update dependency @fortawesome/fontawesome-free to v5.10.0 2019-07-29 15:16:40 +00:00
Pavel Djundik 932d9c809b Handle upload token requesting in a better way 2019-07-29 11:04:33 +03:00
Renovate Bot e92082de57
Update dependency file-type to v12.1.0 2019-07-26 11:32:30 +00:00
Renovate Bot ed911ddba4
Update dependency copy-webpack-plugin to v5.0.4 2019-07-26 10:41:26 +00:00
Renovate Bot 8514503fc3
Update dependency semver to v6.3.0 2019-07-23 19:27:55 +00:00
Pavel Djundik 5041e82980 Send service worker fetch errors to client 2019-07-23 15:31:26 +03:00
Pavel Djundik efa0aeb2c6 Allow themes to change theme-color 2019-07-22 19:50:04 +03:00
Pavel Djundik 526a689e14
Merge pull request #3323 from thelounge/renovate/babel-plugin-istanbul-5.x
Update dependency babel-plugin-istanbul to v5.2.0
2019-07-22 12:01:37 +03:00
Renovate Bot b7f445ade3
Update dependency babel-plugin-istanbul to v5.2.0 2019-07-21 14:50:38 +00:00
Pavel Djundik 998bdd49aa
Merge pull request #3321 from thelounge/renovate/eslint-6.x
Update dependency eslint to v6.1.0
2019-07-21 10:59:47 +03:00
Renovate Bot fc979c17f7
Update dependency eslint to v6.1.0 2019-07-21 04:11:04 +00:00
Pavel Djundik 2500602d3b
Merge pull request #3320 from thelounge/xpaw/fix-3309
Fix channel list not working on some touch devices
2019-07-19 22:42:41 +03:00
Pavel Djundik ba356ae34c
Merge pull request #1873 from thelounge/astorije/outlines
Add custom focus outlines for inputs and green buttons
2019-07-19 16:45:15 +03:00
Jérémie Astori e246c06c32 Do not silence outline of all elements, and add custom outline where possible
- Tweak outlines
- Consolidate focus shadows between inputs and buttons, apply custom focus to whole page (i.e. on "Join a channel" form), cleanup
- Do not add custom outlines on checkboxes/radios as browsers are not doing this too well
- Apply hover effect on all inputs, not just in `#windows`
2019-07-19 16:12:15 +03:00
Pavel Djundik fc532be5df
Merge pull request #2760 from thelounge/mcinkay/plugins/show-updates
Add check for outdated packages, and show on the help screen.
2019-07-19 16:06:53 +03:00
Pavel Djundik d7ff13a41d
Merge pull request #3319 from thelounge/renovate/eslint-config-prettier-6.x
Update dependency eslint-config-prettier to v6
2019-07-19 15:36:17 +03:00
Alistair McKinlay 20816d509d Add check for outdated packages, and show on the help screen. 2019-07-19 13:33:09 +01:00
Pavel Djundik f40e35515f Remove linebreak-style 2019-07-19 14:44:46 +03:00
Pavel Djundik a7bdc99d47 Fix channel list not working on some touch devices
Fixes #3309
2019-07-19 14:27:59 +03:00
Renovate Bot e536730dc1
Update dependency eslint-config-prettier to v6 2019-07-19 10:56:44 +00:00
Al McKinlay 9ef5c6c67e
Set up prettier for JS/Vue files (#3312)
Set up prettier for JS/Vue files
2019-07-19 11:55:55 +01:00
Alistair McKinlay 33de4fb4f4 Add prettier info to CONTRIBUTING.md 2019-07-19 11:30:43 +01:00
Alistair McKinlay cc7b4e4817 Fix prettier breaking eslint-disable for confirm 2019-07-19 11:27:40 +01:00
Jérémie Astori 2fbdbead55 WIP Improve Prettified Vue code 2019-07-19 11:27:40 +01:00
Alistair McKinlay 133e7bf710 Format js/vue with prettier 2019-07-19 11:27:40 +01:00
Alistair McKinlay 48eeb11391 Set up prettier on js/vue 2019-07-19 11:24:30 +01:00
Pavel Djundik 7e5c2672b2
Merge pull request #3307 from thelounge/xpaw/browser-object
Store ip and language in a separate object in user file
2019-07-19 13:11:20 +03:00
Pavel Djundik 06d3178633
Merge pull request #3317 from thelounge/renovate/lodash-monorepo
Update dependency lodash to v4.17.15
2019-07-19 13:00:00 +03:00
Renovate Bot 19d7333dce
Update dependency lodash to v4.17.15 2019-07-19 08:32:29 +00:00
Pavel Djundik 9f1b9fa310
Merge pull request #3311 from thelounge/renovate/webpack-4.x
Update dependency webpack to v4.36.1
2019-07-19 11:30:56 +03:00
Pavel Djundik ef6c884441
Merge pull request #3313 from thelounge/renovate/babel-monorepo
Update babel monorepo to v7.5.5
2019-07-19 11:30:48 +03:00
Pavel Djundik 1922314827
Merge pull request #3316 from thelounge/renovate/mocha-6.x
Update dependency mocha to v6.2.0
2019-07-19 11:30:39 +03:00
Pavel Djundik 4371cd6db1
Merge pull request #3315 from thelounge/renovate/husky-3.x
Update dependency husky to v3.0.1
2019-07-19 11:30:31 +03:00
Pavel Djundik 2af6b87369
Merge pull request #3314 from thelounge/renovate/vue-loader-15.x
Update dependency vue-loader to v15.7.1
2019-07-19 11:30:19 +03:00
Renovate Bot fe0c9a22da
Update dependency mocha to v6.2.0 2019-07-18 21:56:34 +00:00
Renovate Bot 9003566d5c
Update dependency husky to v3.0.1 2019-07-18 21:37:33 +00:00
Renovate Bot f5c3111445
Update dependency webpack to v4.36.1 2019-07-18 10:33:51 +00:00
Renovate Bot 3ff71701a4
Update dependency vue-loader to v15.7.1 2019-07-18 10:33:39 +00:00
Renovate Bot a1c307ee8d
Update babel monorepo to v7.5.5 2019-07-18 10:33:21 +00:00
Pavel Djundik 0ff5a7df67
Merge pull request #3305 from thelounge/xpaw/init-reconnect-state
Merge network/channel objects when reconnecting to keep object references
2019-07-18 12:40:44 +03:00
Pavel Djundik 03233c3f4d
Merge pull request #3310 from thelounge/xpaw/improve-raw
Improve raw messages
2019-07-18 11:58:46 +03:00
Pavel Djundik 803de9a877
Merge pull request #3060 from thelounge/astorije/smaller-prettier
Set up Prettier on HTML, JSON, Markdown, and YAML files
2019-07-17 12:52:52 +03:00
Alistair McKinlay ab8d819193 Set up Prettier on md, html, json, and yaml 2019-07-17 10:22:30 +01:00
Pavel Djundik 2b1197880d Merge network/channel objects when reconnecting 2019-07-17 12:07:49 +03:00
Pavel Djundik 2741421401
Merge pull request #3306 from thelounge/renovate/mochapack-1.x
Update dependency mochapack to v1.1.2
2019-07-17 10:44:34 +03:00
Pavel Djundik 701be850b6
Merge pull request #3308 from thelounge/renovate/mini-css-extract-plugin-0.x
Update dependency mini-css-extract-plugin to v0.8.0
2019-07-17 10:44:18 +03:00
Pavel Djundik e04bfe39bf Improve raw messages 2019-07-17 10:34:23 +03:00
Renovate Bot 48767d6030
Update dependency mini-css-extract-plugin to v0.8.0 2019-07-16 20:51:08 +00:00
Pavel Djundik 54a4085b68 Store ip and language in a separate object in user file 2019-07-16 13:00:29 +03:00
Renovate Bot 6b70f2a99f
Update dependency mochapack to v1.1.2 2019-07-16 09:09:17 +00:00
Pavel Djundik 01347787b7
Merge pull request #3297 from thelounge/xpaw/better-disable-touch
A better way of disabling sorting on touch devices
2019-07-15 12:46:10 +03:00
Pavel Djundik ee8228ced2
Merge pull request #3298 from thelounge/renovate/linkify-it-2.x
Update dependency linkify-it to v2.2.0
2019-07-15 10:36:12 +03:00
Pavel Djundik 8b38cbcb52
Merge pull request #3300 from thelounge/renovate/yarn-1.x
Update dependency yarn to v1.17.3
2019-07-15 10:36:00 +03:00
Pavel Djundik 442389c544
Merge pull request #3304 from thelounge/renovate/webpack-cli-3.x
Update dependency webpack-cli to v3.3.6
2019-07-15 10:35:51 +03:00
Pavel Djundik eac4fb73f2
Merge pull request #3299 from thelounge/xpaw/fix-sync-sort
Fix channel sorting to work across clients on Vue
2019-07-15 10:33:37 +03:00
Renovate Bot 7f481634f0
Update dependency webpack-cli to v3.3.6 2019-07-14 13:10:50 +00:00
Renovate Bot 10948b3bb7
Update dependency yarn to v1.17.3 2019-07-12 21:42:50 +00:00
Pavel Djundik bf2c6a6bcf Fix channel sorting to work across clients on Vue 2019-07-12 19:47:29 +03:00
Renovate Bot ac23215115
Update dependency linkify-it to v2.2.0 2019-07-12 12:11:34 +00:00
Pavel Djundik b2e5be33d6 A better way of disabling sorting on touch devices 2019-07-12 13:43:47 +03:00
Pavel Djundik 820a67802d
Merge pull request #3294 from thelounge/xpaw/fix-3293
Verify reverse DNS when looking up hostnames for webirc
2019-07-12 11:50:50 +03:00
Pavel Djundik a3c77266e0
Merge pull request #3296 from thelounge/xpaw/fix-3295
Correctly parse numbers when passed in CLI
2019-07-12 11:50:40 +03:00
Pavel Djundik 295b3a4251 Correctly parse numbers when passed in CLI
Fixes #3295
2019-07-12 10:56:19 +03:00
Pavel Djundik d3a98a523f Verify reverse DNS when looking up hostnames for webirc
Fixes #3293
2019-07-11 23:20:04 +03:00
Pavel Djundik 257ce5d0a8 v3.1.1 2019-07-11 19:47:38 +03:00
Pavel Djundik 776b20cf4f Update changelog 2019-07-11 19:47:35 +03:00
Pavel Djundik f1594e8a00
Merge pull request #3292 from thelounge/upgrade-lock
Upgrade packages and the lock file
2019-07-11 19:46:46 +03:00
Renovate Bot 011fd99503 Update dependency thelounge-ldapjs-non-maintained-fork to v1.0.4 2019-07-11 19:27:29 +03:00
Pavel Djundik 5f13d3308f Upgrade yarn lockfile 2019-07-11 19:27:29 +03:00
Renovate Bot f20ec1b9c3 Update dependency webpack to v4.35.3 2019-07-11 19:27:29 +03:00
Renovate Bot 13eaed3eb9 Update babel monorepo to v7.5.4 2019-07-11 19:27:29 +03:00
517 changed files with 42145 additions and 28237 deletions

1
.browserslistrc Normal file
View file

@ -0,0 +1 @@
last 2 year, firefox esr

View file

@ -16,6 +16,6 @@ insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.{json,yml}]
[*.{json,md,yml}]
indent_style = space
indent_size = 2

View file

@ -1,5 +1,3 @@
# third party
client/js/libs/jquery/*.js
public/
coverage/
dist/

193
.eslintrc.cjs Normal file
View file

@ -0,0 +1,193 @@
// @ts-check
const {defineConfig} = require("eslint-define-config");
const projects = defineConfig({
parserOptions: {
project: [
"./tsconfig.json",
"./client/tsconfig.json",
"./server/tsconfig.json",
"./shared/tsconfig.json",
"./test/tsconfig.json",
],
},
}).parserOptions.project;
const baseRules = defineConfig({
rules: {
"block-scoped-var": "error",
curly: ["error", "all"],
"dot-notation": "error",
eqeqeq: "error",
"handle-callback-err": "error",
"no-alert": "error",
"no-catch-shadow": "error",
"no-control-regex": "off",
"no-console": "error",
"no-duplicate-imports": "error",
"no-else-return": "error",
"no-implicit-globals": "error",
"no-restricted-globals": ["error", "event", "fdescribe"],
"no-template-curly-in-string": "error",
"no-unsafe-negation": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"no-useless-return": "error",
"no-use-before-define": [
"error",
{
functions: false,
},
],
"no-var": "error",
"object-shorthand": [
"error",
"methods",
{
avoidExplicitReturnArrows: true,
},
],
"padding-line-between-statements": [
"error",
{
blankLine: "always",
prev: ["block", "block-like"],
next: "*",
},
{
blankLine: "always",
prev: "*",
next: ["block", "block-like"],
},
],
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"spaced-comment": ["error", "always"],
strict: "off",
yoda: "error",
},
}).rules;
const vueRules = defineConfig({
rules: {
"import/no-default-export": 0,
"import/unambiguous": 0, // vue SFC can miss script tags
"@typescript-eslint/prefer-readonly": 0, // can be used in template
"vue/component-tags-order": [
"error",
{
order: ["template", "style", "script"],
},
],
"vue/multi-word-component-names": "off",
"vue/no-mutating-props": "off",
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/v-slot-style": ["error", "longform"],
},
}).rules;
const tsRules = defineConfig({
rules: {
// note you must disable the base rule as it can report incorrect errors
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"],
"@typescript-eslint/no-redundant-type-constituents": "off",
},
}).rules;
const tsRulesTemp = defineConfig({
rules: {
// TODO: eventually remove these
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unused-vars": "off",
},
}).rules;
const tsTestRulesTemp = defineConfig({
rules: {
// TODO: remove these
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/restrict-plus-operands": "off",
},
}).rules;
module.exports = defineConfig({
root: true,
parserOptions: {
ecmaVersion: 2022,
},
overrides: [
{
files: ["**/*.ts", "**/*.vue"],
parser: "@typescript-eslint/parser",
parserOptions: {
tsconfigRootDir: __dirname,
project: projects,
extraFileExtensions: [".vue"],
},
plugins: ["@typescript-eslint"],
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
rules: {
...baseRules,
...tsRules,
...tsRulesTemp,
},
},
{
files: ["**/*.vue"],
parser: "vue-eslint-parser",
parserOptions: {
ecmaVersion: 2022,
ecmaFeatures: {
jsx: true,
},
parser: "@typescript-eslint/parser",
tsconfigRootDir: __dirname,
project: projects,
},
plugins: ["vue"],
extends: [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
],
rules: {...baseRules, ...tsRules, ...tsRulesTemp, ...vueRules},
},
{
files: ["./tests/**/*.ts"],
parser: "@typescript-eslint/parser",
rules: {
...baseRules,
...tsRules,
...tsRulesTemp,
...tsTestRulesTemp,
},
},
],
env: {
es6: true,
browser: true,
mocha: true,
node: true,
},
extends: ["eslint:recommended", "prettier"],
rules: baseRules,
});

View file

@ -1,105 +0,0 @@
---
root: true
parserOptions:
ecmaVersion: 2017
env:
es6: true
browser: true
mocha: true
node: true
rules:
arrow-body-style: error
arrow-parens: [error, always]
arrow-spacing: error
block-scoped-var: error
block-spacing: [error, always]
brace-style: [error, 1tbs]
comma-dangle:
- error
- always-multiline
curly: [error, all]
dot-location: [error, property]
dot-notation: error
eol-last: error
eqeqeq: error
handle-callback-err: error
indent: [error, tab]
key-spacing: [error, {beforeColon: false, afterColon: true}]
keyword-spacing: [error, {before: true, after: true}]
linebreak-style: [error, unix]
no-alert: error
no-catch-shadow: error
no-confusing-arrow: [error, {allowParens: true}]
no-control-regex: off
no-console: error
no-duplicate-imports: error
no-else-return: error
no-implicit-globals: error
no-multi-spaces: error
no-multiple-empty-lines: [error, { "max": 1 }]
no-shadow: error
no-template-curly-in-string: error
no-trailing-spaces: error
no-unsafe-negation: error
no-useless-computed-key: error
no-useless-constructor: error
no-useless-return: error
no-use-before-define: [error, {functions: false}]
no-var: error
object-curly-spacing: [error, never]
object-shorthand:
- error
- methods
- avoidExplicitReturnArrows: true
padded-blocks: [error, never]
padding-line-between-statements:
- error
- blankLine: always
prev:
- block
- block-like
next: "*"
- blankLine: always
prev: "*"
next:
- block
- block-like
prefer-const: error
prefer-rest-params: error
prefer-spread: error
quote-props: [error, consistent-as-needed]
quotes: [error, double, avoid-escape]
rest-spread-spacing: error
semi-spacing: error
semi-style: [error, last]
semi: [error, always]
space-before-blocks: error
space-before-function-paren:
- error
- anonymous: never
named: never
asyncArrow: always # Otherwise requires `async()`
space-in-parens: [error, never]
space-infix-ops: error
spaced-comment: [error, always]
strict: off
template-curly-spacing: error
yoda: error
vue/html-indent: [error, tab]
vue/require-default-prop: off
vue/no-v-html: off
vue/no-use-v-if-with-v-for: off
vue/html-closing-bracket-newline: error
vue/multiline-html-element-content-newline: off
vue/singleline-html-element-content-newline: off
plugins:
- vue
extends:
- eslint:recommended
- plugin:vue/recommended

View file

@ -28,6 +28,10 @@ your contributions.
Pope's guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
- Each PR will be reviewed by at least two different project maintainers. You
can read more about this in the [maintainers'
corner](https://github.com/thelounge/thelounge/wiki/Maintainers'-corner).
corner](https://github.com/thelounge/thelounge/wiki/Maintainers'-corner).
- Please document any relevant changes in the documentation that can be found
[in its own repository](https://github.com/thelounge/thelounge.chat).
- Note that we use prettier on the project. You can set up IDE plugins to format
on save ([see VS Code one here](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)).
- We have a git hook to automatically run prettier before commit, in case you don't install the plugin.
- If for any reason, prettier does not work for you, you can run `yarn format:prettier` and that should format everything.

View file

@ -1,15 +1,14 @@
---
name: Bug Report
about: Create a bug report
labels: "Type: Bug"
---
<!-- Have a question? Join #thelounge on freenode -->
<!-- Have a question? Join #thelounge on Libera.Chat -->
* *Node version:*
* *Browser version:*
* *Device, operating system:*
* *The Lounge version:*
- _Node version:_
- _Browser version:_
- _Device, operating system:_
- _The Lounge version:_
---

View file

@ -1,10 +1,10 @@
---
name: Feature Request
about: Request a new feature
labels: "Type: Feature"
---
<!-- Have a question? Join #thelounge on freenode. -->
<!-- Have a question? Join #thelounge on Libera.Chat. -->
<!-- Make sure to check the existing issues prior to submitting your suggestion. -->
### Feature Description

16
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,16 @@
contact_links:
- name: Docker container issues
url: https://github.com/thelounge/thelounge-docker/issues
about: Report issues related to the Docker container here
- name: Debian package issues
url: https://github.com/thelounge/thelounge-deb/issues
about: Report issues related to the Debian package here
- name: Arch Linux package issues
url: https://github.com/thelounge/thelounge-archlinux/issues
about: Report issues related to the Arch Linux package here
- name: General support
url: https://demo.thelounge.chat/?join=%23thelounge
about: "Join #thelounge on Libera.Chat to ask a question before creating an issue"

2
.github/SUPPORT.md vendored
View file

@ -6,6 +6,6 @@ need help, you have a few options:
- Check out [existing questions on Stack Overflow](https://stackoverflow.com/questions/tagged/thelounge)
to see if yours has been answered before. If not, feel free to [ask for a new question](https://stackoverflow.com/questions/ask?tags=thelounge)
(using `thelounge` tag so that other people can easily find it).
- Find us on the Freenode channel `#thelounge`. You might not get an answer
- Find us on the Libera.Chat channel `#thelounge`. You might not get an answer
right away, but this channel is full of nice people who will be happy to
help you.

48
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Build
permissions:
contents: read
on: [push, pull_request]
jobs:
build:
name: Node ${{ matrix.node_version }} on ${{ matrix.os }}
strategy:
matrix:
include:
# EOL: April 2025
- os: macOS-latest
node_version: 18.x
- os: windows-latest
node_version: 18.x
- os: ubuntu-latest
node_version: 18.x
# EOL: April 2026
- os: ubuntu-latest
node_version: 20.x
# EOL: April June 2024
- os: ubuntu-latest
node_version: 21.x
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@master
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node_version }}
- name: Install
run: yarn --frozen-lockfile --non-interactive
- name: Build
run: yarn build
env:
NODE_ENV: production
- name: Test
run: yarn test

53
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: Release
permissions:
contents: read
id-token: write
on:
push:
tags: v*
jobs:
release:
name: Release workflow
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "latest"
registry-url: "https://registry.npmjs.org/"
- name: Install
run: yarn --frozen-lockfile --non-interactive
- name: Build
run: yarn build
env:
NODE_ENV: production
- name: Test
run: yarn test
- name: Publish latest
if: "!contains(github.ref, '-')"
run: npm publish --tag latest --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- name: Publish next
if: contains(github.ref, '-')
run: npm publish --tag next --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
- name: Remove next tag
if: "!contains(github.ref, '-')"
run: npm dist-tag rm thelounge next || true
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ package-lock.json
coverage/
public/
dist/

View file

@ -1,22 +0,0 @@
# This file must not contain generated assets listed in .gitignore.
# npm-debug.log and node_modules/ are ignored by default.
# See https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package
# Ignore all dot files except for .thelounge_home
.*
!.thelounge_home
# Ignore client folder as it's being built into public/ folder
# except for the specified files which are used by the server
client/**
!client/js/libs/handlebars/ircmessageparser/findLinks.js
!client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js
!client/index.html.tpl
public/js/bundle.vendor.js.map
coverage/
scripts/
test/
appveyor.yml
webpack.config*.js
renovate.json

28
.prettierignore Normal file
View file

@ -0,0 +1,28 @@
coverage/
public/
dist/
test/fixtures/.thelounge/logs/
test/fixtures/.thelounge/certificates/
test/fixtures/.thelounge/storage/
test/fixtures/.thelounge/sts-policies.json
*.log
*.png
*.svg
*.ico
*.wav
*.tpl
*.sh
*.opts
*.txt
yarn.lock
.gitignore
.npmrc
.npmignore
.prettierignore
.thelounge_home
.editorconfig
.eslintignore
.gitattributes
.browserslistrc
*.css

View file

@ -1,11 +0,0 @@
extends: stylelint-config-standard
ignoreFiles:
- client/css/bootstrap.css
rules:
indentation: tab
# complains about FontAwesome
font-family-no-missing-generic-family-keyword:
# needs a lot of refactoring to be enabled
no-descending-specificity:

View file

@ -1,71 +0,0 @@
language: node_js
# https://github.com/nodejs/Release
# Specify current LTS version here, which is used for publishing to npm
node_js:
- 10 # EOL: April 2021
os:
- linux
# https://github.com/nodejs/Release
matrix:
fast_finish: true
include:
- name: "Windows build"
os: windows
cache: false # windows cache uploads are slow
env: YARN_GPG=no # starts gpg-agent that never exits
- name: "macOS build"
os: osx
# Version used to deploy to npm registry
- name: "Production build"
env: BUILD_ENV=production
# Next node version and minimum supported node version
- node_js: 11 # EOL: June 2019
- node_js: 8.10.0 # EOL: December 2019 (test exact LTS version)
cache: yarn
before_script:
- NODE_ENV=$BUILD_ENV yarn build
install:
- yarn --frozen-lockfile --non-interactive --network-timeout 300000
notifications:
email:
on_success: never
on_failure: always
# Identifies `a.b.c-xxx.n` tags as pre-releases, and `a.b.c` as stable releases
before_deploy: |
function npm_dist_tag() {
if [[ "$TRAVIS_TAG" = *"-"* ]]; then
echo "next"
else
echo "latest"
fi
}
deploy:
skip_cleanup: true # prevent git stash --all which nukes node_modules folder
provider: npm
tag: $(npm_dist_tag)
email:
secure: 0EZsBJAc9XjdEgvG0g2+UnF6DnB+pOfuTUGg83SJBSzpHiI/fPNRw/LTmvrba3yq3kjS32BfMVrPLKDPIHFSuNgfzxu7w1V3IhmbkMcHHu62o8aG8SDlEs58OuctcSYTYU+oeZY392pjB/kLNerLgPC/IeuHEcE/Os+VFPoFFTYbHAigbiGsRMlNAv3Da5xDpHeemn3B5c+b6l8tS9urSX28ThHHh883VRTd1Bb3ioBQ4C5dPa35Uk+2eV9MLswSMb4YAfZLB4R6jiUl3KAIZ87wbfcZon6/sqOyMx25XqWMG/Y3ygay73esXPyHMpJ/3kenRx7hPR1xoyfmTfyuUBi5k05jHRh2xmaBvFfQOjscvqYu0+7DrweF7dK0Yyy1A+ImCovMPJk5bIOjhFbA7lXQefyOW5CW3wJBKDFa8a/X6Ptdtsd6b3GPkSwa3mZw0u1S4xSDepmYq5XAVr+rIu8wgySahNkWMYzl6TSG8gQ6rvSI82DBf8lYOhSpNo7tFXFZqll2VVYcolhDymbwVe3CLzNZ3l62J1+oOsCPkr8Zf5Mx+BU0fXOHdQTpT/3xwj5kjkgQZcreBqUD49p5X00jgLifXQxJ2iy0VlwkGfZxeM8QrSGApUgO2s6KZkARWQDPD8L4MDyE4oiSZRUohT4N/dn1xqOvZm/eq8PnXFo=
api_key:
secure: AMz3r6oUv71mcTwpjVWc13AJvoIdhCB5zZQly88VMz1kxEgEkhXsugC2E5UOBKE+u45xGypbDoZIMt/raJfPTirZ9emTnl9Q3USc+V74RJ9RmsGrmCF9Kyr6ZdOuhifCLgQGzPK1U3IDp0S2EC6TRMD4x/bPjTakQYoE+XiQGji5p1j1Fjff2jyiJo396CYSR2dRgfG0h1Uz8ilK6AeSQ6iErMqOUvKpYnmlmsa4h/qGCpbb2XQtnzRLpFNYFA264lXNUh3on3DmvKH5qlw5NYJ6hl4ZUNzIk4uNPD2BklHg1l7U6sTWXUk3VLI86GyymCHef29Ry47cKXXNCY0pR3r+ptOm9OxWvtS/8pZv/XFzRq/oCtEk27DWUc/NHJiv/+7uKXSAeSZ0OqDCNxLXfre0nFtzcrXZ5aV4aspjbKrbYZj10gef4q5/OzTFJOxRifPDjvpxnACwGsgZaqei/grRNHkVkHci1IRn56Vj7oKuFJemmckJXi/QuozXf72oYYfi4LUamdfNu/5i5tKV/cj4TFsB+sOt/by9qxPPo/YXkGKTrdoqSshLX0tKyf69zS8Bmp/mb768a1vrxZRco0EajP4YzzoNMjnWpgjFikTNOJ2DzuySdSjVcU2d0OLge7OBtui9yaYA68ETNaA0uhQVxmBOb/Ujt9OGAqrhskU=
on:
condition: "$BUILD_ENV = production"
tags: true
repo: thelounge/thelounge
# If the current release is a stable release, remove potential pre-release tag
after_deploy: |
if [ "$(npm_dist_tag)" == "latest" ]; then
npm dist-tag rm thelounge next || true
fi

10
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,10 @@
{
"recommendations": [
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin"
],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"configurations": [
{
"type": "node-terminal",
"name": "Run Dev",
"request": "launch",
"command": "yarn dev",
"cwd": "${workspaceFolder}"
}
]
}

10
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,10 @@
{
"editor.formatOnSave": true,
"prettier.useEditorConfig": true,
"prettier.requireConfig": true,
"prettier.disableLanguages": [],
"eslint.packageManager": "yarn",
"eslint.codeActionsOnSave.mode": "all",
"[typescript]": {"editor.defaultFormatter": "esbenp.prettier-vscode"},
"[vue]": {"editor.defaultFormatter": "esbenp.prettier-vscode"}
}

File diff suppressed because it is too large Load diff

View file

@ -16,25 +16,20 @@
<a href="https://thelounge.chat/docs">Docs</a>
<a href="https://demo.thelounge.chat/">Demo</a>
<a href="https://github.com/thelounge/thelounge-docker">Docker</a>
</strong>
</p>
<p align="center">
<a href="https://demo.thelounge.chat/"><img
alt="#thelounge IRC channel on freenode"
src="https://img.shields.io/badge/freenode-%23thelounge-415364.svg?colorA=ff9e18&style=flat-square"></a>
<br>
alt="#thelounge IRC channel on Libera.Chat"
src="https://img.shields.io/badge/Libera.Chat-%23thelounge-415364.svg?colorA=ff9e18"></a>
<a href="https://yarn.pm/thelounge"><img
alt="npm version"
src="https://img.shields.io/npm/v/thelounge.svg?style=flat-square&maxAge=3600"></a>
<a href="https://travis-ci.com/thelounge/thelounge"><img
alt="Travis CI Build Status"
src="https://img.shields.io/travis/com/thelounge/thelounge/master.svg?style=flat-square&maxAge=60"></a>
<a href="https://david-dm.org/thelounge/thelounge"><img
alt="Dependencies Status"
src="https://img.shields.io/david/thelounge/thelounge.svg?style=flat-square&maxAge=3600"></a>
<a href="https://npm-stat.com/charts.html?package=thelounge&from=2016-02-12"><img
alt="Total downloads on npm"
src="https://img.shields.io/npm/dt/thelounge.svg?colorB=007dc7&style=flat-square&maxAge=3600"></a>
src="https://img.shields.io/npm/v/thelounge.svg?colorA=333a41&maxAge=3600"></a>
<a href="https://github.com/thelounge/thelounge/actions"><img
alt="Build Status"
src="https://github.com/thelounge/thelounge/workflows/Build/badge.svg"></a>
</p>
<p align="center">
@ -43,11 +38,11 @@
## Overview
* **Modern features brought to IRC.** Push notifications, link previews, new message markers, and more bring IRC to the 21st century.
* **Always connected.** Remains connected to IRC servers while you are offline.
* **Cross platform.** It doesn't matter what OS you use, it just works wherever Node.js runs.
* **Responsive interface.** The client works smoothly on every desktop, smartphone and tablet.
* **Synchronized experience.** Always resume where you left off no matter what device.
- **Modern features brought to IRC.** Push notifications, link previews, new message markers, and more bring IRC to the 21st century.
- **Always connected.** Remains connected to IRC servers while you are offline.
- **Cross platform.** It doesn't matter what OS you use, it just works wherever Node.js runs.
- **Responsive interface.** The client works smoothly on every desktop, smartphone and tablet.
- **Synchronized experience.** Always resume where you left off no matter what device.
To learn more about configuration, usage and features of The Lounge, take a look at [the website](https://thelounge.chat).
@ -55,36 +50,13 @@ The Lounge is the official and community-managed fork of [Shout](https://github.
## Installation and usage
The Lounge requires [Node.js](https://nodejs.org/) v8 or more recent.
[Yarn package manager](https://yarnpkg.com/) is also recommended.
The Lounge requires latest [Node.js](https://nodejs.org/) LTS version or more recent.
The [Yarn package manager](https://yarnpkg.com/) is also recommended.
If you want to install with npm, `--unsafe-perm` is required for a correct install.
### Running stable releases using Yarn (recommended)
### Running stable releases
Run this in a terminal to install (or upgrade) the latest stable release from
[npm registry](https://www.npmjs.com/):
```sh
yarn global add thelounge
```
If you already have The Lounge installed globally, use the following command to update it:
```sh
yarn global upgrade thelounge
```
When installation is complete, run:
```sh
thelounge start
```
For more information, read the [usage documentation](https://thelounge.chat/docs/usage) or run:
```sh
thelounge --help
```
Please refer to the [install and upgrade documentation on our website](https://thelounge.chat/docs/install-and-upgrade) for all available installation methods.
### Running from source
@ -111,6 +83,13 @@ fork.
Before submitting any change, make sure to:
- Read the [Contributing instructions](https://github.com/thelounge/thelounge/blob/master/.github/CONTRIBUTING.md#contributing)
- Run `yarn test` to execute linters and test suite
- Run `yarn build` if you change or add anything in `client/js` or `client/views`
- `yarn dev` can be used to start The Lounge and watch for any file changes in the client folder
- Run `yarn test` to execute linters and the test suite
- Run `yarn format:prettier` if linting fails
- Run `yarn build:client` if you change or add anything in `client/js` or `client/components`
- The built files will be output to `public/` by webpack
- Run `yarn build:server` if you change anything in `server/`
- The built files will be output to `dist/` by tsc
- `yarn dev` can be used to start The Lounge with hot module reloading
To ensure that you don't commit files that fail the linting, you can install a pre-commit git hook.
Execute `yarn githooks-install` to do so.

View file

@ -4,6 +4,6 @@
- Contact us privately first, in a
[responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure)
manner.
- On IRC, send a private message to any voiced user on our Freenode channel,
- On IRC, send a private message to any voiced user on our Libera.Chat channel,
`#thelounge`.
- By email, send us your report at <security@thelounge.chat>.

4
babel.config.cjs Normal file
View file

@ -0,0 +1,4 @@
module.exports = {
presets: [["@babel/preset-env", {bugfixes: true}], "babel-preset-typescript-vue3"],
plugins: ["@babel/plugin-transform-runtime"],
};

View file

@ -1,160 +1,195 @@
<template>
<div
id="viewport"
role="tablist"
>
<aside id="sidebar">
<div class="scrollable-area">
<div class="logo-container">
<img
:src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg.svg`"
class="logo"
alt="The Lounge"
>
<img
:src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg-inverted.svg`"
class="logo-inverted"
alt="The Lounge"
>
</div>
<NetworkList
:networks="networks"
:active-channel="activeChannel"
/>
</div>
<footer id="footer">
<span
class="tooltipped tooltipped-n tooltipped-no-touch"
aria-label="Sign in"
><button
class="icon sign-in"
data-target="#sign-in"
aria-label="Sign in"
role="tab"
aria-controls="sign-in"
aria-selected="false"
/></span>
<span
class="tooltipped tooltipped-n tooltipped-no-touch"
aria-label="Connect to network"
><button
class="icon connect"
data-target="#connect"
aria-label="Connect to network"
role="tab"
aria-controls="connect"
aria-selected="false"
/></span>
<span
class="tooltipped tooltipped-n tooltipped-no-touch"
aria-label="Settings"
><button
class="icon settings"
data-target="#settings"
aria-label="Settings"
role="tab"
aria-controls="settings"
aria-selected="false"
/></span>
<span
class="tooltipped tooltipped-n tooltipped-no-touch"
aria-label="Help"
><button
class="icon help"
data-target="#help"
aria-label="Help"
role="tab"
aria-controls="help"
aria-selected="false"
/></span>
</footer>
</aside>
<div id="sidebar-overlay" />
<article id="windows">
<Chat
v-if="activeChannel"
:network="activeChannel.network"
:channel="activeChannel.channel"
/>
<div
id="sign-in"
class="window"
role="tabpanel"
aria-label="Sign-in"
/>
<div
id="connect"
class="window"
role="tabpanel"
aria-label="Connect"
/>
<div
id="settings"
class="window"
role="tabpanel"
aria-label="Settings"
/>
<div
id="help"
class="window"
role="tabpanel"
aria-label="Help"
/>
<div
id="changelog"
class="window"
aria-label="Changelog"
/>
</article>
<div id="viewport" :class="viewportClasses" role="tablist">
<Sidebar v-if="store.state.appLoaded" :overlay="overlay" />
<div
id="sidebar-overlay"
ref="overlay"
aria-hidden="true"
@click="store.commit('sidebarOpen', false)"
/>
<router-view ref="loungeWindow"></router-view>
<Mentions />
<ImageViewer ref="imageViewer" />
<ContextMenu ref="contextMenu" />
<ConfirmDialog ref="confirmDialog" />
<div id="upload-overlay"></div>
</div>
</template>
<script>
const throttle = require("lodash/throttle");
<script lang="ts">
import constants from "../js/constants";
import eventbus from "../js/eventbus";
import Mousetrap, {ExtendedKeyboardEvent} from "mousetrap";
import throttle from "lodash/throttle";
import storage from "../js/localStorage";
import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind";
import NetworkList from "./NetworkList.vue";
import Chat from "./Chat.vue";
import Sidebar from "./Sidebar.vue";
import ImageViewer from "./ImageViewer.vue";
import ContextMenu from "./ContextMenu.vue";
import ConfirmDialog from "./ConfirmDialog.vue";
import Mentions from "./Mentions.vue";
import {
computed,
provide,
defineComponent,
onBeforeUnmount,
onMounted,
ref,
Ref,
InjectionKey,
} from "vue";
import {useStore} from "../js/store";
import type {DebouncedFunc} from "lodash";
export default {
export const imageViewerKey = Symbol() as InjectionKey<Ref<typeof ImageViewer | null>>;
const contextMenuKey = Symbol() as InjectionKey<Ref<typeof ContextMenu | null>>;
const confirmDialogKey = Symbol() as InjectionKey<Ref<typeof ConfirmDialog | null>>;
export default defineComponent({
name: "App",
components: {
NetworkList,
Chat,
Sidebar,
ImageViewer,
ContextMenu,
ConfirmDialog,
Mentions,
},
props: {
activeChannel: Object,
networks: Array,
},
mounted() {
// Make a single throttled resize listener available to all components
this.debouncedResize = throttle(() => {
this.$root.$emit("resize");
}, 100);
setup() {
const store = useStore();
const overlay = ref(null);
const loungeWindow = ref(null);
const imageViewer = ref(null);
const contextMenu = ref(null);
const confirmDialog = ref(null);
window.addEventListener("resize", this.debouncedResize, {passive: true});
provide(imageViewerKey, imageViewer);
provide(contextMenuKey, contextMenu);
provide(confirmDialogKey, confirmDialog);
// Emit a daychange event every time the day changes so date markers know when to update themselves
const emitDayChange = () => {
this.$root.$emit("daychange");
// This should always be 24h later but re-computing exact value just in case
this.dayChangeTimeout = setTimeout(emitDayChange, this.msUntilNextDay());
const viewportClasses = computed(() => {
return {
notified: store.getters.highlightCount > 0,
"menu-open": store.state.appLoaded && store.state.sidebarOpen,
"menu-dragging": store.state.sidebarDragging,
"userlist-open": store.state.userlistOpen,
};
});
const debouncedResize = ref<DebouncedFunc<() => void>>();
const dayChangeTimeout = ref<any>();
const escapeKey = () => {
eventbus.emit("escapekey");
};
this.dayChangeTimeout = setTimeout(emitDayChange, this.msUntilNextDay());
},
beforeDestroy() {
window.removeEventListener("resize", this.debouncedResize);
clearTimeout(this.dayChangeTimeout);
},
methods: {
isPublic: () => document.body.classList.contains("public"),
msUntilNextDay() {
const toggleSidebar = (e: ExtendedKeyboardEvent) => {
if (isIgnoredKeybind(e)) {
return true;
}
store.commit("toggleSidebar");
return false;
};
const toggleUserList = (e: ExtendedKeyboardEvent) => {
if (isIgnoredKeybind(e)) {
return true;
}
store.commit("toggleUserlist");
return false;
};
const toggleMentions = () => {
if (store.state.networks.length !== 0) {
eventbus.emit("mentions:toggle");
}
};
const msUntilNextDay = () => {
// Compute how many milliseconds are remaining until the next day starts
const today = new Date();
const tommorow = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
const tommorow = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate() + 1
).getTime();
return tommorow - today;
},
return tommorow - today.getTime();
};
const prepareOpenStates = () => {
const viewportWidth = window.innerWidth;
let isUserlistOpen = storage.get("thelounge.state.userlist");
if (viewportWidth > constants.mobileViewportPixels) {
store.commit("sidebarOpen", storage.get("thelounge.state.sidebar") !== "false");
}
// If The Lounge is opened on a small screen (less than 1024px), and we don't have stored
// user list state, close it by default
if (viewportWidth >= 1024 && isUserlistOpen !== "true" && isUserlistOpen !== "false") {
isUserlistOpen = "true";
}
store.commit("userlistOpen", isUserlistOpen === "true");
};
prepareOpenStates();
onMounted(() => {
Mousetrap.bind("esc", escapeKey);
Mousetrap.bind("alt+u", toggleUserList);
Mousetrap.bind("alt+s", toggleSidebar);
Mousetrap.bind("alt+m", toggleMentions);
debouncedResize.value = throttle(() => {
eventbus.emit("resize");
}, 100);
window.addEventListener("resize", debouncedResize.value, {passive: true});
// Emit a daychange event every time the day changes so date markers know when to update themselves
const emitDayChange = () => {
eventbus.emit("daychange");
// This should always be 24h later but re-computing exact value just in case
dayChangeTimeout.value = setTimeout(emitDayChange, msUntilNextDay());
};
dayChangeTimeout.value = setTimeout(emitDayChange, msUntilNextDay());
});
onBeforeUnmount(() => {
Mousetrap.unbind("esc");
Mousetrap.unbind("alt+u");
Mousetrap.unbind("alt+s");
Mousetrap.unbind("alt+m");
if (debouncedResize.value) {
window.removeEventListener("resize", debouncedResize.value);
}
if (dayChangeTimeout.value) {
clearTimeout(dayChangeTimeout.value);
}
});
return {
viewportClasses,
escapeKey,
toggleSidebar,
toggleUserList,
toggleMentions,
store,
overlay,
loungeWindow,
imageViewer,
contextMenu,
confirmDialog,
};
},
};
});
</script>

View file

@ -1,15 +1,13 @@
<template>
<ChannelWrapper
:network="network"
:channel="channel"
:active-channel="activeChannel"
>
<!-- TODO: investigate -->
<ChannelWrapper ref="wrapper" v-bind="$props">
<span class="name">{{ channel.name }}</span>
<span
v-if="channel.unread"
:class="{ highlight: channel.highlight }"
:class="{highlight: channel.highlight && !channel.muted}"
class="badge"
>{{ channel.unread | roundBadgeNumber }}</span>
>{{ unreadCount }}</span
>
<template v-if="channel.type === 'channel'">
<span
v-if="channel.state === 0"
@ -18,42 +16,50 @@
>
<span class="parted-channel-icon" />
</span>
<span
class="close-tooltip tooltipped tooltipped-w"
aria-label="Leave"
>
<button
class="close"
aria-label="Leave"
/>
<span class="close-tooltip tooltipped tooltipped-w" aria-label="Leave">
<button class="close" aria-label="Leave" @click.stop="close" />
</span>
</template>
<template v-else>
<span
class="close-tooltip tooltipped tooltipped-w"
aria-label="Close"
>
<button
class="close"
aria-label="Close"
/>
<span class="close-tooltip tooltipped tooltipped-w" aria-label="Close">
<button class="close" aria-label="Close" @click.stop="close" />
</span>
</template>
</ChannelWrapper>
</template>
<script>
<script lang="ts">
import {PropType, defineComponent, computed} from "vue";
import roundBadgeNumber from "../js/helpers/roundBadgeNumber";
import useCloseChannel from "../js/hooks/use-close-channel";
import {ClientChan, ClientNetwork} from "../js/types";
import ChannelWrapper from "./ChannelWrapper.vue";
export default {
export default defineComponent({
name: "Channel",
components: {
ChannelWrapper,
},
props: {
activeChannel: Object,
network: Object,
channel: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
channel: {
type: Object as PropType<ClientChan>,
required: true,
},
active: Boolean,
isFiltering: Boolean,
},
};
setup(props) {
const unreadCount = computed(() => roundBadgeNumber(props.channel.unread));
const close = useCloseChannel(props.channel);
return {
unreadCount,
close,
};
},
});
</script>

View file

@ -1,55 +1,112 @@
<template>
<!-- TODO: move closed style to it's own class -->
<div
v-if="!network.isCollapsed || channel.highlight || channel.type === 'lobby' || (activeChannel && channel === activeChannel.channel)"
v-if="isChannelVisible"
ref="element"
:class="[
'chan',
channel.type,
{ active: activeChannel && channel === activeChannel.channel },
{ 'parted-channel': channel.type === 'channel' && channel.state === 0 }
'channel-list-item',
{active: active},
{'parted-channel': channel.type === 'channel' && channel.state === 0},
{'has-draft': channel.pendingMessage},
{'has-unread': channel.unread},
{'has-highlight': channel.highlight},
{
'not-secure':
channel.type === 'lobby' && network.status.connected && !network.status.secure,
},
{'not-connected': channel.type === 'lobby' && !network.status.connected},
{'is-muted': channel.muted},
]"
:aria-label="getAriaLabel()"
:title="getAriaLabel()"
:data-id="channel.id"
:data-target="'#chan-' + channel.id"
:data-name="channel.name"
:data-type="channel.type"
:aria-controls="'#chan-' + channel.id"
:aria-selected="activeChannel && channel === activeChannel.channel"
:aria-selected="active"
:style="channel.closed ? {transition: 'none', opacity: 0.4} : undefined"
role="tab"
@click="click"
@contextmenu.prevent="openContextMenu"
>
<slot
:network="network"
:channel="channel"
:activeChannel="activeChannel"
/>
<slot :network="network" :channel="channel" :active-channel="activeChannel" />
</div>
</template>
<script>
export default {
<script lang="ts">
import eventbus from "../js/eventbus";
import isChannelCollapsed from "../js/helpers/isChannelCollapsed";
import {ClientNetwork, ClientChan} from "../js/types";
import {computed, defineComponent, PropType} from "vue";
import {useStore} from "../js/store";
import {switchToChannel} from "../js/router";
export default defineComponent({
name: "ChannelWrapper",
props: {
network: Object,
channel: Object,
activeChannel: Object,
},
methods: {
getAriaLabel() {
const extra = [];
if (this.channel.unread > 0) {
extra.push(`${this.channel.unread} unread`);
}
if (this.channel.highlight > 0) {
extra.push(`${this.channel.highlight} mention`);
}
if (extra.length > 0) {
return `${this.channel.name} (${extra.join(", ")})`;
}
return this.channel.name;
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
channel: {
type: Object as PropType<ClientChan>,
required: true,
},
active: Boolean,
isFiltering: Boolean,
},
};
setup(props) {
const store = useStore();
const activeChannel = computed(() => store.state.activeChannel);
const isChannelVisible = computed(
() => props.isFiltering || !isChannelCollapsed(props.network, props.channel)
);
const getAriaLabel = () => {
const extra: string[] = [];
const type = props.channel.type;
if (props.channel.unread > 0) {
if (props.channel.unread > 1) {
extra.push(`${props.channel.unread} unread messages`);
} else {
extra.push(`${props.channel.unread} unread message`);
}
}
if (props.channel.highlight > 0) {
if (props.channel.highlight > 1) {
extra.push(`${props.channel.highlight} mentions`);
} else {
extra.push(`${props.channel.highlight} mention`);
}
}
return `${type}: ${props.channel.name} ${extra.length ? `(${extra.join(", ")})` : ""}`;
};
const click = () => {
if (props.isFiltering) {
return;
}
switchToChannel(props.channel);
};
const openContextMenu = (event: MouseEvent) => {
eventbus.emit("contextmenu:channel", {
event: event,
channel: props.channel,
network: props.network,
});
};
return {
activeChannel,
isChannelVisible,
getAriaLabel,
click,
openContextMenu,
};
},
});
</script>

View file

@ -1,42 +1,67 @@
<template>
<div
id="chat-container"
class="window"
>
<div id="chat-container" class="window" :data-current-channel="channel.name" lang="">
<div
id="chat"
:data-id="channel.id"
:class="{
'hide-motd': !this.$root.settings.motd,
'colored-nicks': this.$root.settings.coloredNicks,
'show-seconds': this.$root.settings.showSeconds,
'hide-motd': !store.state.settings.motd,
'time-seconds': store.state.settings.showSeconds,
'time-12h': store.state.settings.use12hClock,
'colored-nicks': true, // TODO temporarily fixes themes, to be removed in next major version
}"
>
<div
:id="'chan-' + channel.id"
:class="[channel.type, 'chan', 'active']"
:data-id="channel.id"
class="chat-view"
:data-type="channel.type"
:aria-label="channel.name"
role="tabpanel"
>
<div class="header">
<button
class="lt"
aria-label="Toggle channel list"
/>
<span class="title">{{ channel.name }}</span>
<SidebarToggle />
<span class="title" :aria-label="'Currently open ' + channel.type">{{
channel.name
}}</span>
<div v-if="channel.editTopic === true" class="topic-container">
<input
ref="topicInput"
:value="channel.topic"
class="topic-input"
placeholder="Set channel topic"
enterkeyhint="done"
@keyup.enter="saveTopic"
@keyup.esc="channel.editTopic = false"
/>
<span aria-label="Save topic" class="save-topic" @click="saveTopic">
<span type="button" aria-label="Save topic"></span>
</span>
</div>
<span
v-else
:title="channel.topic"
class="topic"
><ParsedMessage
v-if="channel.topic"
:network="network"
:text="channel.topic"
:class="{topic: true, empty: !channel.topic}"
@dblclick="editTopic"
><ParsedMessage
v-if="channel.topic"
:network="network"
:text="channel.topic"
/></span>
<MessageSearchForm
v-if="
store.state.settings.searchEnabled &&
['channel', 'query'].includes(channel.type)
"
:network="network"
:channel="channel"
/>
<button
class="mentions"
aria-label="Open your mentions"
@click="openMentions"
/>
<button
class="menu"
aria-label="Open the context menu"
@click="openContextMenu"
/>
<span
v-if="channel.type === 'channel'"
@ -46,17 +71,15 @@
<button
class="rt"
aria-label="Toggle user list"
@click="store.commit('toggleUserlist')"
/>
</span>
</div>
<div
v-if="channel.type === 'special'"
class="chat-content"
>
<div v-if="channel.type === 'special'" class="chat-content">
<div class="chat">
<div class="messages">
<div class="msg">
<Component
<component
:is="specialComponent"
:network="network"
:channel="channel"
@ -65,80 +88,187 @@
</div>
</div>
</div>
<div
v-else
class="chat-content"
>
<div v-else class="chat-content">
<div
:class="['scroll-down tooltipped tooltipped-w tooltipped-no-touch', {'scroll-down-shown': !channel.scrolledToBottom}]"
:class="[
'scroll-down tooltipped tooltipped-w tooltipped-no-touch',
{'scroll-down-shown': !channel.scrolledToBottom},
]"
aria-label="Jump to recent messages"
@click="$refs.messageList.jumpToBottom()"
@click="messageList?.jumpToBottom()"
>
<div class="scroll-down-arrow" />
</div>
<ChatUserList v-if="channel.type === 'channel'" :channel="channel" />
<MessageList
ref="messageList"
:network="network"
:channel="channel"
/>
<ChatUserList
v-if="channel.type === 'channel'"
:channel="channel"
:focused="focused"
/>
</div>
</div>
</div>
<div
v-if="this.$root.currentUserVisibleError"
v-if="store.state.currentUserVisibleError"
id="user-visible-error"
@click="hideUserVisibleError"
>{{ this.$root.currentUserVisibleError }}</div>
<span id="upload-progressbar" />
<ChatInput
:network="network"
:channel="channel"
/>
>
{{ store.state.currentUserVisibleError }}
</div>
<ChatInput :network="network" :channel="channel" />
</div>
</template>
<script>
<script lang="ts">
import socket from "../js/socket";
import eventbus from "../js/eventbus";
import ParsedMessage from "./ParsedMessage.vue";
import MessageList from "./MessageList.vue";
import ChatInput from "./ChatInput.vue";
import ChatUserList from "./ChatUserList.vue";
import SidebarToggle from "./SidebarToggle.vue";
import MessageSearchForm from "./MessageSearchForm.vue";
import ListBans from "./Special/ListBans.vue";
import ListInvites from "./Special/ListInvites.vue";
import ListChannels from "./Special/ListChannels.vue";
import ListIgnored from "./Special/ListIgnored.vue";
import {defineComponent, PropType, ref, computed, watch, nextTick, onMounted, Component} from "vue";
import type {ClientNetwork, ClientChan} from "../js/types";
import {useStore} from "../js/store";
import {SpecialChanType, ChanType} from "../../shared/types/chan";
export default {
export default defineComponent({
name: "Chat",
components: {
ParsedMessage,
MessageList,
ChatInput,
ChatUserList,
SidebarToggle,
MessageSearchForm,
},
props: {
network: Object,
channel: Object,
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
focused: Number,
},
computed: {
specialComponent() {
switch (this.channel.special) {
case "list_bans": return ListBans;
case "list_invites": return ListInvites;
case "list_channels": return ListChannels;
case "list_ignored": return ListIgnored;
emits: ["channel-changed"],
setup(props, {emit}) {
const store = useStore();
const messageList = ref<typeof MessageList>();
const topicInput = ref<HTMLInputElement | null>(null);
const specialComponent = computed(() => {
switch (props.channel.special) {
case SpecialChanType.BANLIST:
return ListBans as Component;
case SpecialChanType.INVITELIST:
return ListInvites as Component;
case SpecialChanType.CHANNELLIST:
return ListChannels as Component;
case SpecialChanType.IGNORELIST:
return ListIgnored as Component;
}
return undefined;
},
});
const channelChanged = () => {
// Triggered when active channel is set or changed
emit("channel-changed", props.channel);
socket.emit("open", props.channel.id);
if (props.channel.usersOutdated) {
props.channel.usersOutdated = false;
socket.emit("names", {
target: props.channel.id,
});
}
};
const hideUserVisibleError = () => {
store.commit("currentUserVisibleError", null);
};
const editTopic = () => {
if (props.channel.type === ChanType.CHANNEL) {
props.channel.editTopic = true;
}
};
const saveTopic = () => {
props.channel.editTopic = false;
if (!topicInput.value) {
return;
}
const newTopic = topicInput.value.value;
if (props.channel.topic !== newTopic) {
const target = props.channel.id;
const text = `/raw TOPIC ${props.channel.name} :${newTopic}`;
socket.emit("input", {target, text});
}
};
const openContextMenu = (event: any) => {
eventbus.emit("contextmenu:channel", {
event: event,
channel: props.channel,
network: props.network,
});
};
const openMentions = (event: any) => {
eventbus.emit("mentions:toggle", {
event: event,
});
};
watch(
() => props.channel,
() => {
channelChanged();
}
);
watch(
() => props.channel.editTopic,
(newTopic) => {
if (newTopic) {
void nextTick(() => {
topicInput.value?.focus();
});
}
}
);
onMounted(() => {
channelChanged();
if (props.channel.editTopic) {
void nextTick(() => {
topicInput.value?.focus();
});
}
});
return {
store,
messageList,
topicInput,
specialComponent,
hideUserVisibleError,
editTopic,
saveTopic,
openContextMenu,
openMentions,
};
},
methods: {
hideUserVisibleError() {
this.$root.currentUserVisibleError = null;
},
},
};
});
</script>

View file

@ -1,23 +1,22 @@
<template>
<form
id="form"
method="post"
action=""
@submit.prevent="onSubmit"
>
<form id="form" method="post" action="" @submit.prevent="onSubmit">
<span id="upload-progressbar" />
<span id="nick">{{ network.nick }}</span>
<textarea
id="input"
ref="input"
dir="auto"
class="mousetrap"
enterkeyhint="send"
:value="channel.pendingMessage"
:placeholder="getInputPlaceholder(channel)"
:aria-label="getInputPlaceholder(channel)"
class="mousetrap"
@input="setPendingMessage"
@keypress.enter.exact.prevent="onSubmit"
@blur="onBlur"
/>
<span
v-if="this.$root.isFileUploadEnabled"
v-if="store.state.serverConfiguration?.fileUpload"
id="upload-tooltip"
class="tooltipped tooltipped-w tooltipped-no-touch"
aria-label="Upload file"
@ -27,13 +26,15 @@
id="upload-input"
ref="uploadInput"
type="file"
aria-labelledby="upload"
multiple
>
@change="onUploadInputChange"
/>
<button
id="upload"
type="button"
aria-label="Upload file"
:disabled="!this.$root.isConnected"
:disabled="!store.state.isConnected"
/>
</span>
<span
@ -45,27 +46,33 @@
id="submit"
type="submit"
aria-label="Send message"
:disabled="!this.$root.isConnected"
:disabled="!store.state.isConnected"
/>
</span>
</form>
</template>
<script>
const commands = require("../js/commands/index");
const socket = require("../js/socket");
const upload = require("../js/upload");
const Mousetrap = require("mousetrap");
const {wrapCursor} = require("undate");
<script lang="ts">
import Mousetrap from "mousetrap";
import {wrapCursor} from "undate";
import autocompletion from "../js/autocompletion";
import {commands} from "../js/commands/index";
import socket from "../js/socket";
import upload from "../js/upload";
import eventbus from "../js/eventbus";
import {watch, defineComponent, nextTick, onMounted, PropType, ref, onUnmounted} from "vue";
import type {ClientNetwork, ClientChan} from "../js/types";
import {useStore} from "../js/store";
import {ChanType} from "../../shared/types/chan";
const formattingHotkeys = {
k: "\x03",
b: "\x02",
u: "\x1F",
i: "\x1D",
o: "\x0F",
s: "\x1e",
m: "\x11",
"mod+k": "\x03",
"mod+b": "\x02",
"mod+u": "\x1F",
"mod+i": "\x1D",
"mod+o": "\x0F",
"mod+s": "\x1e",
"mod+m": "\x11",
};
// Autocomplete bracket and quote characters like in a modern IDE
@ -80,155 +87,272 @@ const bracketWraps = {
"*": "*",
"`": "`",
"~": "~",
"_": "_",
_: "_",
};
export default {
export default defineComponent({
name: "ChatInput",
props: {
network: Object,
channel: Object,
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
},
watch: {
"channel.pendingMessage"() {
this.setInputSize();
},
},
mounted() {
if (this.$root.settings.autocomplete) {
require("../js/autocompletion").enable(this.$refs.input);
}
setup(props) {
const store = useStore();
const input = ref<HTMLTextAreaElement>();
const uploadInput = ref<HTMLInputElement>();
const autocompletionRef = ref<ReturnType<typeof autocompletion>>();
const inputTrap = Mousetrap(this.$refs.input);
for (const hotkey in formattingHotkeys) {
inputTrap.bind("mod+" + hotkey, function(e) {
// Key is lowercased because keybinds also get processed if caps lock is on
const modifier = formattingHotkeys[e.key.toLowerCase()];
wrapCursor(
e.target,
modifier,
e.target.selectionStart === e.target.selectionEnd ? "" : modifier
);
return false;
});
}
inputTrap.bind(Object.keys(bracketWraps), function(e) {
if (e.target.selectionStart !== e.target.selectionEnd) {
wrapCursor(e.target, e.key, bracketWraps[e.key]);
return false;
}
});
inputTrap.bind(["up", "down"], (e, key) => {
if (this.$root.isAutoCompleting || e.target.selectionStart !== e.target.selectionEnd) {
return;
}
if (this.channel.inputHistoryPosition === 0) {
this.channel.inputHistory[this.channel.inputHistoryPosition] = this.channel.pendingMessage;
}
if (key === "up") {
if (this.channel.inputHistoryPosition < this.channel.inputHistory.length - 1) {
this.channel.inputHistoryPosition++;
const setInputSize = () => {
void nextTick(() => {
if (!input.value) {
return;
}
} else if (this.channel.inputHistoryPosition > 0) {
this.channel.inputHistoryPosition--;
}
this.channel.pendingMessage = this.$refs.input.value = this.channel.inputHistory[this.channel.inputHistoryPosition];
this.setInputSize();
return false;
});
if (this.$root.isFileUploadEnabled) {
upload.initialize();
}
},
destroyed() {
require("../js/autocompletion").disable();
upload.abort();
},
methods: {
setPendingMessage(e) {
this.channel.pendingMessage = e.target.value;
this.channel.inputHistoryPosition = 0;
this.setInputSize();
},
setInputSize() {
this.$nextTick(() => {
const style = window.getComputedStyle(this.$refs.input);
const lineHeight = parseFloat(style.lineHeight, 10) || 1;
const style = window.getComputedStyle(input.value);
const lineHeight = parseFloat(style.lineHeight) || 1;
// Start by resetting height before computing as scrollHeight does not
// decrease when deleting characters
this.$refs.input.style.height = "";
input.value.style.height = "";
// Use scrollHeight to calculate how many lines there are in input, and ceil the value
// because some browsers tend to incorrently round the values when using high density
// displays or using page zoom feature
this.$refs.input.style.height = Math.ceil(this.$refs.input.scrollHeight / lineHeight) * lineHeight + "px";
input.value.style.height = `${
Math.ceil(input.value.scrollHeight / lineHeight) * lineHeight
}px`;
});
},
getInputPlaceholder(channel) {
if (channel.type === "channel" || channel.type === "query") {
};
const setPendingMessage = (e: Event) => {
props.channel.pendingMessage = (e.target as HTMLInputElement).value;
props.channel.inputHistoryPosition = 0;
setInputSize();
};
const getInputPlaceholder = (channel: ClientChan) => {
if (channel.type === ChanType.CHANNEL || channel.type === ChanType.QUERY) {
return `Write to ${channel.name}`;
}
return "";
},
onSubmit() {
};
const onSubmit = () => {
if (!input.value) {
return;
}
// Triggering click event opens the virtual keyboard on mobile
// This can only be called from another interactive event (e.g. button click)
this.$refs.input.click();
this.$refs.input.focus();
input.value.click();
input.value.focus();
if (!this.$root.isConnected) {
if (!store.state.isConnected) {
return false;
}
const target = this.channel.id;
const text = this.channel.pendingMessage;
const target = props.channel.id;
const text = props.channel.pendingMessage;
if (text.length === 0) {
return false;
}
this.channel.inputHistoryPosition = 0;
this.channel.pendingMessage = "";
this.$refs.input.value = "";
this.setInputSize();
if (autocompletionRef.value) {
autocompletionRef.value.hide();
}
props.channel.inputHistoryPosition = 0;
props.channel.pendingMessage = "";
input.value.value = "";
setInputSize();
// Store new message in history if last message isn't already equal
if (this.channel.inputHistory[1] !== text) {
this.channel.inputHistory.splice(1, 0, text);
if (props.channel.inputHistory[1] !== text) {
props.channel.inputHistory.splice(1, 0, text);
}
// Limit input history to a 100 entries
if (this.channel.inputHistory.length > 100) {
this.channel.inputHistory.pop();
if (props.channel.inputHistory.length > 100) {
props.channel.inputHistory.pop();
}
if (text[0] === "/") {
const args = text.substr(1).split(" ");
const cmd = args.shift().toLowerCase();
const args = text.substring(1).split(" ");
const cmd = args.shift()?.toLowerCase();
if (Object.prototype.hasOwnProperty.call(commands, cmd) && commands[cmd].input(args)) {
if (!cmd) {
return false;
}
if (Object.prototype.hasOwnProperty.call(commands, cmd) && commands[cmd](args)) {
return false;
}
}
socket.emit("input", {target, text});
},
openFileUpload() {
this.$refs.uploadInput.click();
},
};
const onUploadInputChange = () => {
if (!uploadInput.value || !uploadInput.value.files) {
return;
}
const files = Array.from(uploadInput.value.files);
upload.triggerUpload(files);
uploadInput.value.value = ""; // Reset <input> element so you can upload the same file
};
const openFileUpload = () => {
uploadInput.value?.click();
};
const blurInput = () => {
input.value?.blur();
};
const onBlur = () => {
if (autocompletionRef.value) {
autocompletionRef.value.hide();
}
};
watch(
() => props.channel.id,
() => {
if (autocompletionRef.value) {
autocompletionRef.value.hide();
}
}
);
watch(
() => props.channel.pendingMessage,
() => {
setInputSize();
}
);
onMounted(() => {
eventbus.on("escapekey", blurInput);
if (store.state.settings.autocomplete) {
if (!input.value) {
throw new Error("ChatInput autocomplete: input element is not available");
}
autocompletionRef.value = autocompletion(input.value);
}
const inputTrap = Mousetrap(input.value);
inputTrap.bind(Object.keys(formattingHotkeys), function (e, key) {
const modifier = formattingHotkeys[key];
if (!e.target) {
return;
}
wrapCursor(
e.target as HTMLTextAreaElement,
modifier,
(e.target as HTMLTextAreaElement).selectionStart ===
(e.target as HTMLTextAreaElement).selectionEnd
? ""
: modifier
);
return false;
});
inputTrap.bind(Object.keys(bracketWraps), function (e, key) {
if (
(e.target as HTMLTextAreaElement)?.selectionStart !==
(e.target as HTMLTextAreaElement).selectionEnd
) {
wrapCursor(e.target as HTMLTextAreaElement, key, bracketWraps[key]);
return false;
}
});
inputTrap.bind(["up", "down"], (e, key) => {
if (
store.state.isAutoCompleting ||
(e.target as HTMLTextAreaElement).selectionStart !==
(e.target as HTMLTextAreaElement).selectionEnd ||
!input.value
) {
return;
}
const onRow = (
input.value.value.slice(undefined, input.value.selectionStart).match(/\n/g) ||
[]
).length;
const totalRows = (input.value.value.match(/\n/g) || []).length;
const {channel} = props;
if (channel.inputHistoryPosition === 0) {
channel.inputHistory[channel.inputHistoryPosition] = channel.pendingMessage;
}
if (key === "up" && onRow === 0) {
if (channel.inputHistoryPosition < channel.inputHistory.length - 1) {
channel.inputHistoryPosition++;
} else {
return;
}
} else if (
key === "down" &&
channel.inputHistoryPosition > 0 &&
onRow === totalRows
) {
channel.inputHistoryPosition--;
} else {
return;
}
channel.pendingMessage = channel.inputHistory[channel.inputHistoryPosition];
input.value.value = channel.pendingMessage;
setInputSize();
return false;
});
if (store.state.serverConfiguration?.fileUpload) {
upload.mounted();
}
});
onUnmounted(() => {
eventbus.off("escapekey", blurInput);
if (autocompletionRef.value) {
autocompletionRef.value.destroy();
autocompletionRef.value = undefined;
}
upload.unmounted();
upload.abort();
});
return {
store,
input,
uploadInput,
onUploadInputChange,
openFileUpload,
blurInput,
onBlur,
setInputSize,
upload,
getInputPlaceholder,
onSubmit,
setPendingMessage,
};
},
};
});
</script>

View file

@ -2,13 +2,16 @@
<aside
ref="userlist"
class="userlist"
:aria-label="'User list for ' + channel.name"
@mouseleave="removeHoverUser"
>
<div class="count">
<input
ref="input"
:value="userSearchInput"
:placeholder="channel.users.length + ' user' + (channel.users.length === 1 ? '' : 's')"
:placeholder="
channel.users.length + ' user' + (channel.users.length === 1 ? '' : 's')
"
type="search"
class="search"
aria-label="Search among the user list"
@ -19,22 +22,25 @@
@keydown.page-up="navigateUserList($event, -10)"
@keydown.page-down="navigateUserList($event, 10)"
@keydown.enter="selectUser"
>
/>
</div>
<div class="names">
<div
v-for="(users, mode) in groupedUsers"
:key="mode"
:class="['user-mode', getModeClass(mode)]"
:class="['user-mode', getModeClass(String(mode))]"
>
<template v-if="userSearchInput.length > 0">
<UsernameFiltered
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
<Username
v-for="user in users"
:key="user.original.nick"
:key="user.original.nick + '-search'"
:on-hover="hoverUser"
:active="user.original === activeUser"
:user="user"
:user="user.original"
v-html="user.string"
/>
<!-- eslint-enable -->
</template>
<template v-else>
<Username
@ -50,10 +56,12 @@
</aside>
</template>
<script>
const fuzzy = require("fuzzy");
<script lang="ts">
import {filter as fuzzyFilter} from "fuzzy";
import {computed, defineComponent, nextTick, PropType, ref} from "vue";
import type {UserInMessage} from "../../shared/types/msg";
import type {ClientChan, ClientUser} from "../js/types";
import Username from "./Username.vue";
import UsernameFiltered from "./UsernameFiltered.vue";
const modes = {
"~": "owner",
@ -65,116 +73,150 @@ const modes = {
"": "normal",
};
export default {
export default defineComponent({
name: "ChatUserList",
components: {
Username,
UsernameFiltered,
},
props: {
channel: Object,
channel: {type: Object as PropType<ClientChan>, required: true},
},
data() {
return {
userSearchInput: "",
activeUser: null,
};
},
computed: {
// filteredUsers is computed, to avoid unnecessary filtering
// as it is shared between filtering and keybindings.
filteredUsers() {
return fuzzy.filter(
this.userSearchInput,
this.channel.users,
{
pre: "<b>",
post: "</b>",
extract: (u) => u.nick,
}
);
},
groupedUsers() {
const groups = {};
if (this.userSearchInput) {
const result = this.filteredUsers;
for (const user of result) {
if (!groups[user.original.mode]) {
groups[user.original.mode] = [];
}
groups[user.original.mode].push(user);
}
} else {
for (const user of this.channel.users) {
if (!groups[user.mode]) {
groups[user.mode] = [user];
} else {
groups[user.mode].push(user);
}
}
}
return groups;
},
},
methods: {
setUserSearchInput(e) {
this.userSearchInput = e.target.value;
},
getModeClass(mode) {
return modes[mode];
},
selectUser() {
// Simulate a click on the active user to open the context menu.
// Coordinates are provided to position the menu correctly.
if (!this.activeUser) {
setup(props) {
const userSearchInput = ref("");
const activeUser = ref<UserInMessage | null>();
const userlist = ref<HTMLDivElement>();
const filteredUsers = computed(() => {
if (!userSearchInput.value) {
return;
}
return fuzzyFilter(userSearchInput.value, props.channel.users, {
pre: "<b>",
post: "</b>",
extract: (u) => u.nick,
});
});
const groupedUsers = computed(() => {
const groups = {};
if (userSearchInput.value && filteredUsers.value) {
const result = filteredUsers.value;
for (const user of result) {
const mode: string = user.original.modes[0] || "";
if (!groups[mode]) {
groups[mode] = [];
}
// Prepend user mode to search result
user.string = mode + user.string;
groups[mode].push(user);
}
} else {
for (const user of props.channel.users) {
const mode = user.modes[0] || "";
if (!groups[mode]) {
groups[mode] = [user];
} else {
groups[mode].push(user);
}
}
}
return groups as {
[mode: string]: (ClientUser & {
original: UserInMessage;
string: string;
})[];
};
});
const setUserSearchInput = (e: Event) => {
userSearchInput.value = (e.target as HTMLInputElement).value;
};
const getModeClass = (mode: string) => {
return modes[mode] as typeof modes;
};
const selectUser = () => {
// Simulate a click on the active user to open the context menu.
// Coordinates are provided to position the menu correctly.
if (!activeUser.value || !userlist.value) {
return;
}
const el = userlist.value.querySelector(".active");
if (!el) {
return;
}
const el = this.$refs.userlist.querySelector(".active");
const rect = el.getBoundingClientRect();
const ev = new MouseEvent("click", {
view: window,
bubbles: true,
cancelable: true,
clientX: rect.x,
clientY: rect.y + rect.height,
clientX: rect.left,
clientY: rect.top + rect.height,
});
el.dispatchEvent(ev);
},
hoverUser(user) {
this.activeUser = user;
},
removeHoverUser() {
this.activeUser = null;
},
navigateUserList(event, direction) {
};
const hoverUser = (user: UserInMessage) => {
activeUser.value = user;
};
const removeHoverUser = () => {
activeUser.value = null;
};
const scrollToActiveUser = () => {
// Scroll the list if needed after the active class is applied
void nextTick(() => {
const el = userlist.value?.querySelector(".active");
el?.scrollIntoView({block: "nearest", inline: "nearest"});
});
};
const navigateUserList = (event: Event, direction: number) => {
// Prevent propagation to stop global keybind handler from capturing pagedown/pageup
// and redirecting it to the message list container for scrolling
event.stopImmediatePropagation();
event.preventDefault();
let users = this.channel.users;
let users = props.channel.users;
// Only using filteredUsers when we have to avoids filtering when it's not needed
if (this.userSearchInput) {
users = this.filteredUsers.map((result) => result.original);
if (userSearchInput.value && filteredUsers.value) {
users = filteredUsers.value.map((result) => result.original);
}
// Bail out if there's no users to select
if (!users.length) {
this.activeUser = null;
activeUser.value = null;
return;
}
let currentIndex = users.indexOf(this.activeUser);
const abort = () => {
activeUser.value = direction ? users[0] : users[users.length - 1];
scrollToActiveUser();
};
// If there's no active user select the first or last one depending on direction
if (!this.activeUser || currentIndex === -1) {
this.activeUser = direction ? users[0] : users[users.length - 1];
this.scrollToActiveUser();
if (!activeUser.value) {
abort();
return;
}
let currentIndex = users.indexOf(activeUser.value as ClientUser);
if (currentIndex === -1) {
abort();
return;
}
@ -190,16 +232,24 @@ export default {
currentIndex -= users.length;
}
this.activeUser = users[currentIndex];
this.scrollToActiveUser();
},
scrollToActiveUser() {
// Scroll the list if needed after the active class is applied
this.$nextTick(() => {
const el = this.$refs.userlist.querySelector(".active");
el.scrollIntoView({block: "nearest", inline: "nearest"});
});
},
activeUser.value = users[currentIndex];
scrollToActiveUser();
};
return {
filteredUsers,
groupedUsers,
userSearchInput,
activeUser,
userlist,
setUserSearchInput,
getModeClass,
selectUser,
hoverUser,
removeHoverUser,
navigateUserList,
};
},
};
});
</script>

View file

@ -0,0 +1,102 @@
<template>
<div id="confirm-dialog-overlay" :class="{opened: !!data}">
<div v-if="data !== null" id="confirm-dialog">
<div class="confirm-text">
<div class="confirm-text-title">{{ data?.title }}</div>
<p>{{ data?.text }}</p>
</div>
<div class="confirm-buttons">
<button class="btn btn-cancel" @click="close(false)">Cancel</button>
<button class="btn btn-danger" @click="close(true)">{{ data?.button }}</button>
</div>
</div>
</div>
</template>
<style>
#confirm-dialog {
background: var(--body-bg-color);
color: #fff;
margin: 10px;
border-radius: 5px;
max-width: 500px;
}
#confirm-dialog .confirm-text {
padding: 15px;
user-select: text;
}
#confirm-dialog .confirm-text-title {
font-size: 20px;
font-weight: 700;
margin-bottom: 10px;
}
#confirm-dialog .confirm-buttons {
display: flex;
justify-content: flex-end;
padding: 15px;
background: rgba(0, 0, 0, 0.3);
}
#confirm-dialog .confirm-buttons .btn {
margin-bottom: 0;
margin-left: 10px;
}
#confirm-dialog .confirm-buttons .btn-cancel {
border-color: transparent;
}
</style>
<script lang="ts">
import eventbus from "../js/eventbus";
import {defineComponent, onMounted, onUnmounted, ref} from "vue";
type ConfirmDialogData = {
title: string;
text: string;
button: string;
};
type ConfirmDialogCallback = {
(confirmed: boolean): void;
};
export default defineComponent({
name: "ConfirmDialog",
setup() {
const data = ref<ConfirmDialogData>();
const callback = ref<ConfirmDialogCallback>();
const open = (incoming: ConfirmDialogData, cb: ConfirmDialogCallback) => {
data.value = incoming;
callback.value = cb;
};
const close = (result: boolean) => {
data.value = undefined;
if (callback.value) {
callback.value(!!result);
}
};
onMounted(() => {
eventbus.on("escapekey", close);
eventbus.on("confirm-dialog", open);
});
onUnmounted(() => {
eventbus.off("escapekey", close);
eventbus.off("confirm-dialog", open);
});
return {
data,
close,
};
},
});
</script>

View file

@ -0,0 +1,284 @@
<template>
<div
v-if="isOpen"
id="context-menu-container"
:class="{passthrough}"
@click="containerClick"
@contextmenu.prevent="containerClick"
@keydown.exact.up.prevent="navigateMenu(-1)"
@keydown.exact.down.prevent="navigateMenu(1)"
@keydown.exact.tab.prevent="navigateMenu(1)"
@keydown.shift.tab.prevent="navigateMenu(-1)"
>
<ul
id="context-menu"
ref="contextMenu"
role="menu"
:style="{
top: style.top + 'px',
left: style.left + 'px',
}"
tabindex="-1"
@mouseleave="activeItem = -1"
@keydown.enter.prevent="clickActiveItem"
>
<!-- TODO: type -->
<template v-for="(item, id) of (items as any)" :key="item.name">
<li
:class="[
'context-menu-' + item.type,
item.class ? 'context-menu-' + item.class : null,
{active: id === activeItem},
]"
role="menuitem"
@mouseenter="hoverItem(id)"
@click="clickItem(item)"
>
{{ item.label }}
</li>
</template>
</ul>
</div>
</template>
<script lang="ts">
import {
generateUserContextMenu,
generateChannelContextMenu,
generateInlineChannelContextMenu,
ContextMenuItem,
} from "../js/helpers/contextMenu";
import eventbus from "../js/eventbus";
import {defineComponent, nextTick, onMounted, onUnmounted, PropType, ref} from "vue";
import {ClientChan, ClientMessage, ClientNetwork, ClientUser} from "../js/types";
import {useStore} from "../js/store";
import {useRouter} from "vue-router";
export default defineComponent({
name: "ContextMenu",
props: {
message: {
required: false,
type: Object as PropType<ClientMessage>,
},
},
setup() {
const store = useStore();
const router = useRouter();
const isOpen = ref(false);
const passthrough = ref(false);
const contextMenu = ref<HTMLUListElement | null>();
const previousActiveElement = ref<HTMLElement | null>();
const items = ref<ContextMenuItem[]>([]);
const activeItem = ref(-1);
const style = ref({
top: 0,
left: 0,
});
const close = () => {
if (!isOpen.value) {
return;
}
isOpen.value = false;
items.value = [];
if (previousActiveElement.value) {
previousActiveElement.value.focus();
previousActiveElement.value = null;
}
};
const enablePointerEvents = () => {
passthrough.value = false;
document.body.removeEventListener("pointerup", enablePointerEvents);
};
const containerClick = (event: MouseEvent) => {
if (event.currentTarget === event.target) {
close();
}
};
const positionContextMenu = (event: MouseEvent) => {
const element = event.target as HTMLElement;
if (!contextMenu.value) {
return;
}
const menuWidth = contextMenu.value?.offsetWidth;
const menuHeight = contextMenu.value?.offsetHeight;
if (element && element.classList.contains("menu")) {
return {
left: element.getBoundingClientRect().left - (menuWidth - element.offsetWidth),
top: element.getBoundingClientRect().top + element.offsetHeight,
};
}
const offset = {left: event.pageX, top: event.pageY};
if (window.innerWidth - offset.left < menuWidth) {
offset.left = window.innerWidth - menuWidth;
}
if (window.innerHeight - offset.top < menuHeight) {
offset.top = window.innerHeight - menuHeight;
}
return offset;
};
const hoverItem = (id: number) => {
activeItem.value = id;
};
const clickItem = (item: ContextMenuItem) => {
close();
if ("action" in item && item.action) {
item.action();
} else if ("link" in item && item.link) {
router.push(item.link).catch(() => {
// eslint-disable-next-line no-console
console.error("Failed to navigate to", item.link);
});
}
};
const clickActiveItem = () => {
if (items.value[activeItem.value]) {
clickItem(items.value[activeItem.value]);
}
};
const open = (event: MouseEvent, newItems: ContextMenuItem[]) => {
event.preventDefault();
previousActiveElement.value = document.activeElement as HTMLElement;
items.value = newItems;
activeItem.value = 0;
isOpen.value = true;
// Position the menu and set the focus on the first item after it's size has updated
nextTick(() => {
const pos = positionContextMenu(event);
if (!pos) {
return;
}
style.value.left = pos.left;
style.value.top = pos.top;
contextMenu.value?.focus();
}).catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
});
};
const openChannelContextMenu = (data: {
event: MouseEvent;
channel: ClientChan;
network: ClientNetwork;
}) => {
if (data.event.type === "contextmenu") {
// Pass through all pointer events to allow the network list's
// dragging events to continue triggering.
passthrough.value = true;
document.body.addEventListener("pointerup", enablePointerEvents, {
passive: true,
});
}
const newItems = generateChannelContextMenu(data.channel, data.network);
open(data.event, newItems);
};
const openInlineChannelContextMenu = (data: {channel: string; event: MouseEvent}) => {
const {network} = store.state.activeChannel;
const newItems = generateInlineChannelContextMenu(store, data.channel, network);
open(data.event, newItems);
};
const openUserContextMenu = (data: {
user: Pick<ClientUser, "nick" | "modes">;
event: MouseEvent;
}) => {
const {network, channel} = store.state.activeChannel;
const newItems = generateUserContextMenu(
store,
channel,
network,
channel.users.find((u) => u.nick === data.user.nick) || {
nick: data.user.nick,
modes: [],
}
);
open(data.event, newItems);
};
const navigateMenu = (direction: number) => {
let currentIndex = activeItem.value;
currentIndex += direction;
const nextItem = items.value[currentIndex];
// If the next item we would select is a divider, skip over it
if (nextItem && "type" in nextItem && nextItem.type === "divider") {
currentIndex += direction;
}
if (currentIndex < 0) {
currentIndex += items.value.length;
}
if (currentIndex > items.value.length - 1) {
currentIndex -= items.value.length;
}
activeItem.value = currentIndex;
};
onMounted(() => {
eventbus.on("escapekey", close);
eventbus.on("contextmenu:cancel", close);
eventbus.on("contextmenu:user", openUserContextMenu);
eventbus.on("contextmenu:channel", openChannelContextMenu);
eventbus.on("contextmenu:inline-channel", openInlineChannelContextMenu);
});
onUnmounted(() => {
eventbus.off("escapekey", close);
eventbus.off("contextmenu:cancel", close);
eventbus.off("contextmenu:user", openUserContextMenu);
eventbus.off("contextmenu:channel", openChannelContextMenu);
eventbus.off("contextmenu:inline-channel", openInlineChannelContextMenu);
close();
});
return {
isOpen,
items,
activeItem,
style,
contextMenu,
passthrough,
close,
containerClick,
navigateMenu,
hoverItem,
clickItem,
clickActiveItem,
};
},
});
</script>

View file

@ -1,58 +1,66 @@
<template>
<div
:aria-label="localeDate"
class="date-marker-container tooltipped tooltipped-s"
>
<div :aria-label="localeDate" class="date-marker-container tooltipped tooltipped-s">
<div class="date-marker">
<span
:data-label="friendlyDate()"
class="date-marker-text"
/>
<span :aria-label="friendlyDate()" class="date-marker-text" />
</div>
</div>
</template>
<script>
const moment = require("moment");
<script lang="ts">
import dayjs from "dayjs";
import calendar from "dayjs/plugin/calendar";
import {computed, defineComponent, onBeforeUnmount, onMounted, PropType} from "vue";
import eventbus from "../js/eventbus";
import type {ClientMessage} from "../js/types";
export default {
dayjs.extend(calendar);
export default defineComponent({
name: "DateMarker",
props: {
message: Object,
},
computed: {
localeDate() {
return moment(this.message.time).format("D MMMM YYYY");
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
focused: Boolean,
},
mounted() {
if (this.hoursPassed() < 48) {
this.$root.$on("daychange", this.dayChange);
}
},
beforeDestroy() {
this.$root.$off("daychange", this.dayChange);
},
methods: {
hoursPassed() {
return (Date.now() - Date.parse(this.message.time)) / 3600000;
},
dayChange() {
this.$forceUpdate();
setup(props) {
const localeDate = computed(() => dayjs(props.message.time).format("D MMMM YYYY"));
if (this.hoursPassed() >= 48) {
this.$root.$off("daychange", this.dayChange);
const hoursPassed = () => {
return (Date.now() - Date.parse(props.message.time.toString())) / 3600000;
};
const dayChange = () => {
if (hoursPassed() >= 48) {
eventbus.off("daychange", dayChange);
}
},
friendlyDate() {
};
const friendlyDate = () => {
// See http://momentjs.com/docs/#/displaying/calendar-time/
return moment(this.message.time).calendar(null, {
return dayjs(props.message.time).calendar(null, {
sameDay: "[Today]",
lastDay: "[Yesterday]",
lastWeek: "D MMMM YYYY",
sameElse: "D MMMM YYYY",
});
},
};
onMounted(() => {
if (hoursPassed() < 48) {
eventbus.on("daychange", dayChange);
}
});
onBeforeUnmount(() => {
eventbus.off("daychange", dayChange);
});
return {
localeDate,
friendlyDate,
};
},
};
});
</script>

View file

@ -0,0 +1,120 @@
<template>
<div ref="containerRef" :class="$props.class">
<slot
v-for="(item, index) of list"
:key="item[itemKey]"
:element="item"
:index="index"
name="item"
></slot>
</div>
</template>
<script lang="ts">
import {defineComponent, ref, PropType, watch, onUnmounted, onBeforeUnmount} from "vue";
import Sortable from "sortablejs";
const Props = {
delay: {
type: Number,
default: 0,
required: false,
},
delayOnTouchOnly: {
type: Boolean,
default: false,
required: false,
},
touchStartThreshold: {
type: Number,
default: 10,
required: false,
},
handle: {
type: String,
default: "",
required: false,
},
draggable: {
type: String,
default: "",
required: false,
},
ghostClass: {
type: String,
default: "",
required: false,
},
dragClass: {
type: String,
default: "",
required: false,
},
group: {
type: String,
default: "",
required: false,
},
class: {
type: String,
default: "",
required: false,
},
itemKey: {
type: String,
default: "",
required: true,
},
list: {
type: Array as PropType<any[]>,
default: [],
required: true,
},
filter: {
type: String,
default: "",
required: false,
},
};
export default defineComponent({
name: "Draggable",
props: Props,
emits: ["change", "choose", "unchoose"],
setup(props, {emit}) {
const containerRef = ref<HTMLElement | null>(null);
const sortable = ref<Sortable | null>(null);
watch(containerRef, (newDraggable) => {
if (newDraggable) {
sortable.value = new Sortable(newDraggable, {
...props,
onChoose(event) {
emit("choose", event);
},
onUnchoose(event) {
emit("unchoose", event);
},
onEnd(event) {
emit("change", event);
},
});
}
});
onBeforeUnmount(() => {
if (sortable.value) {
sortable.value.destroy();
containerRef.value = null;
}
});
return {
containerRef,
};
},
});
</script>

View file

@ -0,0 +1,478 @@
<template>
<div
id="image-viewer"
ref="viewer"
:class="{opened: link !== null}"
@wheel="onMouseWheel"
@touchstart.passive="onTouchStart"
@click="onClick"
>
<template v-if="link !== null">
<button class="close-btn" aria-label="Close"></button>
<button
v-if="previousImage"
class="previous-image-btn"
aria-label="Previous image"
@click.stop="previous"
></button>
<button
v-if="nextImage"
class="next-image-btn"
aria-label="Next image"
@click.stop="next"
></button>
<a class="open-btn" :href="link.link" target="_blank" rel="noopener"></a>
<img
ref="image"
:src="link.thumb"
alt=""
:style="computeImageStyles"
@load="onImageLoad"
@mousedown="onImageMouseDown"
@touchstart.passive="onImageTouchStart"
/>
</template>
</div>
</template>
<script lang="ts">
import Mousetrap from "mousetrap";
import {computed, defineComponent, ref, watch} from "vue";
import eventbus from "../js/eventbus";
import {ClientChan, ClientLinkPreview} from "../js/types";
import {SharedMsg} from "../../shared/types/msg";
export default defineComponent({
name: "ImageViewer",
setup() {
const viewer = ref<HTMLDivElement>();
const image = ref<HTMLImageElement>();
const link = ref<ClientLinkPreview | null>(null);
const previousImage = ref<ClientLinkPreview | null>();
const nextImage = ref<ClientLinkPreview | null>();
const channel = ref<ClientChan | null>();
const position = ref<{
x: number;
y: number;
}>({
x: 0,
y: 0,
});
const transform = ref<{
scale: number;
x: number;
y: number;
}>({
scale: 1,
x: 0,
y: 0,
});
const computeImageStyles = computed(() => {
// Sub pixels may cause the image to blur in certain browsers
// round it down to prevent that
const transformX = Math.floor(transform.value.x);
const transformY = Math.floor(transform.value.y);
return {
left: `${position.value.x}px`,
top: `${position.value.y}px`,
transform: `translate3d(${transformX}px, ${transformY}px, 0) scale3d(${transform.value.scale}, ${transform.value.scale}, 1)`,
};
});
const closeViewer = () => {
if (link.value === null) {
return;
}
channel.value = null;
previousImage.value = null;
nextImage.value = null;
link.value = null;
};
const setPrevNextImages = () => {
if (!channel.value || !link.value) {
return null;
}
const links = channel.value.messages
.map((msg: SharedMsg) => msg.previews)
.flat()
.filter((preview) => preview && preview.thumb);
const currentIndex = links.indexOf(link.value);
previousImage.value = links[currentIndex - 1] || null;
nextImage.value = links[currentIndex + 1] || null;
};
const previous = () => {
if (previousImage.value) {
link.value = previousImage.value;
}
};
const next = () => {
if (nextImage.value) {
link.value = nextImage.value;
}
};
const prepareImage = () => {
const viewerEl = viewer.value;
const imageEl = image.value;
if (!viewerEl || !imageEl) {
return;
}
const width = viewerEl.offsetWidth;
const height = viewerEl.offsetHeight;
const scale = Math.min(1, width / imageEl.width, height / imageEl.height);
position.value.x = Math.floor(-image.value!.naturalWidth / 2);
position.value.y = Math.floor(-image.value!.naturalHeight / 2);
transform.value.scale = Math.max(scale, 0.1);
transform.value.x = width / 2;
transform.value.y = height / 2;
};
const onImageLoad = () => {
prepareImage();
};
const calculateZoomShift = (newScale: number, x: number, y: number, oldScale: number) => {
if (!image.value || !viewer.value) {
return;
}
const imageWidth = image.value.width;
const centerX = viewer.value.offsetWidth / 2;
const centerY = viewer.value.offsetHeight / 2;
return {
x:
centerX -
((centerX - (y - (imageWidth * x) / 2)) / x) * newScale +
(imageWidth * newScale) / 2,
y:
centerY -
((centerY - (oldScale - (imageWidth * x) / 2)) / x) * newScale +
(imageWidth * newScale) / 2,
};
};
const correctPosition = () => {
const imageEl = image.value;
const viewerEl = viewer.value;
if (!imageEl || !viewerEl) {
return;
}
const widthScaled = imageEl.width * transform.value.scale;
const heightScaled = imageEl.height * transform.value.scale;
const containerWidth = viewerEl.offsetWidth;
const containerHeight = viewerEl.offsetHeight;
if (widthScaled < containerWidth) {
transform.value.x = containerWidth / 2;
} else if (transform.value.x - widthScaled / 2 > 0) {
transform.value.x = widthScaled / 2;
} else if (transform.value.x + widthScaled / 2 < containerWidth) {
transform.value.x = containerWidth - widthScaled / 2;
}
if (heightScaled < containerHeight) {
transform.value.y = containerHeight / 2;
} else if (transform.value.y - heightScaled / 2 > 0) {
transform.value.y = heightScaled / 2;
} else if (transform.value.y + heightScaled / 2 < containerHeight) {
transform.value.y = containerHeight - heightScaled / 2;
}
};
// Reduce multiple touch points into a single x/y/scale
const reduceTouches = (touches: TouchList) => {
let totalX = 0;
let totalY = 0;
let totalScale = 0;
for (let i = 0; i < touches.length; i++) {
const x = touches[i].clientX;
const y = touches[i].clientY;
totalX += x;
totalY += y;
for (let i2 = 0; i2 < touches.length; i2++) {
if (i !== i2) {
const x2 = touches[i2].clientX;
const y2 = touches[i2].clientY;
totalScale += Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
}
}
}
if (totalScale === 0) {
totalScale = 1;
}
return {
x: totalX / touches.length,
y: totalY / touches.length,
scale: totalScale / touches.length,
};
};
const onTouchStart = (e: TouchEvent) => {
// prevent sidebar touchstart event, we don't want to interact with sidebar while in image viewer
e.stopImmediatePropagation();
};
// Touch image manipulation:
// 1. Move around by dragging it with one finger
// 2. Change image scale by using two fingers
const onImageTouchStart = (e: TouchEvent) => {
const img = image.value;
let touch = reduceTouches(e.touches);
let currentTouches = e.touches;
let touchEndFingers = 0;
const currentTransform = {
x: touch.x,
y: touch.y,
scale: touch.scale,
};
const startTransform = {
x: transform.value.x,
y: transform.value.y,
scale: transform.value.scale,
};
const touchMove = (moveEvent) => {
touch = reduceTouches(moveEvent.touches);
if (currentTouches.length !== moveEvent.touches.length) {
currentTransform.x = touch.x;
currentTransform.y = touch.y;
currentTransform.scale = touch.scale;
startTransform.x = transform.value.x;
startTransform.y = transform.value.y;
startTransform.scale = transform.value.scale;
}
const deltaX = touch.x - currentTransform.x;
const deltaY = touch.y - currentTransform.y;
const deltaScale = touch.scale / currentTransform.scale;
currentTouches = moveEvent.touches;
touchEndFingers = 0;
const newScale = Math.min(3, Math.max(0.1, startTransform.scale * deltaScale));
const fixedPosition = calculateZoomShift(
newScale,
startTransform.scale,
startTransform.x,
startTransform.y
);
if (!fixedPosition) {
return;
}
transform.value.x = fixedPosition.x + deltaX;
transform.value.y = fixedPosition.y + deltaY;
transform.value.scale = newScale;
correctPosition();
};
const touchEnd = (endEvent: TouchEvent) => {
const changedTouches = endEvent.changedTouches.length;
if (currentTouches.length > changedTouches + touchEndFingers) {
touchEndFingers += changedTouches;
return;
}
// todo: this is swipe to close, but it's not working very well due to unfinished delta calculation
/* if (
transform.value.scale <= 1 &&
endEvent.changedTouches[0].clientY - startTransform.y <= -70
) {
return this.closeViewer();
}*/
correctPosition();
img?.removeEventListener("touchmove", touchMove);
img?.removeEventListener("touchend", touchEnd);
};
img?.addEventListener("touchmove", touchMove, {passive: true});
img?.addEventListener("touchend", touchEnd, {passive: true});
};
// Image mouse manipulation:
// 1. Mouse wheel scrolling will zoom in and out
// 2. If image is zoomed in, simply dragging it will move it around
const onImageMouseDown = (e: MouseEvent) => {
// todo: ignore if in touch event currently?
// only left mouse
// TODO: e.buttons?
if (e.which !== 1) {
return;
}
e.stopPropagation();
e.preventDefault();
const viewerEl = viewer.value;
const imageEl = image.value;
if (!viewerEl || !imageEl) {
return;
}
const startX = e.clientX;
const startY = e.clientY;
const startTransformX = transform.value.x;
const startTransformY = transform.value.y;
const widthScaled = imageEl.width * transform.value.scale;
const heightScaled = imageEl.height * transform.value.scale;
const containerWidth = viewerEl.offsetWidth;
const containerHeight = viewerEl.offsetHeight;
const centerX = transform.value.x - widthScaled / 2;
const centerY = transform.value.y - heightScaled / 2;
let movedDistance = 0;
const mouseMove = (moveEvent: MouseEvent) => {
moveEvent.stopPropagation();
moveEvent.preventDefault();
const newX = moveEvent.clientX - startX;
const newY = moveEvent.clientY - startY;
movedDistance = Math.max(movedDistance, Math.abs(newX), Math.abs(newY));
if (centerX < 0 || widthScaled + centerX > containerWidth) {
transform.value.x = startTransformX + newX;
}
if (centerY < 0 || heightScaled + centerY > containerHeight) {
transform.value.y = startTransformY + newY;
}
correctPosition();
};
const mouseUp = (upEvent: MouseEvent) => {
correctPosition();
if (movedDistance < 2 && upEvent.button === 0) {
closeViewer();
}
image.value?.removeEventListener("mousemove", mouseMove);
image.value?.removeEventListener("mouseup", mouseUp);
};
image.value?.addEventListener("mousemove", mouseMove);
image.value?.addEventListener("mouseup", mouseUp);
};
// If image is zoomed in, holding ctrl while scrolling will move the image up and down
const onMouseWheel = (e: WheelEvent) => {
// if image viewer is closing (css animation), you can still trigger mousewheel
// TODO: Figure out a better fix for this
if (link.value === null) {
return;
}
e.preventDefault(); // TODO: Can this be passive?
if (e.ctrlKey) {
transform.value.y += e.deltaY;
} else {
const delta = e.deltaY > 0 ? 0.1 : -0.1;
const newScale = Math.min(3, Math.max(0.1, transform.value.scale + delta));
const fixedPosition = calculateZoomShift(
newScale,
transform.value.scale,
transform.value.x,
transform.value.y
);
if (!fixedPosition) {
return;
}
transform.value.scale = newScale;
transform.value.x = fixedPosition.x;
transform.value.y = fixedPosition.y;
}
correctPosition();
};
const onClick = (e: Event) => {
// If click triggers on the image, ignore it
if (e.target === image.value) {
return;
}
closeViewer();
};
watch(link, (newLink, oldLink) => {
// TODO: history.pushState
if (newLink === null) {
eventbus.off("escapekey", closeViewer);
eventbus.off("resize", correctPosition);
Mousetrap.unbind("left");
Mousetrap.unbind("right");
return;
}
setPrevNextImages();
if (!oldLink) {
eventbus.on("escapekey", closeViewer);
eventbus.on("resize", correctPosition);
Mousetrap.bind("left", previous);
Mousetrap.bind("right", next);
}
});
return {
link,
channel,
image,
transform,
closeViewer,
next,
previous,
onImageLoad,
onImageMouseDown,
onMouseWheel,
onClick,
onTouchStart,
previousImage,
nextImage,
onImageTouchStart,
computeImageStyles,
viewer,
};
},
});
</script>

View file

@ -0,0 +1,35 @@
<template>
<span
class="inline-channel"
dir="auto"
role="button"
tabindex="0"
@click.prevent="openContextMenu"
@contextmenu.prevent="openContextMenu"
><slot></slot
></span>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import eventbus from "../js/eventbus";
export default defineComponent({
name: "InlineChannel",
props: {
channel: String,
},
setup(props) {
const openContextMenu = (event) => {
eventbus.emit("contextmenu:inline-channel", {
event: event,
channel: props.channel,
});
};
return {
openContextMenu,
};
},
});
</script>

View file

@ -5,7 +5,7 @@
method="post"
action=""
autocomplete="off"
@keydown.esc.prevent="$emit('toggleJoinChannel')"
@keydown.esc.prevent="$emit('toggle-join-channel')"
@submit.prevent="onSubmit"
>
<input
@ -19,7 +19,7 @@
maxlength="200"
title="The channel name may not contain spaces"
required
>
/>
<input
v-model="inputPassword"
type="password"
@ -30,55 +30,64 @@
maxlength="200"
title="The channel password may not contain spaces"
autocomplete="new-password"
>
<button
type="submit"
class="btn btn-small"
>Join</button>
/>
<button type="submit" class="btn btn-small">Join</button>
</form>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType, ref} from "vue";
import {switchToChannel} from "../js/router";
import socket from "../js/socket";
import {useStore} from "../js/store";
import {ClientNetwork, ClientChan} from "../js/types";
export default {
export default defineComponent({
name: "JoinChannel",
directives: {
focus: {
inserted(el) {
el.focus();
},
mounted: (el: HTMLFormElement) => el.focus(),
},
},
props: {
network: Object,
channel: Object,
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
},
data() {
return {
inputChannel: "",
inputPassword: "",
};
},
methods: {
onSubmit() {
const channelToFind = this.inputChannel.toLowerCase();
const existingChannel = this.network.channels.find((c) => c.name.toLowerCase() === channelToFind);
emits: ["toggle-join-channel"],
setup(props, {emit}) {
const store = useStore();
const inputChannel = ref("");
const inputPassword = ref("");
const onSubmit = () => {
const existingChannel = store.getters.findChannelOnCurrentNetwork(inputChannel.value);
if (existingChannel) {
const $ = require("jquery");
$(`#sidebar .chan[data-id="${existingChannel.id}"]`).trigger("click");
switchToChannel(existingChannel);
} else {
const chanTypes = props.network.serverOptions.CHANTYPES;
let channel = inputChannel.value;
if (chanTypes && chanTypes.length > 0 && !chanTypes.includes(channel[0])) {
channel = chanTypes[0] + channel;
}
socket.emit("input", {
text: `/join ${this.inputChannel} ${this.inputPassword}`,
target: this.channel.id,
text: `/join ${channel} ${inputPassword.value}`,
target: props.channel.id,
});
}
this.inputChannel = "";
this.inputPassword = "";
this.$emit("toggleJoinChannel");
},
inputChannel.value = "";
inputPassword.value = "";
emit("toggle-join-channel");
};
return {
inputChannel,
inputPassword,
onSubmit,
};
},
};
});
</script>

View file

@ -1,21 +1,24 @@
<template>
<div
v-if="link.shown"
v-show="link.canDisplay"
v-show="link.sourceLoaded || link.type === 'link'"
ref="container"
class="preview"
dir="ltr"
>
<div
ref="content"
:class="['toggle-content', 'toggle-type-' + link.type, { opened: isContentShown }]"
:class="['toggle-content', 'toggle-type-' + link.type, {opened: isContentShown}]"
>
<template v-if="link.type === 'link'">
<a
v-if="link.thumb"
v-show="link.sourceLoaded"
:href="link.link"
class="toggle-thumbnail"
target="_blank"
rel="noopener"
@click="onThumbnailClick"
>
<img
:src="link.thumb"
@ -25,9 +28,9 @@
@error="onThumbnailError"
@abort="onThumbnailError"
@load="onPreviewReady"
>
/>
</a>
<div class="toggle-text">
<div class="toggle-text" dir="auto">
<div class="head">
<div class="overflowable">
<a
@ -35,25 +38,26 @@
:title="link.head"
target="_blank"
rel="noopener"
>{{ link.head }}</a>
>{{ link.head }}</a
>
</div>
<button
v-if="showMoreButton"
:aria-expanded="isContentShown"
:aria-label="moreButtonLabel"
dir="auto"
class="more"
@click="onMoreClick"
><span class="more-caret" /></button>
>
<span class="more-caret" />
</button>
</div>
<div class="body overflowable">
<a
:href="link.link"
:title="link.body"
target="_blank"
rel="noopener"
>{{ link.body }}</a>
<a :href="link.link" :title="link.body" target="_blank" rel="noopener">{{
link.body
}}</a>
</div>
</div>
</template>
@ -63,62 +67,51 @@
class="toggle-thumbnail"
target="_blank"
rel="noopener"
@click="onThumbnailClick"
>
<img
v-show="link.sourceLoaded"
:src="link.thumb"
decoding="async"
alt=""
@load="onPreviewReady"
>
/>
</a>
</template>
<template v-else-if="link.type === 'video'">
<video
v-show="link.sourceLoaded"
preload="metadata"
controls
@canplay="onPreviewReady"
>
<source
:src="link.media"
:type="link.mediaType"
>
<source :src="link.media" :type="link.mediaType" />
</video>
</template>
<template v-else-if="link.type === 'audio'">
<audio
v-show="link.sourceLoaded"
controls
preload="metadata"
@canplay="onPreviewReady"
>
<source
:src="link.media"
:type="link.mediaType"
>
<source :src="link.media" :type="link.mediaType" />
</audio>
</template>
<template v-else-if="link.type === 'error'">
<em v-if="link.error === 'image-too-big'">
This image is larger than {{ link.maxSize | friendlysize }} and cannot be
previewed.
<a
:href="link.link"
target="_blank"
rel="noopener"
>Click here</a>
This image is larger than {{ imageMaxSize }} and cannot be previewed.
<a :href="link.link" target="_blank" rel="noopener">Click here</a>
to open it in a new window.
</em>
<template v-else-if="link.error === 'message'">
<div>
<em>
A preview could not be loaded.
<a
:href="link.link"
target="_blank"
rel="noopener"
>Click here</a>
<a :href="link.link" target="_blank" rel="noopener">Click here</a>
to open it in a new window.
</em>
<br>
<br />
<pre class="prefetch-error">{{ link.message }}</pre>
</div>
@ -127,121 +120,210 @@
:aria-label="moreButtonLabel"
class="more"
@click="onMoreClick"
><span class="more-caret" /></button>
>
<span class="more-caret" />
</button>
</template>
</template>
</div>
</div>
</template>
<script>
export default {
<script lang="ts">
import {
computed,
defineComponent,
inject,
nextTick,
onBeforeUnmount,
onMounted,
onUnmounted,
PropType,
ref,
watch,
} from "vue";
import {onBeforeRouteUpdate} from "vue-router";
import eventbus from "../js/eventbus";
import friendlysize from "../js/helpers/friendlysize";
import {useStore} from "../js/store";
import type {ClientChan, ClientLinkPreview} from "../js/types";
import {imageViewerKey} from "./App.vue";
export default defineComponent({
name: "LinkPreview",
props: {
link: Object,
keepScrollPosition: Function,
},
data() {
return {
showMoreButton: false,
isContentShown: false,
};
},
computed: {
moreButtonLabel() {
return this.isContentShown ? "Less" : "More";
link: {
type: Object as PropType<ClientLinkPreview>,
required: true,
},
},
watch: {
"link.type"() {
this.updateShownState();
this.onPreviewUpdate();
keepScrollPosition: {
type: Function as PropType<() => void>,
required: true,
},
channel: {type: Object as PropType<ClientChan>, required: true},
},
created() {
this.updateShownState();
},
mounted() {
this.$root.$on("resize", this.handleResize);
setup(props) {
const store = useStore();
this.onPreviewUpdate();
},
beforeDestroy() {
this.$root.$off("resize", this.handleResize);
},
destroyed() {
// Let this preview go through load/canplay events again,
// Otherwise the browser can cause a resize on video elements
this.link.canDisplay = false;
},
methods: {
onPreviewUpdate() {
// Don't display previews while they are loading on the server
if (this.link.type === "loading") {
const showMoreButton = ref(false);
const isContentShown = ref(false);
const imageViewer = inject(imageViewerKey);
onBeforeRouteUpdate((to, from, next) => {
// cancel the navigation if the user is trying to close the image viewer
if (imageViewer?.value?.link) {
imageViewer.value.closeViewer();
return next(false);
}
next();
});
const content = ref<HTMLDivElement | null>(null);
const container = ref<HTMLDivElement | null>(null);
const moreButtonLabel = computed(() => {
return isContentShown.value ? "Less" : "More";
});
const imageMaxSize = computed(() => {
if (!props.link.maxSize) {
return;
}
// Error don't have any media to render
if (this.link.type === "error") {
this.onPreviewReady();
}
return friendlysize(props.link.maxSize);
});
// If link doesn't have a thumbnail, render it
if (this.link.type === "link" && !this.link.thumb) {
this.onPreviewReady();
}
},
onPreviewReady() {
this.$set(this.link, "canDisplay", true);
this.keepScrollPosition();
if (this.link.type !== "link") {
return;
}
this.handleResize();
},
onThumbnailError() {
// If thumbnail fails to load, hide it and show the preview without it
this.link.thumb = "";
this.onPreviewReady();
},
onMoreClick() {
this.isContentShown = !this.isContentShown;
this.keepScrollPosition();
},
handleResize() {
this.$nextTick(() => {
if (!this.$refs.content) {
const handleResize = () => {
nextTick(() => {
if (!content.value || !container.value) {
return;
}
this.showMoreButton = this.$refs.content.offsetWidth >= this.$refs.container.offsetWidth;
showMoreButton.value = content.value.offsetWidth >= container.value.offsetWidth;
}).catch((e) => {
// eslint-disable-next-line no-console
console.error("Error in LinkPreview.handleResize", e);
});
},
updateShownState() {
let defaultState = true;
};
switch (this.link.type) {
case "error":
defaultState = this.link.error === "image-too-big" ? this.$root.settings.media : this.$root.settings.links;
break;
const onPreviewReady = () => {
props.link.sourceLoaded = true;
case "loading":
defaultState = false;
break;
props.keepScrollPosition();
case "link":
defaultState = this.$root.settings.links;
break;
if (props.link.type === "link") {
handleResize();
}
};
default:
defaultState = this.$root.settings.media;
const onPreviewUpdate = () => {
// Don't display previews while they are loading on the server
if (props.link.type === "loading") {
return;
}
this.link.shown = this.link.shown && defaultState;
},
// Error does not have any media to render
if (props.link.type === "error") {
onPreviewReady();
}
// If link doesn't have a thumbnail, render it
if (props.link.type === "link") {
handleResize();
props.keepScrollPosition();
}
};
const onThumbnailError = () => {
// If thumbnail fails to load, hide it and show the preview without it
props.link.thumb = "";
onPreviewReady();
};
const onThumbnailClick = (e: MouseEvent) => {
e.preventDefault();
if (!imageViewer?.value) {
return;
}
imageViewer.value.channel = props.channel;
imageViewer.value.link = props.link;
};
const onMoreClick = () => {
isContentShown.value = !isContentShown.value;
props.keepScrollPosition();
};
const updateShownState = () => {
// User has manually toggled the preview, do not apply default
if (props.link.shown !== null) {
return;
}
let defaultState = false;
switch (props.link.type) {
case "error":
// Collapse all errors by default unless its a message about image being too big
if (props.link.error === "image-too-big") {
defaultState = store.state.settings.media;
}
break;
case "link":
defaultState = store.state.settings.links;
break;
default:
defaultState = store.state.settings.media;
}
props.link.shown = defaultState;
};
updateShownState();
watch(
() => props.link.type,
() => {
updateShownState();
onPreviewUpdate();
}
);
onMounted(() => {
eventbus.on("resize", handleResize);
onPreviewUpdate();
});
onBeforeUnmount(() => {
eventbus.off("resize", handleResize);
});
onUnmounted(() => {
// Let this preview go through load/canplay events again,
// Otherwise the browser can cause a resize on video elements
props.link.sourceLoaded = false;
});
return {
moreButtonLabel,
imageMaxSize,
onThumbnailClick,
onThumbnailError,
onMoreClick,
onPreviewReady,
onPreviewUpdate,
showMoreButton,
isContentShown,
content,
container,
};
},
};
});
</script>

View file

@ -0,0 +1,22 @@
<template>
<span class="preview-size">({{ previewSize }})</span>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import friendlysize from "../js/helpers/friendlysize";
export default defineComponent({
name: "LinkPreviewFileSize",
props: {
size: {type: Number, required: true},
},
setup(props) {
const previewSize = friendlysize(props.size);
return {
previewSize,
};
},
});
</script>

View file

@ -1,29 +1,37 @@
<template>
<button
v-if="link.type !== 'loading'"
:class="['toggle-button', 'toggle-preview', { opened: link.shown }]"
:class="['toggle-button', 'toggle-preview', {opened: link.shown}]"
:aria-label="ariaLabel"
@click="onClick"
/>
</template>
<script>
export default {
<script lang="ts">
import {computed, defineComponent, PropType} from "vue";
import {ClientMessage, ClientLinkPreview} from "../js/types";
export default defineComponent({
name: "LinkPreviewToggle",
props: {
link: Object,
link: {type: Object as PropType<ClientLinkPreview>, required: true},
message: {type: Object as PropType<ClientMessage>, required: true},
},
computed: {
ariaLabel() {
return this.link.shown ? "Collapse preview" : "Expand preview";
},
},
methods: {
onClick() {
this.link.shown = !this.link.shown;
emits: ["toggle-link-preview"],
setup(props, {emit}) {
const ariaLabel = computed(() => {
return props.link.shown ? "Collapse preview" : "Expand preview";
});
this.$parent.$emit("linkPreviewToggle", this.link, this.$parent.message);
},
const onClick = () => {
props.link.shown = !props.link.shown;
emit("toggle-link-preview", props.link, props.message);
};
return {
ariaLabel,
onClick,
};
},
};
});
</script>

View file

@ -0,0 +1,247 @@
<template>
<div
v-if="isOpen"
id="mentions-popup-container"
@click="containerClick"
@contextmenu="containerClick"
>
<div class="mentions-popup">
<div class="mentions-popup-title">
Recent mentions
<button
v-if="resolvedMessages.length"
class="btn dismiss-all-mentions"
@click="dismissAllMentions()"
>
Dismiss all
</button>
</div>
<template v-if="resolvedMessages.length === 0">
<p v-if="isLoading">Loading</p>
<p v-else>You have no recent mentions.</p>
</template>
<template v-for="message in resolvedMessages" v-else :key="message.msgId">
<div :class="['msg', message.type]">
<div class="mentions-info">
<div>
<span class="from">
<Username :user="(message.from as any)" />
<template v-if="message.channel">
in {{ message.channel.channel.name }} on
{{ message.channel.network.name }}
</template>
<template v-else> in unknown channel </template> </span
>{{ ` ` }}
<span :title="message.localetime" class="time">
{{ messageTime(message.time.toString()) }}
</span>
</div>
<div>
<span
class="close-tooltip tooltipped tooltipped-w"
aria-label="Dismiss this mention"
>
<button
class="msg-dismiss"
aria-label="Dismiss this mention"
@click="dismissMention(message)"
></button>
</span>
</div>
</div>
<div class="content" dir="auto">
<ParsedMessage :message="(message as any)" />
</div>
</div>
</template>
</div>
</div>
</template>
<style>
#mentions-popup-container {
z-index: 8;
}
.mentions-popup {
background-color: var(--window-bg-color);
position: absolute;
width: 400px;
right: 80px;
top: 55px;
max-height: 400px;
overflow-y: auto;
z-index: 2;
padding: 10px;
}
.mentions-popup > .mentions-popup-title {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-size: 20px;
}
.mentions-popup .mentions-info {
display: flex;
justify-content: space-between;
}
.mentions-popup .msg {
margin-bottom: 15px;
user-select: text;
}
.mentions-popup .msg:last-child {
margin-bottom: 0;
}
.mentions-popup .msg .content {
background-color: var(--highlight-bg-color);
border-radius: 5px;
padding: 6px;
margin-top: 2px;
word-wrap: break-word;
word-break: break-word; /* Webkit-specific */
}
.mentions-popup .msg-dismiss::before {
font-size: 20px;
font-weight: normal;
display: inline-block;
line-height: 16px;
text-align: center;
content: "×";
}
.mentions-popup .msg-dismiss:hover {
color: var(--link-color);
}
.mentions-popup .dismiss-all-mentions {
margin: 0;
padding: 4px 6px;
}
@media (min-height: 500px) {
.mentions-popup {
max-height: 60vh;
}
}
@media (max-width: 768px) {
.mentions-popup {
border-radius: 0;
border: 0;
box-shadow: none;
width: 100%;
max-height: none;
right: 0;
left: 0;
bottom: 0;
top: 45px; /* header height */
}
}
</style>
<script lang="ts">
import Username from "./Username.vue";
import ParsedMessage from "./ParsedMessage.vue";
import socket from "../js/socket";
import eventbus from "../js/eventbus";
import localetime from "../js/helpers/localetime";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import {computed, watch, defineComponent, ref, onMounted, onUnmounted} from "vue";
import {useStore} from "../js/store";
import {ClientMention} from "../js/types";
dayjs.extend(relativeTime);
export default defineComponent({
name: "Mentions",
components: {
Username,
ParsedMessage,
},
setup() {
const store = useStore();
const isOpen = ref(false);
const isLoading = ref(false);
const resolvedMessages = computed(() => {
const messages = store.state.mentions.slice().reverse();
for (const message of messages) {
message.localetime = localetime(message.time);
message.channel = store.getters.findChannel(message.chanId);
}
return messages.filter((message) => !message.channel?.channel.muted);
});
watch(
() => store.state.mentions,
() => {
isLoading.value = false;
}
);
const messageTime = (time: string) => {
return dayjs(time).fromNow();
};
const dismissMention = (message: ClientMention) => {
store.state.mentions.splice(
store.state.mentions.findIndex((m) => m.msgId === message.msgId),
1
);
socket.emit("mentions:dismiss", message.msgId);
};
const dismissAllMentions = () => {
store.state.mentions = [];
socket.emit("mentions:dismiss_all");
};
const containerClick = (event: Event) => {
if (event.currentTarget === event.target) {
isOpen.value = false;
}
};
const togglePopup = () => {
isOpen.value = !isOpen.value;
if (isOpen.value) {
isLoading.value = true;
socket.emit("mentions:get");
}
};
const closePopup = () => {
isOpen.value = false;
};
onMounted(() => {
eventbus.on("mentions:toggle", togglePopup);
eventbus.on("escapekey", closePopup);
});
onUnmounted(() => {
eventbus.off("mentions:toggle", togglePopup);
eventbus.off("escapekey", closePopup);
});
return {
isOpen,
isLoading,
resolvedMessages,
messageTime,
dismissMention,
dismissAllMentions,
containerClick,
};
},
});
</script>

View file

@ -1,120 +1,173 @@
<template>
<div
:id="'msg-' + message.id"
:class="['msg', message.type, {self: message.self, highlight: message.highlight}]"
:class="[
'msg',
{
self: message.self,
highlight: message.highlight || focused,
'previous-source': isPreviousSource,
},
]"
:data-type="message.type"
:data-command="message.command"
:data-from="message.from && message.from.nick"
>
<span
:aria-label="message.time | localetime"
aria-hidden="true"
:aria-label="messageTimeLocale"
class="time tooltipped tooltipped-e"
>{{ messageTime }} </span>
>{{ `${messageTime}&#32;` }}
</span>
<template v-if="message.type === 'unhandled'">
<span class="from">[{{ message.command }}]</span>
<span class="content">
<span
v-for="(param, id) in message.params"
:key="id"
>{{ param }} </span>
<span v-for="(param, id) in message.params" :key="id">{{
`&#32;${param}&#32;`
}}</span>
</span>
</template>
<template v-else-if="isAction()">
<span class="from"><span class="only-copy">*** </span></span>
<Component
:is="messageComponent"
:network="network"
:message="message"
/>
<span class="from"><span class="only-copy">***&nbsp;</span></span>
<component :is="messageComponent" :network="network" :message="message" />
</template>
<template v-else-if="message.type === 'action'">
<span class="from"><span class="only-copy">* </span></span>
<span class="content">
<Username :user="message.from" />&#32;<ParsedMessage
<span class="from"><span class="only-copy">*&nbsp;</span></span>
<span class="content" dir="auto">
<Username
:user="message.from"
:network="network"
:message="message"
/>
:channel="channel"
dir="auto"
/>&#32;<ParsedMessage :message="message" />
<LinkPreview
v-for="preview in message.previews"
:key="preview.link"
:keep-scroll-position="keepScrollPosition"
:link="preview"
:channel="channel"
/>
</span>
</template>
<template v-else>
<span
v-if="message.type === 'message'"
class="from"
>
<span v-if="message.type === 'message'" class="from">
<template v-if="message.from && message.from.nick">
<span class="only-copy">&lt;</span>
<Username :user="message.from" />
<span class="only-copy">&gt; </span>
<span class="only-copy" aria-hidden="true">&lt;</span>
<Username :user="message.from" :network="network" :channel="channel" />
<span class="only-copy" aria-hidden="true">&gt;&nbsp;</span>
</template>
</span>
<span
v-else
class="from"
>
<span v-else-if="message.type === 'plugin'" class="from">
<template v-if="message.from && message.from.nick">
<span class="only-copy">-</span>
<Username :user="message.from" />
<span class="only-copy">- </span>
<span class="only-copy" aria-hidden="true">[</span>
{{ message.from.nick }}
<span class="only-copy" aria-hidden="true">]&nbsp;</span>
</template>
</span>
<span class="content">
<ParsedMessage
:network="network"
:message="message"
/>
<span v-else class="from">
<template v-if="message.from && message.from.nick">
<span class="only-copy" aria-hidden="true">-</span>
<Username :user="message.from" :network="network" :channel="channel" />
<span class="only-copy" aria-hidden="true">-&nbsp;</span>
</template>
</span>
<span class="content" dir="auto">
<span
v-if="message.showInActive"
aria-label="This message was shown in your active channel"
class="msg-shown-in-active tooltipped tooltipped-e"
><span></span
></span>
<span
v-if="message.statusmsgGroup"
:aria-label="`This message was only shown to users with ${message.statusmsgGroup} mode`"
class="msg-statusmsg tooltipped tooltipped-e"
><span>{{ message.statusmsgGroup }}</span></span
>
<ParsedMessage :network="network" :message="message" />
<LinkPreview
v-for="preview in message.previews"
:key="preview.link"
:keep-scroll-position="keepScrollPosition"
:link="preview"
:channel="channel"
/>
</span>
</template>
</div>
</template>
<script>
<script lang="ts">
import {computed, defineComponent, PropType} from "vue";
import dayjs from "dayjs";
import constants from "../js/constants";
import localetime from "../js/helpers/localetime";
import Username from "./Username.vue";
import LinkPreview from "./LinkPreview.vue";
import ParsedMessage from "./ParsedMessage.vue";
import MessageTypes from "./MessageTypes";
const moment = require("moment");
const constants = require("../js/constants");
import type {ClientChan, ClientMessage, ClientNetwork} from "../js/types";
import {useStore} from "../js/store";
MessageTypes.ParsedMessage = ParsedMessage;
MessageTypes.LinkPreview = LinkPreview;
MessageTypes.Username = Username;
export default {
export default defineComponent({
name: "Message",
components: MessageTypes,
props: {
message: Object,
network: Object,
keepScrollPosition: Function,
message: {type: Object as PropType<ClientMessage>, required: true},
channel: {type: Object as PropType<ClientChan>, required: false},
network: {type: Object as PropType<ClientNetwork>, required: true},
keepScrollPosition: Function as PropType<() => void>,
isPreviousSource: Boolean,
focused: Boolean,
},
computed: {
messageTime() {
const format = this.$root.settings.showSeconds ? constants.timeFormats.msgWithSeconds : constants.timeFormats.msgDefault;
setup(props) {
const store = useStore();
return moment(this.message.time).format(format);
},
messageComponent() {
return "message-" + this.message.type;
},
const timeFormat = computed(() => {
let format: keyof typeof constants.timeFormats;
if (store.state.settings.use12hClock) {
format = store.state.settings.showSeconds ? "msg12hWithSeconds" : "msg12h";
} else {
format = store.state.settings.showSeconds ? "msgWithSeconds" : "msgDefault";
}
return constants.timeFormats[format];
});
const messageTime = computed(() => {
return dayjs(props.message.time).format(timeFormat.value);
});
const messageTimeLocale = computed(() => {
return localetime(props.message.time);
});
const messageComponent = computed(() => {
return "message-" + (props.message.type || "invalid"); // TODO: force existence of type in sharedmsg
});
const isAction = () => {
if (!props.message.type) {
return false;
}
return typeof MessageTypes["message-" + props.message.type] !== "undefined";
};
return {
timeFormat,
messageTime,
messageTimeLocale,
messageComponent,
isAction,
};
},
mounted() {
require("../js/renderPreview");
},
methods: {
isAction() {
return typeof MessageTypes["message-" + this.message.type] !== "undefined";
},
},
};
});
</script>

View file

@ -1,14 +1,11 @@
<template>
<div :class="[ 'msg', 'condensed', { closed: isCollapsed } ]">
<div :class="['msg', {closed: isCollapsed}]" data-type="condensed">
<div class="condensed-summary">
<span class="time" />
<span class="from" />
<span
class="content"
@click="onCollapseClick"
>{{ condensedText }}<button
class="toggle-button"
aria-label="Toggle status messages"
<span class="content" @click="onCollapseClick"
>{{ condensedText
}}<button class="toggle-button" aria-label="Toggle status messages"
/></span>
</div>
<Message
@ -20,86 +17,149 @@
</div>
</template>
<script>
const constants = require("../js/constants");
<script lang="ts">
import {computed, defineComponent, PropType, ref} from "vue";
import {condensedTypes} from "../../shared/irc";
import {MessageType} from "../../shared/types/msg";
import {ClientMessage, ClientNetwork} from "../js/types";
import Message from "./Message.vue";
export default {
export default defineComponent({
name: "MessageCondensed",
components: {
Message,
},
props: {
network: Object,
messages: Array,
keepScrollPosition: Function,
network: {type: Object as PropType<ClientNetwork>, required: true},
messages: {
type: Array as PropType<ClientMessage[]>,
required: true,
},
keepScrollPosition: {
type: Function as PropType<() => void>,
required: true,
},
focused: Boolean,
},
data() {
return {
isCollapsed: true,
};
},
computed: {
condensedText() {
const obj = {};
setup(props) {
const isCollapsed = ref(true);
constants.condensedTypes.forEach((type) => {
const onCollapseClick = () => {
isCollapsed.value = !isCollapsed.value;
props.keepScrollPosition();
};
const condensedText = computed(() => {
const obj: Record<string, number> = {};
condensedTypes.forEach((type) => {
obj[type] = 0;
});
for (const message of this.messages) {
obj[message.type]++;
for (const message of props.messages) {
// special case since one MODE message can change multiple modes
if (message.type === MessageType.MODE) {
// syntax: +vv-t maybe-some targets
// we want the number of mode changes in the message, so count the
// number of chars other than + and - before the first space
const text = message.text ? message.text : "";
const modeChangesCount = text
.split(" ")[0]
.split("")
.filter((char) => char !== "+" && char !== "-").length;
obj[message.type] += modeChangesCount;
} else {
if (!message.type) {
/* eslint-disable no-console */
console.log(`empty message type, this should not happen: ${message.id}`);
continue;
}
obj[message.type]++;
}
}
// Count quits as parts in condensed messages to reduce information density
obj.part += obj.quit;
const strings = [];
constants.condensedTypes.forEach((type) => {
const strings: string[] = [];
condensedTypes.forEach((type) => {
if (obj[type]) {
switch (type) {
case "away":
strings.push(obj[type] + (obj[type] > 1 ? " users have gone away" : " user has gone away"));
break;
case "back":
strings.push(obj[type] + (obj[type] > 1 ? " users have come back" : " user has come back"));
break;
case "chghost":
strings.push(obj[type] + (obj[type] > 1 ? " users have changed hostname" : " user has changed hostname"));
break;
case "join":
strings.push(obj[type] + (obj[type] > 1 ? " users have joined" : " user has joined"));
break;
case "part":
strings.push(obj[type] + (obj[type] > 1 ? " users have left" : " user has left"));
break;
case "nick":
strings.push(obj[type] + (obj[type] > 1 ? " users have changed nick" : " user has changed nick"));
break;
case "kick":
strings.push(obj[type] + (obj[type] > 1 ? " users were kicked" : " user was kicked"));
break;
case "mode":
strings.push(obj[type] + (obj[type] > 1 ? " modes were set" : " mode was set"));
break;
case "chghost":
strings.push(
String(obj[type]) +
(obj[type] > 1
? " users have changed hostname"
: " user has changed hostname")
);
break;
case "join":
strings.push(
String(obj[type]) +
(obj[type] > 1 ? " users have joined" : " user has joined")
);
break;
case "part":
strings.push(
String(obj[type]) +
(obj[type] > 1 ? " users have left" : " user has left")
);
break;
case "nick":
strings.push(
String(obj[type]) +
(obj[type] > 1
? " users have changed nick"
: " user has changed nick")
);
break;
case "kick":
strings.push(
String(obj[type]) +
(obj[type] > 1 ? " users were kicked" : " user was kicked")
);
break;
case "mode":
strings.push(
String(obj[type]) +
(obj[type] > 1 ? " modes were set" : " mode was set")
);
break;
case "away":
strings.push(
"marked away " +
(obj[type] > 1 ? String(obj[type]) + " times" : "once")
);
break;
case "back":
strings.push(
"marked back " +
(obj[type] > 1 ? String(obj[type]) + " times" : "once")
);
break;
}
}
});
let text = strings.pop();
if (strings.length) {
text = strings.join(", ") + ", and " + text;
let text = strings.pop();
if (strings.length) {
text = strings.join(", ") + ", and " + text!;
}
return text;
}
return text;
},
return "";
});
return {
isCollapsed,
condensedText,
onCollapseClick,
};
},
methods: {
onCollapseClick() {
this.isCollapsed = !this.isCollapsed;
this.keepScrollPosition();
},
},
};
});
</script>

View file

@ -1,13 +1,9 @@
<template>
<div
ref="chat"
class="chat"
tabindex="-1"
>
<div :class="['show-more', { show: channel.moreHistoryAvailable }]">
<div ref="chat" class="chat" tabindex="-1">
<div v-show="channel.moreHistoryAvailable" class="show-more">
<button
ref="loadMoreButton"
:disabled="channel.historyLoading || !$root.isConnected"
:disabled="channel.historyLoading || !store.state.isConnected"
class="btn"
@click="onShowMoreClick"
>
@ -26,10 +22,11 @@
<DateMarker
v-if="shouldDisplayDateMarker(message, id)"
:key="message.id + '-date'"
:message="message"
:message="message as any"
:focused="message.id === focused"
/>
<div
v-if="shouldDisplayUnreadMarker(message.id)"
v-if="shouldDisplayUnreadMarker(Number(message.id))"
:key="message.id + '-unread'"
class="unread-marker"
>
@ -38,35 +35,64 @@
<MessageCondensed
v-if="message.type === 'condensed'"
:key="message.id"
:key="message.messages[0].id"
:network="network"
:keep-scroll-position="keepScrollPosition"
:messages="message.messages"
:focused="message.id === focused"
/>
<Message
v-else
:key="message.id"
:channel="channel"
:network="network"
:message="message"
:keep-scroll-position="keepScrollPosition"
@linkPreviewToggle="onLinkPreviewToggle"
:is-previous-source="isPreviousSource(message, id)"
:focused="message.id === focused"
@toggle-link-preview="onLinkPreviewToggle"
/>
</template>
</div>
</div>
</template>
<script>
require("intersection-observer");
const constants = require("../js/constants");
const clipboard = require("../js/clipboard");
<script lang="ts">
import {condensedTypes} from "../../shared/irc";
import {ChanType} from "../../shared/types/chan";
import {MessageType, SharedMsg} from "../../shared/types/msg";
import eventbus from "../js/eventbus";
import clipboard from "../js/clipboard";
import socket from "../js/socket";
import Message from "./Message.vue";
import MessageCondensed from "./MessageCondensed.vue";
import DateMarker from "./DateMarker.vue";
import {
computed,
defineComponent,
nextTick,
onBeforeUnmount,
onBeforeUpdate,
onMounted,
onUnmounted,
PropType,
ref,
watch,
} from "vue";
import {useStore} from "../js/store";
import {ClientChan, ClientMessage, ClientNetwork, ClientLinkPreview} from "../js/types";
export default {
type CondensedMessageContainer = {
type: "condensed";
time: Date;
messages: ClientMessage[];
id?: number;
};
// TODO; move into component
let unreadMarkerShown = false;
export default defineComponent({
name: "MessageList",
components: {
Message,
@ -74,32 +100,108 @@ export default {
DateMarker,
},
props: {
network: Object,
channel: Object,
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
focused: Number,
},
computed: {
condensedMessages() {
if (this.channel.type !== "channel") {
return this.channel.messages;
setup(props) {
const store = useStore();
const chat = ref<HTMLDivElement | null>(null);
const loadMoreButton = ref<HTMLButtonElement | null>(null);
const historyObserver = ref<IntersectionObserver | null>(null);
const skipNextScrollEvent = ref(false);
const isWaitingForNextTick = ref(false);
const jumpToBottom = () => {
skipNextScrollEvent.value = true;
props.channel.scrolledToBottom = true;
const el = chat.value;
if (el) {
el.scrollTop = el.scrollHeight;
}
};
const onShowMoreClick = () => {
if (!store.state.isConnected) {
return;
}
let lastMessage = -1;
// Find the id of first message that isn't showInActive
// If showInActive is set, this message is actually in another channel
for (const message of props.channel.messages) {
if (!message.showInActive) {
lastMessage = message.id;
break;
}
}
props.channel.historyLoading = true;
socket.emit("more", {
target: props.channel.id,
lastId: lastMessage,
condensed: store.state.settings.statusMessages !== "shown",
});
};
const onLoadButtonObserved = (entries: IntersectionObserverEntry[]) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
onShowMoreClick();
});
};
nextTick(() => {
if (!chat.value) {
return;
}
if (window.IntersectionObserver) {
historyObserver.value = new window.IntersectionObserver(onLoadButtonObserved, {
root: chat.value,
});
}
jumpToBottom();
}).catch((e) => {
// eslint-disable-next-line no-console
console.error("Error in new IntersectionObserver", e);
});
const condensedMessages = computed(() => {
if (props.channel.type !== ChanType.CHANNEL && props.channel.type !== ChanType.QUERY) {
return props.channel.messages;
}
// If actions are hidden, just return a message list with them excluded
if (this.$root.settings.statusMessages === "hidden") {
return this.channel.messages.filter((message) => !constants.condensedTypes.includes(message.type));
if (store.state.settings.statusMessages === "hidden") {
return props.channel.messages.filter(
(message) => !condensedTypes.has(message.type || "")
);
}
// If actions are not condensed, just return raw message list
if (this.$root.settings.statusMessages !== "condensed") {
return this.channel.messages;
if (store.state.settings.statusMessages !== "condensed") {
return props.channel.messages;
}
const condensed = [];
let lastCondensedContainer = null;
let lastCondensedContainer: CondensedMessageContainer | null = null;
for (const message of this.channel.messages) {
const condensed: (ClientMessage | CondensedMessageContainer)[] = [];
for (const message of props.channel.messages) {
// If this message is not condensable, or its an action affecting our user,
// then just append the message to container and be done with it
if (message.self || message.highlight || !constants.condensedTypes.includes(message.type)) {
if (message.self || message.highlight || !condensedTypes.has(message.type || "")) {
lastCondensedContainer = null;
condensed.push(message);
@ -107,7 +209,7 @@ export default {
continue;
}
if (lastCondensedContainer === null) {
if (!lastCondensedContainer) {
lastCondensedContainer = {
time: message.time,
type: "condensed",
@ -117,197 +219,222 @@ export default {
condensed.push(lastCondensedContainer);
}
lastCondensedContainer.messages.push(message);
lastCondensedContainer!.messages.push(message);
// Set id of the condensed container to last message id,
// which is required for the unread marker to work correctly
lastCondensedContainer.id = message.id;
lastCondensedContainer!.id = message.id;
// If this message is the unread boundary, create a split condensed container
if (message.id === this.channel.firstUnread) {
if (message.id === props.channel.firstUnread) {
lastCondensedContainer = null;
}
}
return condensed;
},
},
watch: {
"channel.id"() {
this.channel.scrolledToBottom = true;
return condensed.map((message) => {
// Skip condensing single messages, it doesn't save any
// space but makes useful information harder to see
if (message.type === "condensed" && message.messages.length === 1) {
return message.messages[0];
}
// Re-add the intersection observer to trigger the check again on channel switch
// Otherwise if last channel had the button visible, switching to a new channel won't trigger the history
if (this.historyObserver) {
this.historyObserver.unobserve(this.$refs.loadMoreButton);
this.historyObserver.observe(this.$refs.loadMoreButton);
}
},
"channel.messages"() {
this.keepScrollPosition();
},
"channel.pendingMessage"() {
this.$nextTick(() => {
// Keep the scroll stuck when input gets resized while typing
this.keepScrollPosition();
return message;
});
},
},
created() {
this.$nextTick(() => {
if (!this.$refs.chat) {
return;
}
if (window.IntersectionObserver) {
this.historyObserver = new window.IntersectionObserver(this.onLoadButtonObserved, {
root: this.$refs.chat,
});
}
this.jumpToBottom();
});
},
mounted() {
this.$refs.chat.addEventListener("scroll", this.handleScroll, {passive: true});
this.$root.$on("resize", this.handleResize);
this.$nextTick(() => {
if (this.historyObserver) {
this.historyObserver.observe(this.$refs.loadMoreButton);
}
});
},
beforeUpdate() {
this.unreadMarkerShown = false;
},
beforeDestroy() {
this.$root.$off("resize", this.handleResize);
this.$refs.chat.removeEventListener("scroll", this.handleScroll);
},
destroyed() {
if (this.historyObserver) {
this.historyObserver.disconnect();
}
},
methods: {
shouldDisplayDateMarker(message, id) {
const previousMessage = this.condensedMessages[id - 1];
const shouldDisplayDateMarker = (
message: SharedMsg | CondensedMessageContainer,
id: number
) => {
const previousMessage = condensedMessages.value[id - 1];
if (!previousMessage) {
return true;
}
return (new Date(previousMessage.time)).getDay() !== (new Date(message.time)).getDay();
},
shouldDisplayUnreadMarker(id) {
if (!this.unreadMarkerShown && id > this.channel.firstUnread) {
this.unreadMarkerShown = true;
const oldDate = new Date(previousMessage.time);
const newDate = new Date(message.time);
return (
oldDate.getDate() !== newDate.getDate() ||
oldDate.getMonth() !== newDate.getMonth() ||
oldDate.getFullYear() !== newDate.getFullYear()
);
};
const shouldDisplayUnreadMarker = (id: number) => {
if (!unreadMarkerShown && id > props.channel.firstUnread) {
unreadMarkerShown = true;
return true;
}
return false;
},
onCopy() {
clipboard(this.$el);
},
onLinkPreviewToggle(preview, message) {
this.keepScrollPosition();
};
const isPreviousSource = (currentMessage: ClientMessage, id: number) => {
const previousMessage = condensedMessages.value[id - 1];
return (
previousMessage &&
currentMessage.type === MessageType.MESSAGE &&
previousMessage.type === MessageType.MESSAGE &&
currentMessage.from &&
previousMessage.from &&
currentMessage.from.nick === previousMessage.from.nick
);
};
const onCopy = () => {
if (chat.value) {
clipboard(chat.value);
}
};
const keepScrollPosition = async () => {
// If we are already waiting for the next tick to force scroll position,
// we have no reason to perform more checks and set it again in the next tick
if (isWaitingForNextTick.value) {
return;
}
const el = chat.value;
if (!el) {
return;
}
if (!props.channel.scrolledToBottom) {
if (props.channel.historyLoading) {
const heightOld = el.scrollHeight - el.scrollTop;
isWaitingForNextTick.value = true;
await nextTick();
isWaitingForNextTick.value = false;
skipNextScrollEvent.value = true;
el.scrollTop = el.scrollHeight - heightOld;
}
return;
}
isWaitingForNextTick.value = true;
await nextTick();
isWaitingForNextTick.value = false;
jumpToBottom();
};
const onLinkPreviewToggle = async (preview: ClientLinkPreview, message: ClientMessage) => {
await keepScrollPosition();
// Tell the server we're toggling so it remembers at page reload
// TODO Avoid sending many single events when using `/collapse` or `/expand`
// See https://github.com/thelounge/thelounge/issues/1377
socket.emit("msg:preview:toggle", {
target: this.channel.id,
target: props.channel.id,
msgId: message.id,
link: preview.link,
shown: preview.shown,
});
},
onShowMoreClick() {
let lastMessage = this.channel.messages[0];
lastMessage = lastMessage ? lastMessage.id : -1;
};
this.channel.historyLoading = true;
socket.emit("more", {
target: this.channel.id,
lastId: lastMessage,
});
},
onLoadButtonObserved(entries) {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
this.onShowMoreClick();
});
},
keepScrollPosition() {
// If we are already waiting for the next tick to force scroll position,
// we have no reason to perform more checks and set it again in the next tick
if (this.isWaitingForNextTick) {
return;
}
const el = this.$refs.chat;
if (!el) {
return;
}
if (!this.channel.scrolledToBottom) {
if (this.channel.historyLoading) {
const heightOld = el.scrollHeight - el.scrollTop;
this.isWaitingForNextTick = true;
this.$nextTick(() => {
this.isWaitingForNextTick = false;
this.skipNextScrollEvent = true;
el.scrollTop = el.scrollHeight - heightOld;
});
}
return;
}
this.isWaitingForNextTick = true;
this.$nextTick(() => {
this.isWaitingForNextTick = false;
this.jumpToBottom();
});
},
handleScroll() {
const handleScroll = () => {
// Setting scrollTop also triggers scroll event
// We don't want to perform calculations for that
if (this.skipNextScrollEvent) {
this.skipNextScrollEvent = false;
if (skipNextScrollEvent.value) {
skipNextScrollEvent.value = false;
return;
}
const el = this.$refs.chat;
const el = chat.value;
if (!el) {
return;
}
this.channel.scrolledToBottom = el.scrollHeight - el.scrollTop - el.offsetHeight <= 30;
},
handleResize() {
// Keep message list scrolled to bottom on resize
if (this.channel.scrolledToBottom) {
this.jumpToBottom();
}
},
jumpToBottom() {
this.skipNextScrollEvent = true;
this.channel.scrolledToBottom = true;
props.channel.scrolledToBottom = el.scrollHeight - el.scrollTop - el.offsetHeight <= 30;
};
const el = this.$refs.chat;
el.scrollTop = el.scrollHeight;
},
const handleResize = () => {
// Keep message list scrolled to bottom on resize
if (props.channel.scrolledToBottom) {
jumpToBottom();
}
};
onMounted(() => {
chat.value?.addEventListener("scroll", handleScroll, {passive: true});
eventbus.on("resize", handleResize);
void nextTick(() => {
if (historyObserver.value && loadMoreButton.value) {
historyObserver.value.observe(loadMoreButton.value);
}
});
});
watch(
() => props.channel.id,
() => {
props.channel.scrolledToBottom = true;
// Re-add the intersection observer to trigger the check again on channel switch
// Otherwise if last channel had the button visible, switching to a new channel won't trigger the history
if (historyObserver.value && loadMoreButton.value) {
historyObserver.value.unobserve(loadMoreButton.value);
historyObserver.value.observe(loadMoreButton.value);
}
}
);
watch(
() => props.channel.messages,
async () => {
await keepScrollPosition();
},
{
deep: true,
}
);
watch(
() => props.channel.pendingMessage,
async () => {
// Keep the scroll stuck when input gets resized while typing
await keepScrollPosition();
}
);
onBeforeUpdate(() => {
unreadMarkerShown = false;
});
onBeforeUnmount(() => {
eventbus.off("resize", handleResize);
chat.value?.removeEventListener("scroll", handleScroll);
});
onUnmounted(() => {
if (historyObserver.value) {
historyObserver.value.disconnect();
}
});
return {
chat,
store,
onShowMoreClick,
loadMoreButton,
onCopy,
condensedMessages,
shouldDisplayDateMarker,
shouldDisplayUnreadMarker,
keepScrollPosition,
isPreviousSource,
jumpToBottom,
onLinkPreviewToggle,
};
},
};
});
</script>

View file

@ -0,0 +1,175 @@
<template>
<form :class="['message-search', {opened: searchOpened}]" @submit.prevent="searchMessages">
<div class="input-wrapper">
<input
ref="searchInputField"
v-model="searchInput"
type="search"
name="search"
class="input"
placeholder="Search messages…"
@blur="closeSearch"
@keyup.esc="closeSearch"
/>
</div>
<button
v-if="!onSearchPage"
class="search"
type="button"
aria-label="Search messages in this channel"
@mousedown.prevent="toggleSearch"
/>
</form>
</template>
<style>
form.message-search {
display: flex;
}
form.message-search .input-wrapper {
display: flex;
}
form.message-search input {
width: 100%;
height: auto !important;
margin: 7px 0;
border: 0;
color: inherit;
background-color: #fafafa;
appearance: none;
}
form.message-search input::placeholder {
color: rgba(0, 0, 0, 0.35);
}
@media (min-width: 480px) {
form.message-search input {
min-width: 140px;
}
form.message-search input:focus {
min-width: 220px;
}
}
form.message-search .input-wrapper {
position: absolute;
top: 45px;
left: 0;
right: 0;
z-index: 1;
height: 0;
overflow: hidden;
background: var(--window-bg-color);
}
form.message-search .input-wrapper input {
margin: 7px;
}
form.message-search.opened .input-wrapper {
height: 50px;
}
#chat form.message-search button {
display: flex;
color: #607992;
}
</style>
<script lang="ts">
import {computed, defineComponent, onMounted, PropType, ref, watch} from "vue";
import {useRoute, useRouter} from "vue-router";
import eventbus from "../js/eventbus";
import {ClientNetwork, ClientChan} from "../js/types";
export default defineComponent({
name: "MessageSearchForm",
props: {
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
},
setup(props) {
const searchOpened = ref(false);
const searchInput = ref("");
const router = useRouter();
const route = useRoute();
const searchInputField = ref<HTMLInputElement | null>(null);
const onSearchPage = computed(() => {
return route.name === "SearchResults";
});
watch(route, (newValue) => {
if (newValue.query.q) {
searchInput.value = String(newValue.query.q);
}
});
onMounted(() => {
searchInput.value = String(route.query.q || "");
searchOpened.value = onSearchPage.value;
if (searchInputField.value && !searchInput.value && searchOpened.value) {
searchInputField.value.focus();
}
});
const closeSearch = () => {
if (!onSearchPage.value) {
searchInput.value = "";
searchOpened.value = false;
}
};
const toggleSearch = () => {
if (searchOpened.value) {
searchInputField.value?.blur();
return;
}
searchOpened.value = true;
searchInputField.value?.focus();
};
const searchMessages = (event: Event) => {
event.preventDefault();
if (!searchInput.value) {
return;
}
router
.push({
name: "SearchResults",
params: {
id: props.channel.id,
},
query: {
q: searchInput.value,
},
})
.catch((err) => {
if (err.name === "NavigationDuplicated") {
// Search for the same query again
eventbus.emit("re-search");
}
});
};
return {
searchOpened,
searchInput,
searchInputField,
closeSearch,
toggleSearch,
searchMessages,
onSearchPage,
};
},
});
</script>

View file

@ -1,34 +1,35 @@
<template>
<span class="content">
<ParsedMessage
v-if="message.self"
:network="network"
:message="message"
/>
<ParsedMessage v-if="message.self" :network="network" :message="message" />
<template v-else>
<Username :user="message.from" />
is away
<i class="away-message">(<ParsedMessage
:network="network"
:message="message"
/>)</i>
<i class="away-message">(<ParsedMessage :network="network" :message="message" />)</i>
</template>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import type {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeAway",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -1,10 +1,6 @@
<template>
<span class="content">
<ParsedMessage
v-if="message.self"
:network="network"
:message="message"
/>
<ParsedMessage v-if="message.self" :network="network" :message="message" />
<template v-else>
<Username :user="message.from" />
is back
@ -12,19 +8,27 @@
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeBack",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -2,22 +2,37 @@
<span class="content">
<Username :user="message.from" />
has changed
<span v-if="message.new_ident">username to <b>{{ message.new_ident }}</b></span>
<span v-if="message.new_host">hostname to <i class="hostmask">{{ message.new_host }}</i></span>
<span v-if="message.new_ident"
>username to <b>{{ message.new_ident }}</b></span
>
<span v-if="message.new_host"
>hostname to
<i class="hostmask"><ParsedMessage :network="network" :text="message.new_host" /></i
></span>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeChangeHost",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -1,23 +1,31 @@
<template>
<span class="content">
<Username :user="message.from" />&#32;
<span class="ctcp-message"><ParsedMessage :text="message.ctcpMessage" /></span>
<Username :user="message.from" />
{{ `&#32;` }}<span class="ctcp-message"><ParsedMessage :text="message.ctcpMessage" /></span>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeCTCP",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -6,19 +6,27 @@
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeRequestCTCP",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -0,0 +1,77 @@
<template>
<span class="content">
<ParsedMessage :network="network" :message="message" :text="errorMessage" />
</span>
</template>
<script lang="ts">
import ParsedMessage from "../ParsedMessage.vue";
import {computed, defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
export default defineComponent({
name: "MessageTypeError",
components: {
ParsedMessage,
},
props: {
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
setup(props) {
const errorMessage = computed(() => {
// TODO: enforce chan and nick fields so that we can get rid of that
const chan = props.message.channel || "!UNKNOWN_CHAN";
const nick = props.message.nick || "!UNKNOWN_NICK";
switch (props.message.error) {
case "bad_channel_key":
return `Cannot join ${chan} - Bad channel key.`;
case "banned_from_channel":
return `Cannot join ${chan} - You have been banned from the channel.`;
case "cannot_send_to_channel":
return `Cannot send to channel ${chan}`;
case "channel_is_full":
return `Cannot join ${chan} - Channel is full.`;
case "chanop_privs_needed":
return "Cannot perform action: You're not a channel operator.";
case "invite_only_channel":
return `Cannot join ${chan} - Channel is invite only.`;
case "no_such_nick":
return `User ${nick} hasn't logged in or does not exist.`;
case "not_on_channel":
return "Cannot perform action: You're not on the channel.";
case "password_mismatch":
return "Password mismatch.";
case "too_many_channels":
return `Cannot join ${chan} - You've already reached the maximum number of channels allowed.`;
case "unknown_command":
// TODO: not having message.command should never happen, so force existence
return `Unknown command: ${props.message.command || "!UNDEFINED_COMMAND_BUG"}`;
case "user_not_in_channel":
return `User ${nick} is not on the channel.`;
case "user_on_channel":
return `User ${nick} is already on the channel.`;
default:
if (props.message.reason) {
return `${props.message.reason} (${
props.message.error || "!UNDEFINED_ERR"
})`;
}
return props.message.error;
}
});
return {
errorMessage,
};
},
});
</script>

View file

@ -1,12 +1,10 @@
"use strict";
// This creates a version of `require()` in the context of the current
// directory, so we iterate over its content, which is a map statically built by
// Webpack.
// Second argument says it's recursive, third makes sure we only load templates.
const requireViews = require.context(".", false, /\.vue$/);
module.exports = requireViews.keys().reduce((acc, path) => {
export default requireViews.keys().reduce((acc: Record<string, any>, path) => {
acc["message-" + path.substring(2, path.length - 4)] = requireViews(path).default;
return acc;

View file

@ -3,30 +3,32 @@
<Username :user="message.from" />
invited
<span v-if="message.invitedYou">you</span>
<Username
v-else
:user="message.target"
/>
to <ParsedMessage
:network="network"
:text="message.channel"
/>
<Username v-else :user="message.target" />
to <ParsedMessage :network="network" :text="message.channel" />
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeInvite",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -1,22 +1,38 @@
<template>
<span class="content">
<Username :user="message.from" />
<i class="hostmask"> ({{ message.hostmask }})</i>
<i class="hostmask">&#32;(<ParsedMessage :network="network" :text="message.hostmask" />)</i>
<template v-if="message.account">
<i class="account">&#32;[{{ message.account }}]</i>
</template>
<template v-if="message.gecos">
<i class="realname">&#32;({{ message.gecos }})</i>
</template>
has joined the channel
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeJoin",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -3,29 +3,33 @@
<Username :user="message.from" />
has kicked
<Username :user="message.target" />
<i
v-if="message.text"
class="part-reason"
> (<ParsedMessage
:network="network"
:message="message"
/>)</i>
<i v-if="message.text" class="part-reason"
>&#32;(<ParsedMessage :network="network" :message="message" />)</i
>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeKick",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -6,19 +6,27 @@
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeMode",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -4,12 +4,21 @@
</span>
</template>
<script>
export default {
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
export default defineComponent({
name: "MessageChannelMode",
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -0,0 +1,24 @@
<template>
<span class="content">
Your user mode is <b>{{ message.raw_modes }}</b>
</span>
</template>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
export default defineComponent({
name: "MessageChannelMode",
props: {
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
});
</script>

View file

@ -0,0 +1,49 @@
<template>
<span class="content">
<span class="text"><ParsedMessage :network="network" :text="cleanText" /></span>
</span>
</template>
<script lang="ts">
import {computed, defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
export default defineComponent({
name: "MessageTypeMonospaceBlock",
components: {
ParsedMessage,
},
props: {
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
setup(props) {
const cleanText = computed(() => {
let lines = props.message.text.split("\n");
// If all non-empty lines of the MOTD start with a hyphen (which is common
// across MOTDs), remove all the leading hyphens.
if (lines.every((line) => line === "" || line[0] === "-")) {
lines = lines.map((line) => line.substring(2));
}
// Remove empty lines around the MOTD (but not within it)
return lines
.map((line) => line.replace(/\s*$/, ""))
.join("\n")
.replace(/^[\r\n]+|[\r\n]+$/g, "");
});
return {
cleanText,
};
},
});
</script>

View file

@ -1,40 +0,0 @@
<template>
<span class="content">
<span class="text"><ParsedMessage
:network="network"
:text="cleanText"
/></span>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
export default {
name: "MessageTypeMOTD",
components: {
ParsedMessage,
},
props: {
network: Object,
message: Object,
},
computed: {
cleanText() {
let lines = this.message.text.split("\n");
// If all non-empty lines of the MOTD start with a hyphen (which is common
// across MOTDs), remove all the leading hyphens.
if (lines.every((line) => line === "" || line[0] === "-")) {
lines = lines.map((line) => line.substr(2));
}
// Remove empty lines around the MOTD (but not within it)
return lines
.map((line) => line.replace(/\s*$/,""))
.join("\n")
.replace(/^[\r\n]+|[\r\n]+$/g, "");
},
},
};
</script>

View file

@ -6,17 +6,25 @@
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeNick",
components: {
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -1,29 +1,35 @@
<template>
<span class="content">
<Username :user="message.from" />
<i class="hostmask"> ({{ message.hostmask }})</i> has left the channel <i
v-if="message.text"
class="part-reason"
>(<ParsedMessage
:network="network"
:message="message"
/>)</i>
<i class="hostmask"> (<ParsedMessage :network="network" :text="message.hostmask" />)</i> has
left the channel
<i v-if="message.text" class="part-reason"
>(<ParsedMessage :network="network" :message="message" />)</i
>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypePart",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -1,29 +1,35 @@
<template>
<span class="content">
<Username :user="message.from" />
<i class="hostmask"> ({{ message.hostmask }})</i> has quit <i
v-if="message.text"
class="quit-reason"
>(<ParsedMessage
:network="network"
:message="message"
/>)</i>
<i class="hostmask"> (<ParsedMessage :network="network" :text="message.hostmask" />)</i> has
quit
<i v-if="message.text" class="quit-reason"
>(<ParsedMessage :network="network" :message="message" />)</i
>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import type {ClientMessage, ClientNetwork} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeQuit",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -0,0 +1,22 @@
<template>
<span class="content">{{ message.text }}</span>
</template>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
export default defineComponent({
name: "MessageTypeRaw",
props: {
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
});
</script>

View file

@ -1,30 +1,36 @@
<template>
<span class="content">
<template v-if="message.from && message.from.nick"><Username :user="message.from" /> has changed the topic to: </template>
<template v-if="message.from && message.from.nick"
><Username :user="message.from" /> has changed the topic to:
</template>
<template v-else>The topic is: </template>
<span
v-if="message.text"
class="new-topic"
><ParsedMessage
:network="network"
:message="message"
<span v-if="message.text" class="new-topic"
><ParsedMessage :network="network" :message="message"
/></span>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import type {ClientMessage, ClientNetwork} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeTopic",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
});
</script>

View file

@ -2,21 +2,37 @@
<span class="content">
Topic set by
<Username :user="message.from" />
on {{ message.when | localetime }}
on {{ messageTimeLocale }}
</span>
</template>
<script>
<script lang="ts">
import localetime from "../../js/helpers/localetime";
import {computed, defineComponent, PropType} from "vue";
import {ClientNetwork, ClientMessage} from "../../js/types";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeTopicSetBy",
components: {
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
setup(props) {
const messageTimeLocale = computed(() => localetime(props.message.when));
return {
messageTimeLocale,
};
},
});
</script>

View file

@ -12,7 +12,12 @@
</template>
<dt>Host mask:</dt>
<dd class="hostmask">{{ message.whois.ident }}@{{ message.whois.hostname }}</dd>
<dd class="hostmask">
<ParsedMessage
:network="network"
:text="message.whois.ident + '@' + message.whois.hostname"
/>
</dd>
<template v-if="message.whois.actual_hostname">
<dt>Actual host:</dt>
@ -21,17 +26,17 @@
:href="'https://ipinfo.io/' + message.whois.actual_ip"
target="_blank"
rel="noopener"
>{{ message.whois.actual_ip }}</a>
<i v-if="message.whois.actual_hostname != message.whois.actual_ip"> ({{ message.whois.actual_hostname }})</i>
>{{ message.whois.actual_ip }}</a
>
<i v-if="message.whois.actual_hostname != message.whois.actual_ip">
({{ message.whois.actual_hostname }})</i
>
</dd>
</template>
<template v-if="message.whois.real_name">
<dt>Real name:</dt>
<dd><ParsedMessage
:network="network"
:text="message.whois.real_name"
/></dd>
<dd><ParsedMessage :network="network" :text="message.whois.real_name" /></dd>
</template>
<template v-if="message.whois.registered_nick">
@ -41,10 +46,7 @@
<template v-if="message.whois.channels">
<dt>Channels:</dt>
<dd><ParsedMessage
:network="network"
:text="message.whois.channels"
/></dd>
<dd><ParsedMessage :network="network" :text="message.whois.channels" /></dd>
</template>
<template v-if="message.whois.modes">
@ -53,9 +55,9 @@
</template>
<template v-if="message.whois.special">
<template v-for="special in message.whois.special">
<dt :key="special">Special:</dt>
<dd :key="special">{{ special }}</dd>
<template v-for="special in message.whois.special" :key="special">
<dt>Special:</dt>
<dd>{{ special }}</dd>
</template>
</template>
@ -76,10 +78,7 @@
<template v-if="message.whois.away">
<dt>Away:</dt>
<dd><ParsedMessage
:network="network"
:text="message.whois.away"
/></dd>
<dd><ParsedMessage :network="network" :text="message.whois.away" /></dd>
</template>
<template v-if="message.whois.secure">
@ -87,37 +86,58 @@
<dd>Yes</dd>
</template>
<template v-if="message.whois.certfp">
<dt>Certificate:</dt>
<dd>{{ message.whois.certfp }}</dd>
</template>
<template v-if="message.whois.server">
<dt>Connected to:</dt>
<dd>{{ message.whois.server }} <i>({{ message.whois.server_info }})</i></dd>
<dd>
{{ message.whois.server }} <i>({{ message.whois.server_info }})</i>
</dd>
</template>
<template v-if="message.whois.logonTime">
<dt>Connected at:</dt>
<dd>{{ message.whois.logonTime | localetime }}</dd>
<dd>{{ localetime(message.whois.logonTime) }}</dd>
</template>
<template v-if="message.whois.idle">
<dt>Idle since:</dt>
<dd>{{ message.whois.idleTime | localetime }}</dd>
<dd>{{ localetime(message.whois.idleTime) }}</dd>
</template>
</dl>
</span>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import localetime from "../../js/helpers/localetime";
import {ClientNetwork, ClientMessage} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
export default defineComponent({
name: "MessageTypeWhois",
components: {
ParsedMessage,
Username,
},
props: {
network: Object,
message: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
message: {
type: Object as PropType<ClientMessage>,
required: true,
},
},
};
setup() {
return {
localetime: (date: Date) => localetime(date),
};
},
});
</script>

View file

@ -0,0 +1,572 @@
<template>
<div id="connect" class="window" role="tabpanel" aria-label="Connect">
<div class="header">
<SidebarToggle />
</div>
<form class="container" method="post" action="" @submit.prevent="onSubmit">
<h1 class="title">
<template v-if="defaults.uuid">
<input v-model="defaults.uuid" type="hidden" name="uuid" />
Edit {{ defaults.name }}
</template>
<template v-else>
Connect
<template
v-if="config?.lockNetwork && store?.state.serverConfiguration?.public"
>
to {{ defaults.name }}
</template>
</template>
</h1>
<template v-if="!config?.lockNetwork">
<h2>Network settings</h2>
<div class="connect-row">
<label for="connect:name">Name</label>
<input
id="connect:name"
v-model.trim="defaults.name"
class="input"
name="name"
maxlength="100"
/>
</div>
<div class="connect-row">
<label for="connect:host">Server</label>
<div class="input-wrap">
<input
id="connect:host"
v-model.trim="defaults.host"
class="input"
name="host"
aria-label="Server address"
maxlength="255"
required
/>
<span id="connect:portseparator">:</span>
<input
id="connect:port"
v-model="defaults.port"
class="input"
type="number"
min="1"
max="65535"
name="port"
aria-label="Server port"
/>
</div>
</div>
<div class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:password"
v-model="defaults.password"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
placeholder="Server password (optional)"
name="password"
maxlength="300"
/>
</RevealPassword>
</div>
<div class="connect-row">
<label></label>
<div class="input-wrap">
<label class="tls">
<input
v-model="defaults.tls"
type="checkbox"
name="tls"
:disabled="defaults.hasSTSPolicy"
/>
Use secure connection (TLS)
<span
v-if="defaults.hasSTSPolicy"
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="This network has a strict transport security policy, you will be unable to disable TLS"
>🔒 STS</span
>
</label>
<label class="tls">
<input
v-model="defaults.rejectUnauthorized"
type="checkbox"
name="rejectUnauthorized"
/>
Only allow trusted certificates
</label>
</div>
</div>
<h2>Proxy Settings</h2>
<div class="connect-row">
<label></label>
<div class="input-wrap">
<label for="connect:proxyEnabled">
<input
id="connect:proxyEnabled"
v-model="defaults.proxyEnabled"
type="checkbox"
name="proxyEnabled"
/>
Enable Proxy
</label>
</div>
</div>
<template v-if="defaults.proxyEnabled">
<div class="connect-row">
<label for="connect:proxyHost">SOCKS Address</label>
<div class="input-wrap">
<input
id="connect:proxyHost"
v-model.trim="defaults.proxyHost"
class="input"
name="proxyHost"
aria-label="Proxy host"
maxlength="255"
/>
<span id="connect:proxyPortSeparator">:</span>
<input
id="connect:proxyPort"
v-model="defaults.proxyPort"
class="input"
type="number"
min="1"
max="65535"
name="proxyPort"
aria-label="SOCKS port"
/>
</div>
</div>
<div class="connect-row">
<label for="connect:proxyUsername">Proxy username</label>
<input
id="connect:proxyUsername"
ref="proxyUsernameInput"
v-model.trim="defaults.proxyUsername"
class="input username"
name="proxyUsername"
maxlength="100"
placeholder="Proxy username"
/>
</div>
<div class="connect-row">
<label for="connect:proxyPassword">Proxy password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:proxyPassword"
ref="proxyPassword"
v-model="defaults.proxyPassword"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
placeholder="Proxy password"
name="proxyPassword"
maxlength="300"
/>
</RevealPassword>
</div>
</template>
</template>
<template v-else-if="config.lockNetwork && !store.state.serverConfiguration?.public">
<h2>Network settings</h2>
<div class="connect-row">
<label for="connect:name">Name</label>
<input
id="connect:name"
v-model.trim="defaults.name"
class="input"
name="name"
maxlength="100"
/>
</div>
<div class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:password"
v-model="defaults.password"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
placeholder="Server password (optional)"
name="password"
maxlength="300"
/>
</RevealPassword>
</div>
</template>
<h2>User preferences</h2>
<div class="connect-row">
<label for="connect:nick">Nick</label>
<input
id="connect:nick"
v-model="defaults.nick"
class="input nick"
name="nick"
pattern="[^\s:!@]+"
maxlength="100"
required
@input="onNickChanged"
/>
</div>
<template v-if="!config?.useHexIp">
<div class="connect-row">
<label for="connect:username">Username</label>
<input
id="connect:username"
ref="usernameInput"
v-model.trim="defaults.username"
class="input username"
name="username"
maxlength="100"
/>
</div>
</template>
<div class="connect-row">
<label for="connect:realname">Real name</label>
<input
id="connect:realname"
v-model.trim="defaults.realname"
class="input"
name="realname"
maxlength="300"
/>
</div>
<div class="connect-row">
<label for="connect:leaveMessage">Leave message</label>
<input
id="connect:leaveMessage"
v-model.trim="defaults.leaveMessage"
autocomplete="off"
class="input"
name="leaveMessage"
placeholder="The Lounge - https://thelounge.chat"
/>
</div>
<template v-if="defaults.uuid && !store.state.serverConfiguration?.public">
<div class="connect-row">
<label for="connect:commands">
Commands
<span
class="tooltipped tooltipped-ne tooltipped-no-delay"
aria-label="One /command per line.
Each command will be executed in
the server tab on new connection"
>
<button class="extra-help" />
</span>
</label>
<textarea
id="connect:commands"
ref="commandsInput"
autocomplete="off"
:value="defaults.commands ? defaults.commands.join('\n') : ''"
class="input"
name="commands"
@input="resizeCommandsInput"
/>
</div>
</template>
<template v-else-if="!defaults.uuid">
<div class="connect-row">
<label for="connect:channels">Channels</label>
<input
id="connect:channels"
v-model.trim="defaults.join"
class="input"
name="join"
/>
</div>
</template>
<template v-if="store.state.serverConfiguration?.public">
<template v-if="config?.lockNetwork">
<div class="connect-row">
<label></label>
<div class="input-wrap">
<label class="tls">
<input v-model="displayPasswordField" type="checkbox" />
I have a password
</label>
</div>
</div>
<div v-if="displayPasswordField" class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:password"
ref="publicPassword"
v-model="defaults.password"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
placeholder="Server password (optional)"
name="password"
maxlength="300"
/>
</RevealPassword>
</div>
</template>
</template>
<template v-else>
<h2 id="label-auth">Authentication</h2>
<div class="connect-row connect-auth" role="group" aria-labelledby="label-auth">
<label class="opt">
<input
:checked="!defaults.sasl"
type="radio"
name="sasl"
value=""
@change="setSaslAuth('')"
/>
No authentication
</label>
<label class="opt">
<input
:checked="defaults.sasl === 'plain'"
type="radio"
name="sasl"
value="plain"
@change="setSaslAuth('plain')"
/>
Username + password (SASL PLAIN)
</label>
<label
v-if="!store.state.serverConfiguration?.public && defaults.tls"
class="opt"
>
<input
:checked="defaults.sasl === 'external'"
type="radio"
name="sasl"
value="external"
@change="setSaslAuth('external')"
/>
Client certificate (SASL EXTERNAL)
</label>
</div>
<template v-if="defaults.sasl === 'plain'">
<div class="connect-row">
<label for="connect:username">Account</label>
<input
id="connect:saslAccount"
v-model.trim="defaults.saslAccount"
class="input"
name="saslAccount"
maxlength="100"
required
/>
</div>
<div class="connect-row">
<label for="connect:password">Password</label>
<RevealPassword
v-slot:default="slotProps"
class="input-wrap password-container"
>
<input
id="connect:saslPassword"
v-model="defaults.saslPassword"
class="input"
:type="slotProps.isVisible ? 'text' : 'password'"
name="saslPassword"
maxlength="300"
required
/>
</RevealPassword>
</div>
</template>
<div v-else-if="defaults.sasl === 'external'" class="connect-sasl-external">
<p>The Lounge automatically generates and manages the client certificate.</p>
<p>
On the IRC server, you will need to tell the services to attach the
certificate fingerprint (certfp) to your account, for example:
</p>
<pre><code>/msg NickServ CERT ADD</code></pre>
</div>
</template>
<div>
<button type="submit" class="btn" :disabled="disabled ? true : false">
<template v-if="defaults.uuid">Save network</template>
<template v-else>Connect</template>
</button>
</div>
</form>
</div>
</template>
<style>
#connect .connect-auth {
display: block;
margin-bottom: 10px;
}
#connect .connect-auth .opt {
display: block;
width: 100%;
}
#connect .connect-auth input {
margin: 3px 10px 0 0;
}
#connect .connect-sasl-external {
padding: 10px;
border-radius: 2px;
background-color: #d9edf7;
color: #31708f;
}
#connect .connect-sasl-external pre {
margin: 0;
user-select: text;
}
</style>
<script lang="ts">
import RevealPassword from "./RevealPassword.vue";
import SidebarToggle from "./SidebarToggle.vue";
import {defineComponent, nextTick, PropType, ref, watch} from "vue";
import {useStore} from "../js/store";
import {ClientNetwork} from "../js/types";
export type NetworkFormDefaults = Partial<ClientNetwork> & {
join?: string;
};
export default defineComponent({
name: "NetworkForm",
components: {
RevealPassword,
SidebarToggle,
},
props: {
handleSubmit: {
type: Function as PropType<(network: ClientNetwork) => void>,
required: true,
},
defaults: {
type: Object as PropType<NetworkFormDefaults>,
required: true,
},
disabled: Boolean,
},
setup(props) {
const store = useStore();
const config = ref(store.state.serverConfiguration);
const previousUsername = ref(props.defaults?.username);
const displayPasswordField = ref(false);
const publicPassword = ref<HTMLInputElement | null>(null);
watch(displayPasswordField, (newValue) => {
if (newValue) {
void nextTick(() => {
publicPassword.value?.focus();
});
}
});
const commandsInput = ref<HTMLInputElement | null>(null);
const resizeCommandsInput = () => {
if (!commandsInput.value) {
return;
}
// Reset height first so it can down size
commandsInput.value.style.height = "";
// 2 pixels to account for the border
commandsInput.value.style.height = `${Math.ceil(
commandsInput.value.scrollHeight + 2
)}px`;
};
watch(
// eslint-disable-next-line
() => props.defaults?.commands,
() => {
void nextTick(() => {
resizeCommandsInput();
});
}
);
watch(
// eslint-disable-next-line
() => props.defaults?.tls,
(isSecureChecked) => {
const ports = [6667, 6697];
const newPort = isSecureChecked ? 0 : 1;
// If you disable TLS and current port is 6697,
// set it to 6667, and vice versa
if (props.defaults?.port === ports[newPort]) {
props.defaults.port = ports[1 - newPort];
}
}
);
const setSaslAuth = (type: string) => {
if (props.defaults) {
props.defaults.sasl = type;
}
};
const usernameInput = ref<HTMLInputElement | null>(null);
const onNickChanged = (event: Event) => {
if (!usernameInput.value) {
return;
}
const usernameRef = usernameInput.value;
if (!usernameRef.value || usernameRef.value === previousUsername.value) {
usernameRef.value = (event.target as HTMLInputElement)?.value;
}
previousUsername.value = (event.target as HTMLInputElement)?.value;
};
const onSubmit = (event: Event) => {
const formData = new FormData(event.target as HTMLFormElement);
const data: Partial<ClientNetwork> = {};
formData.forEach((value, key) => {
data[key] = value;
});
props.handleSubmit(data as ClientNetwork);
};
return {
store,
config,
displayPasswordField,
publicPassword,
commandsInput,
resizeCommandsInput,
setSaslAuth,
usernameInput,
onNickChanged,
onSubmit,
};
},
});
</script>

View file

@ -1,85 +1,229 @@
<template>
<div
v-if="networks.length === 0"
v-if="store.state.networks.length === 0"
class="empty"
role="navigation"
aria-label="Network and Channel list"
>
You are not connected to any networks yet.
</div>
<Draggable
v-else
:list="networks"
:disabled="isSortingEnabled"
handle=".lobby"
draggable=".network"
ghost-class="ui-sortable-ghost"
drag-class="ui-sortable-dragged"
group="networks"
class="networks"
@change="onNetworkSort"
@start="onDragStart"
@end="onDragEnd"
>
<div
v-for="network in networks"
:id="'network-' + network.uuid"
:key="network.uuid"
:class="{
collapsed: network.isCollapsed,
'not-connected': !network.status.connected,
'not-secure': !network.status.secure,
}"
:data-uuid="network.uuid"
:data-nick="network.nick"
class="network"
role="region"
>
<NetworkLobby
:network="network"
:active-channel="activeChannel"
:is-join-channel-shown="network.isJoinChannelShown"
@toggleJoinChannel="network.isJoinChannelShown = !network.isJoinChannelShown"
<div v-else ref="networklist" role="navigation" aria-label="Network and Channel list">
<div class="jump-to-input">
<input
ref="searchInput"
:value="searchText"
placeholder="Jump to..."
type="search"
class="search input mousetrap"
aria-label="Search among the channel list"
tabindex="-1"
@input="setSearchText"
@keydown.up="navigateResults($event, -1)"
@keydown.down="navigateResults($event, 1)"
@keydown.page-up="navigateResults($event, -10)"
@keydown.page-down="navigateResults($event, 10)"
@keydown.enter="selectResult"
@keydown.escape="deactivateSearch"
@focus="activateSearch"
/>
<JoinChannel
v-if="network.isJoinChannelShown"
:network="network"
:channel="network.channels[0]"
@toggleJoinChannel="network.isJoinChannelShown = !network.isJoinChannelShown"
/>
<Draggable
draggable=".chan"
ghost-class="ui-sortable-ghost"
drag-class="ui-sortable-dragged"
:group="network.uuid"
:disabled="isSortingEnabled"
:list="network.channels"
class="channels"
@change="onChannelSort"
@start="onDragStart"
@end="onDragEnd"
>
<Channel
v-for="(channel, index) in network.channels"
v-if="index > 0"
:key="channel.id"
:channel="channel"
:network="network"
:active-channel="activeChannel"
/>
</Draggable>
</div>
</Draggable>
<div v-if="searchText" class="jump-to-results">
<div v-if="results.length">
<div
v-for="item in results"
:key="item.channel.id"
@mouseenter="setActiveSearchItem(item.channel)"
@click.prevent="selectResult"
>
<Channel
v-if="item.channel.type !== 'lobby'"
:channel="item.channel"
:network="item.network"
:active="item.channel === activeSearchItem"
:is-filtering="true"
/>
<NetworkLobby
v-else
:channel="item.channel"
:network="item.network"
:active="item.channel === activeSearchItem"
:is-filtering="true"
/>
</div>
</div>
<div v-else class="no-results">No results found.</div>
</div>
<Draggable
v-else
:list="store.state.networks"
:delay="LONG_TOUCH_DURATION"
:delay-on-touch-only="true"
:touch-start-threshold="10"
handle=".channel-list-item[data-type='lobby']"
draggable=".network"
ghost-class="ui-sortable-ghost"
drag-class="ui-sortable-dragging"
group="networks"
class="networks"
item-key="uuid"
@change="onNetworkSort"
@choose="onDraggableChoose"
@unchoose="onDraggableUnchoose"
>
<template v-slot:item="{element: network}">
<div
:id="'network-' + network.uuid"
:key="network.uuid"
:class="{
collapsed: network.isCollapsed,
'not-connected': !network.status.connected,
'not-secure': !network.status.secure,
}"
class="network"
role="region"
aria-live="polite"
@touchstart="onDraggableTouchStart"
@touchmove="onDraggableTouchMove"
@touchend="onDraggableTouchEnd"
@touchcancel="onDraggableTouchEnd"
>
<NetworkLobby
:network="network"
:is-join-channel-shown="network.isJoinChannelShown"
:active="
store.state.activeChannel &&
network.channels[0] === store.state.activeChannel.channel
"
@toggle-join-channel="
network.isJoinChannelShown = !network.isJoinChannelShown
"
/>
<JoinChannel
v-if="network.isJoinChannelShown"
:network="network"
:channel="network.channels[0]"
@toggle-join-channel="
network.isJoinChannelShown = !network.isJoinChannelShown
"
/>
<Draggable
draggable=".channel-list-item"
ghost-class="ui-sortable-ghost"
drag-class="ui-sortable-dragging"
:group="network.uuid"
:list="network.channels"
:delay="LONG_TOUCH_DURATION"
:delay-on-touch-only="true"
:touch-start-threshold="10"
class="channels"
item-key="name"
@change="onChannelSort"
@choose="onDraggableChoose"
@unchoose="onDraggableUnchoose"
>
<template v-slot:item="{element: channel, index}">
<Channel
v-if="index > 0"
:key="channel.id"
:data-item="channel.id"
:channel="channel"
:network="network"
:active="
store.state.activeChannel &&
channel === store.state.activeChannel.channel
"
/>
</template>
</Draggable>
</div>
</template>
</Draggable>
</div>
</template>
<script>
import Draggable from "vuedraggable";
<style>
.jump-to-input {
margin: 8px;
position: relative;
}
.jump-to-input .input {
margin: 0;
width: 100%;
border: 0;
color: #fff;
background-color: rgba(255, 255, 255, 0.1);
padding-right: 35px;
appearance: none;
}
.jump-to-input .input::placeholder {
color: rgba(255, 255, 255, 0.35);
}
.jump-to-input::before {
content: "\f002"; /* http://fontawesome.io/icon/search/ */
color: rgba(255, 255, 255, 0.35);
position: absolute;
right: 8px;
top: 0;
bottom: 0;
pointer-events: none;
line-height: 35px !important;
}
.jump-to-results {
margin: 0;
padding: 0;
list-style: none;
overflow: auto;
}
.jump-to-results .no-results {
margin: 14px 8px;
text-align: center;
}
.jump-to-results .channel-list-item.active {
cursor: pointer;
}
.jump-to-results .channel-list-item .add-channel,
.jump-to-results .channel-list-item .close-tooltip {
display: none;
}
.jump-to-results .channel-list-item[data-type="lobby"] {
padding: 8px 14px;
}
.jump-to-results .channel-list-item[data-type="lobby"]::before {
content: "\f233";
}
</style>
<script lang="ts">
import {computed, watch, defineComponent, nextTick, onBeforeUnmount, onMounted, ref} from "vue";
import Mousetrap from "mousetrap";
import Draggable from "./Draggable.vue";
import {filter as fuzzyFilter} from "fuzzy";
import NetworkLobby from "./NetworkLobby.vue";
import Channel from "./Channel.vue";
import JoinChannel from "./JoinChannel.vue";
import socket from "../js/socket";
import collapseNetworkHelper from "../js/helpers/collapseNetwork";
import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind";
import distance from "../js/helpers/distance";
import eventbus from "../js/eventbus";
import {ClientChan, NetChan} from "../js/types";
import {useStore} from "../js/store";
import {switchToChannel} from "../js/router";
import Sortable from "sortablejs";
export default {
export default defineComponent({
name: "NetworkList",
components: {
JoinChannel,
@ -87,53 +231,346 @@ export default {
Channel,
Draggable,
},
props: {
activeChannel: Object,
networks: Array,
},
computed: {
isSortingEnabled() {
const isTouch = !!("ontouchstart" in window || (window.DocumentTouch && document instanceof window.DocumentTouch));
setup() {
const store = useStore();
const searchText = ref("");
const activeSearchItem = ref<ClientChan | null>();
// Number of milliseconds a touch has to last to be considered long
const LONG_TOUCH_DURATION = 500;
// TODO: Implement a way to sort on touch devices
return isTouch;
},
},
methods: {
onDragStart(e) {
e.target.classList.add("ui-sortable-active");
},
onDragEnd(e) {
e.target.classList.remove("ui-sortable-active");
},
onNetworkSort(e) {
if (!e.moved) {
const startDrag = ref<[number, number] | null>();
const searchInput = ref<HTMLInputElement | null>(null);
const networklist = ref<HTMLDivElement | null>(null);
const sidebarWasClosed = ref(false);
const moveItemInArray = <T>(array: T[], from: number, to: number) => {
const item = array.splice(from, 1)[0];
array.splice(to, 0, item);
};
const items = computed(() => {
const newItems: NetChan[] = [];
for (const network of store.state.networks) {
for (const channel of network.channels) {
if (
store.state.activeChannel &&
channel === store.state.activeChannel.channel
) {
continue;
}
newItems.push({network, channel});
}
}
return newItems;
});
const results = computed(() => {
const newResults = fuzzyFilter(searchText.value, items.value, {
extract: (item) => item.channel.name,
}).map((item) => item.original);
return newResults;
});
const collapseNetwork = (event: Mousetrap.ExtendedKeyboardEvent) => {
if (isIgnoredKeybind(event)) {
return true;
}
if (store.state.activeChannel) {
collapseNetworkHelper(store.state.activeChannel.network, true);
}
return false;
};
const expandNetwork = (event: Mousetrap.ExtendedKeyboardEvent) => {
if (isIgnoredKeybind(event)) {
return true;
}
if (store.state.activeChannel) {
collapseNetworkHelper(store.state.activeChannel.network, false);
}
return false;
};
const onNetworkSort = (e: Sortable.SortableEvent) => {
const {oldIndex, newIndex} = e;
if (oldIndex === undefined || newIndex === undefined || oldIndex === newIndex) {
return;
}
socket.emit("sort", {
type: "networks",
order: this.networks.map((n) => n.uuid),
moveItemInArray(store.state.networks, oldIndex, newIndex);
socket.emit("sort:networks", {
order: store.state.networks.map((n) => n.uuid),
});
},
onChannelSort(e) {
if (!e.moved) {
};
const onChannelSort = (e: Sortable.SortableEvent) => {
let {oldIndex, newIndex} = e;
if (oldIndex === undefined || newIndex === undefined || oldIndex === newIndex) {
return;
}
const {findChannel} = require("../js/vue");
const channel = findChannel(e.moved.element.id);
// Indexes are offset by one due to the lobby
oldIndex += 1;
newIndex += 1;
const unparsedId = e.item.getAttribute("data-item");
if (!unparsedId) {
return;
}
const id = parseInt(unparsedId);
const netChan = store.getters.findChannel(id);
if (!netChan) {
return;
}
moveItemInArray(netChan.network.channels, oldIndex, newIndex);
socket.emit("sort:channel", {
network: netChan.network.uuid,
order: netChan.network.channels.map((c) => c.id),
});
};
const isTouchEvent = (event: any): boolean => {
// This is the same way Sortable.js detects a touch event. See
// SortableJS/Sortable@daaefeda:/src/Sortable.js#L465
return !!(
(event.touches && event.touches[0]) ||
(event.pointerType && event.pointerType === "touch")
);
};
const onDraggableChoose = (event: any) => {
const original = event.originalEvent;
if (isTouchEvent(original)) {
// onDrag is only triggered when the user actually moves the
// dragged object but onChoose is triggered as soon as the
// item is eligible for dragging. This gives us an opportunity
// to tell the user they've held the touch long enough.
event.item.classList.add("ui-sortable-dragging-touch-cue");
if (original instanceof TouchEvent && original.touches.length > 0) {
startDrag.value = [original.touches[0].clientX, original.touches[0].clientY];
} else if (original instanceof PointerEvent) {
startDrag.value = [original.clientX, original.clientY];
}
}
};
const onDraggableUnchoose = (event: any) => {
event.item.classList.remove("ui-sortable-dragging-touch-cue");
startDrag.value = null;
};
const onDraggableTouchStart = (event: TouchEvent) => {
if (event.touches.length === 1) {
// This prevents an iOS long touch default behavior: selecting
// the nearest selectable text.
document.body.classList.add("force-no-select");
}
};
const onDraggableTouchMove = (event: TouchEvent) => {
if (startDrag.value && event.touches.length > 0) {
const touch = event.touches[0];
const currentPosition = [touch.clientX, touch.clientY];
if (distance(startDrag.value, currentPosition as [number, number]) > 10) {
// Context menu is shown on Android after long touch.
// Dismiss it now that we're sure the user is dragging.
eventbus.emit("contextmenu:cancel");
}
}
};
const onDraggableTouchEnd = (event: TouchEvent) => {
if (event.touches.length === 0) {
document.body.classList.remove("force-no-select");
}
};
const activateSearch = () => {
if (searchInput.value === document.activeElement) {
return;
}
sidebarWasClosed.value = store.state.sidebarOpen ? false : true;
store.commit("sidebarOpen", true);
void nextTick(() => {
searchInput.value?.focus();
});
};
const deactivateSearch = () => {
activeSearchItem.value = null;
searchText.value = "";
searchInput.value?.blur();
if (sidebarWasClosed.value) {
store.commit("sidebarOpen", false);
}
};
const toggleSearch = (event: Mousetrap.ExtendedKeyboardEvent) => {
if (isIgnoredKeybind(event)) {
return true;
}
if (searchInput.value === document.activeElement) {
deactivateSearch();
return false;
}
activateSearch();
return false;
};
const setSearchText = (e: Event) => {
searchText.value = (e.target as HTMLInputElement).value;
};
const setActiveSearchItem = (channel?: ClientChan) => {
if (!results.value.length) {
return;
}
if (!channel) {
channel = results.value[0].channel;
}
activeSearchItem.value = channel;
};
const scrollToActive = () => {
// Scroll the list if needed after the active class is applied
void nextTick(() => {
const el = networklist.value?.querySelector(".channel-list-item.active");
if (el) {
el.scrollIntoView({block: "nearest", inline: "nearest"});
}
});
};
const selectResult = () => {
if (!searchText.value || !results.value.length) {
return;
}
socket.emit("sort", {
type: "channels",
target: channel.network.uuid,
order: channel.network.channels.map((c) => c.id),
});
},
if (activeSearchItem.value) {
switchToChannel(activeSearchItem.value);
deactivateSearch();
scrollToActive();
}
};
const navigateResults = (event: Event, direction: number) => {
// Prevent propagation to stop global keybind handler from capturing pagedown/pageup
// and redirecting it to the message list container for scrolling
event.stopImmediatePropagation();
event.preventDefault();
if (!searchText.value) {
return;
}
const channels = results.value.map((r) => r.channel);
// Bail out if there's no channels to select
if (!channels.length) {
activeSearchItem.value = null;
return;
}
let currentIndex = activeSearchItem.value
? channels.indexOf(activeSearchItem.value)
: -1;
// If there's no active channel select the first or last one depending on direction
if (!activeSearchItem.value || currentIndex === -1) {
activeSearchItem.value = direction ? channels[0] : channels[channels.length - 1];
scrollToActive();
return;
}
currentIndex += direction;
// Wrap around the list if necessary. Normaly each loop iterates once at most,
// but might iterate more often if pgup or pgdown are used in a very short list
while (currentIndex < 0) {
currentIndex += channels.length;
}
while (currentIndex > channels.length - 1) {
currentIndex -= channels.length;
}
activeSearchItem.value = channels[currentIndex];
scrollToActive();
};
watch(searchText, () => {
setActiveSearchItem();
});
onMounted(() => {
Mousetrap.bind("alt+shift+right", expandNetwork);
Mousetrap.bind("alt+shift+left", collapseNetwork);
Mousetrap.bind("alt+j", toggleSearch);
});
onBeforeUnmount(() => {
Mousetrap.unbind("alt+shift+right");
Mousetrap.unbind("alt+shift+left");
Mousetrap.unbind("alt+j");
});
const networkContainerRef = ref<HTMLDivElement>();
const channelRefs = ref<{[key: string]: HTMLDivElement}>({});
return {
store,
networklist,
searchInput,
searchText,
results,
activeSearchItem,
LONG_TOUCH_DURATION,
activateSearch,
deactivateSearch,
toggleSearch,
setSearchText,
setActiveSearchItem,
scrollToActive,
selectResult,
navigateResults,
onChannelSort,
onNetworkSort,
onDraggableTouchStart,
onDraggableTouchMove,
onDraggableTouchEnd,
onDraggableChoose,
onDraggableUnchoose,
};
},
};
});
</script>

View file

@ -1,9 +1,5 @@
<template>
<ChannelWrapper
:network="network"
:channel="channel"
:active-channel="activeChannel"
>
<ChannelWrapper v-bind="$props" :channel="channel">
<button
v-if="network.channels.length > 1"
:aria-controls="'network-' + network.uuid"
@ -11,16 +7,12 @@
:aria-expanded="!network.isCollapsed"
class="collapse-network"
@click.stop="onCollapseClick"
><span class="collapse-network-icon" /></button>
<span
v-else
class="collapse-network"
/>
>
<span class="collapse-network-icon" />
</button>
<span v-else class="collapse-network" />
<div class="lobby-wrap">
<span
:title="channel.name"
class="name"
>{{ channel.name }}</span>
<span :title="channel.name" class="name">{{ channel.name }}</span>
<span
v-if="network.status.connected && !network.status.secure"
class="not-secure-tooltip tooltipped tooltipped-w"
@ -35,64 +27,75 @@
>
<span class="not-connected-icon" />
</span>
<span
v-if="channel.unread"
:class="{ highlight: channel.highlight }"
class="badge"
>{{ channel.unread | roundBadgeNumber }}</span>
<span v-if="channel.unread" :class="{highlight: channel.highlight}" class="badge">{{
unreadCount
}}</span>
</div>
<span
:aria-label="joinChannelLabel"
class="add-channel-tooltip tooltipped tooltipped-w tooltipped-no-touch"
>
<button
:class="['add-channel', { opened: isJoinChannelShown }]"
:class="['add-channel', {opened: isJoinChannelShown}]"
:aria-controls="'join-channel-' + channel.id"
:aria-label="joinChannelLabel"
@click.stop="$emit('toggleJoinChannel')"
@click.stop="$emit('toggle-join-channel')"
/>
</span>
</ChannelWrapper>
</template>
<script>
<script lang="ts">
import {computed, defineComponent, PropType} from "vue";
import collapseNetwork from "../js/helpers/collapseNetwork";
import roundBadgeNumber from "../js/helpers/roundBadgeNumber";
import ChannelWrapper from "./ChannelWrapper.vue";
const storage = require("../js/localStorage");
export default {
import type {ClientChan, ClientNetwork} from "../js/types";
export default defineComponent({
name: "Channel",
components: {
ChannelWrapper,
},
props: {
activeChannel: Object,
network: Object,
network: {
type: Object as PropType<ClientNetwork>,
required: true,
},
isJoinChannelShown: Boolean,
active: Boolean,
isFiltering: Boolean,
},
computed: {
channel() {
return this.network.channels[0];
},
joinChannelLabel() {
return this.isJoinChannelShown ? "Cancel" : "Join a channel…";
},
},
methods: {
onCollapseClick() {
const networks = new Set(JSON.parse(storage.get("thelounge.networks.collapsed")));
this.network.isCollapsed = !this.network.isCollapsed;
emits: ["toggle-join-channel"],
setup(props) {
const channel = computed(() => {
return props.network.channels[0];
});
if (this.network.isCollapsed) {
networks.add(this.network.uuid);
} else {
networks.delete(this.network.uuid);
}
const joinChannelLabel = computed(() => {
return props.isJoinChannelShown ? "Cancel" : "Join a channel…";
});
storage.set("thelounge.networks.collapsed", JSON.stringify([...networks]));
},
getExpandLabel(network) {
const unreadCount = computed(() => {
return roundBadgeNumber(channel.value.unread);
});
const onCollapseClick = () => {
collapseNetwork(props.network, !props.network.isCollapsed);
};
const getExpandLabel = (network: ClientNetwork) => {
return network.isCollapsed ? "Expand" : "Collapse";
},
};
return {
channel,
joinChannelLabel,
unreadCount,
onCollapseClick,
getExpandLabel,
};
},
};
});
</script>

View file

@ -1,21 +1,22 @@
<script>
const parse = require("../js/libs/handlebars/parse");
<script lang="ts">
import {defineComponent, PropType, h} from "vue";
import parse from "../js/helpers/parse";
import type {ClientMessage, ClientNetwork} from "../js/types";
export default {
export default defineComponent({
name: "ParsedMessage",
functional: true,
props: {
text: String,
message: Object,
network: Object,
message: {type: Object as PropType<ClientMessage | string>, required: false},
network: {type: Object as PropType<ClientNetwork>, required: false},
},
render(createElement, context) {
render(context) {
return parse(
createElement,
typeof context.props.text !== "undefined" ? context.props.text : context.props.message.text,
context.props.message,
context.props.network
typeof context.text !== "undefined" ? context.text : context.message.text,
context.message,
context.network
);
},
};
});
</script>

View file

@ -0,0 +1,37 @@
<template>
<div>
<slot :is-visible="isVisible" />
<span
ref="revealButton"
type="button"
:class="[
'reveal-password tooltipped tooltipped-n tooltipped-no-delay',
{'reveal-password-visible': isVisible},
]"
:aria-label="isVisible ? 'Hide password' : 'Show password'"
@click="onClick"
>
<span :aria-label="isVisible ? 'Hide password' : 'Show password'" />
</span>
</div>
</template>
<script lang="ts">
import {defineComponent, ref} from "vue";
export default defineComponent({
name: "RevealPassword",
setup() {
const isVisible = ref(false);
const onClick = () => {
isVisible.value = !isVisible.value;
};
return {
isVisible,
onClick,
};
},
});
</script>

View file

@ -0,0 +1,66 @@
<template>
<Chat
v-if="activeChannel"
:network="activeChannel.network"
:channel="activeChannel.channel"
:focused="parseInt(String(route.query.focused), 10)"
@channel-changed="channelChanged"
/>
</template>
<script lang="ts">
import {watch, computed, defineComponent, onMounted} from "vue";
import {useRoute} from "vue-router";
import {useStore} from "../js/store";
import {ClientChan} from "../js/types";
// Temporary component for routing channels and lobbies
import Chat from "./Chat.vue";
export default defineComponent({
name: "RoutedChat",
components: {
Chat,
},
setup() {
const route = useRoute();
const store = useStore();
const activeChannel = computed(() => {
const chanId = parseInt(String(route.params.id || ""), 10);
const channel = store.getters.findChannel(chanId);
return channel;
});
const setActiveChannel = () => {
if (activeChannel.value) {
store.commit("activeChannel", activeChannel.value);
}
};
watch(activeChannel, () => {
setActiveChannel();
});
onMounted(() => {
setActiveChannel();
});
const channelChanged = (channel: ClientChan) => {
const chanId = channel.id;
const chanInStore = store.getters.findChannel(chanId);
if (chanInStore?.channel) {
chanInStore.channel.unread = 0;
chanInStore.channel.highlight = 0;
}
};
return {
route,
activeChannel,
channelChanged,
};
},
});
</script>

View file

@ -0,0 +1,83 @@
<template>
<div class="session-item">
<div class="session-item-info">
<strong>{{ session.agent }}</strong>
<a :href="'https://ipinfo.io/' + session.ip" target="_blank" rel="noopener">{{
session.ip
}}</a>
<p v-if="session.active > 1" class="session-usage">
Active in {{ session.active }} browsers
</p>
<p v-else-if="!session.current && !session.active" class="session-usage">
Last used on <time>{{ lastUse }}</time>
</p>
</div>
<div class="session-item-btn">
<button class="btn" @click.prevent="signOut">
<template v-if="session.current">Sign out</template>
<template v-else>Revoke</template>
</button>
</div>
</div>
</template>
<style>
.session-list .session-item {
display: flex;
font-size: 14px;
}
.session-list .session-item-info {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.session-list .session-item-btn {
flex-shrink: 0;
}
.session-list .session-usage {
font-style: italic;
color: var(--body-color-muted);
}
</style>
<script lang="ts">
import {computed, defineComponent, PropType} from "vue";
import localetime from "../js/helpers/localetime";
import Auth from "../js/auth";
import socket from "../js/socket";
import {ClientSession} from "../js/store";
export default defineComponent({
name: "Session",
props: {
session: {
type: Object as PropType<ClientSession>,
required: true,
},
},
setup(props) {
const lastUse = computed(() => {
return localetime(props.session.lastUse);
});
const signOut = () => {
if (!props.session.current) {
socket.emit("sign-out", props.session.token);
} else {
socket.emit("sign-out");
Auth.signout();
}
};
return {
lastUse,
signOut,
};
},
});
</script>

View file

@ -0,0 +1,197 @@
<template>
<div>
<div
v-if="
!store.state.serverConfiguration?.public &&
!store.state.serverConfiguration?.ldapEnabled
"
id="change-password"
role="group"
aria-labelledby="label-change-password"
>
<h2 id="label-change-password">Change password</h2>
<div class="password-container">
<label for="current-password" class="sr-only"> Enter current password </label>
<RevealPassword v-slot:default="slotProps">
<input
id="current-password"
v-model="old_password"
autocomplete="current-password"
:type="slotProps.isVisible ? 'text' : 'password'"
name="old_password"
class="input"
placeholder="Enter current password"
/>
</RevealPassword>
</div>
<div class="password-container">
<label for="new-password" class="sr-only"> Enter desired new password </label>
<RevealPassword v-slot:default="slotProps">
<input
id="new-password"
v-model="new_password"
:type="slotProps.isVisible ? 'text' : 'password'"
name="new_password"
autocomplete="new-password"
class="input"
placeholder="Enter desired new password"
/>
</RevealPassword>
</div>
<div class="password-container">
<label for="new-password-verify" class="sr-only"> Repeat new password </label>
<RevealPassword v-slot:default="slotProps">
<input
id="new-password-verify"
v-model="verify_password"
:type="slotProps.isVisible ? 'text' : 'password'"
name="verify_password"
autocomplete="new-password"
class="input"
placeholder="Repeat new password"
/>
</RevealPassword>
</div>
<div
v-if="passwordChangeStatus && passwordChangeStatus.success"
class="feedback success"
>
Successfully updated your password
</div>
<div
v-else-if="passwordChangeStatus && passwordChangeStatus.error"
class="feedback error"
>
{{ passwordErrors[passwordChangeStatus.error] }}
</div>
<div>
<button type="submit" class="btn" @click.prevent="changePassword">
Change password
</button>
</div>
</div>
<div v-if="!store.state.serverConfiguration?.public" class="session-list" role="group">
<h2>Sessions</h2>
<h3>Current session</h3>
<Session v-if="currentSession" :session="currentSession" />
<template v-if="activeSessions.length > 0">
<h3>Active sessions</h3>
<Session
v-for="session in activeSessions"
:key="session.token"
:session="session"
/>
</template>
<h3>Other sessions</h3>
<p v-if="store.state.sessions.length === 0">Loading</p>
<p v-else-if="otherSessions.length === 0">
<em>You are not currently logged in to any other device.</em>
</p>
<Session
v-for="session in otherSessions"
v-else
:key="session.token"
:session="session"
/>
</div>
</div>
</template>
<script lang="ts">
import socket from "../../js/socket";
import RevealPassword from "../RevealPassword.vue";
import Session from "../Session.vue";
import {computed, defineComponent, onMounted, PropType, ref} from "vue";
import {useStore} from "../../js/store";
export default defineComponent({
name: "UserSettings",
components: {
RevealPassword,
Session,
},
setup() {
const store = useStore();
const passwordErrors = {
missing_fields: "Please fill in all fields",
password_mismatch: "Both new password fields must match",
password_incorrect: "The current password field does not match your account password",
update_failed: "Failed to update your password",
};
const passwordChangeStatus = ref<{
success: boolean;
error: keyof typeof passwordErrors;
}>();
const old_password = ref("");
const new_password = ref("");
const verify_password = ref("");
const currentSession = computed(() => {
return store.state.sessions.find((item) => item.current);
});
const activeSessions = computed(() => {
return store.state.sessions.filter((item) => !item.current && item.active > 0);
});
const otherSessions = computed(() => {
return store.state.sessions.filter((item) => !item.current && !item.active);
});
onMounted(() => {
socket.emit("sessions:get");
});
const changePassword = () => {
const data = {
old_password: old_password.value,
new_password: new_password.value,
verify_password: verify_password.value,
};
if (!data.old_password || !data.new_password || !data.verify_password) {
passwordChangeStatus.value = {
success: false,
error: "missing_fields",
};
return;
}
if (data.new_password !== data.verify_password) {
passwordChangeStatus.value = {
success: false,
error: "password_mismatch",
};
return;
}
socket.once("change-password", (response) => {
// TODO type
passwordChangeStatus.value = response as any;
});
socket.emit("change-password", data);
};
return {
store,
passwordChangeStatus,
passwordErrors,
currentSession,
activeSessions,
otherSessions,
changePassword,
old_password,
new_password,
verify_password,
};
},
});
</script>

View file

@ -0,0 +1,179 @@
<template>
<div>
<h2>Messages</h2>
<div>
<label class="opt">
<input :checked="store.state.settings.motd" type="checkbox" name="motd" />
Show <abbr title="Message Of The Day">MOTD</abbr>
</label>
</div>
<div>
<label class="opt">
<input
:checked="store.state.settings.showSeconds"
type="checkbox"
name="showSeconds"
/>
Include seconds in timestamp
</label>
</div>
<div>
<label class="opt">
<input
:checked="store.state.settings.use12hClock"
type="checkbox"
name="use12hClock"
/>
Use 12-hour timestamps
</label>
</div>
<template v-if="store.state.serverConfiguration?.prefetch">
<h2>Link previews</h2>
<div>
<label class="opt">
<input :checked="store.state.settings.media" type="checkbox" name="media" />
Auto-expand media
</label>
</div>
<div>
<label class="opt">
<input :checked="store.state.settings.links" type="checkbox" name="links" />
Auto-expand websites
</label>
</div>
</template>
<h2 id="label-status-messages">
Status messages
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="Joins, parts, quits, kicks, nick changes, and mode changes"
>
<button class="extra-help" />
</span>
</h2>
<div role="group" aria-labelledby="label-status-messages">
<label class="opt">
<input
:checked="store.state.settings.statusMessages === 'shown'"
type="radio"
name="statusMessages"
value="shown"
/>
Show all status messages individually
</label>
<label class="opt">
<input
:checked="store.state.settings.statusMessages === 'condensed'"
type="radio"
name="statusMessages"
value="condensed"
/>
Condense status messages together
</label>
<label class="opt">
<input
:checked="store.state.settings.statusMessages === 'hidden'"
type="radio"
name="statusMessages"
value="hidden"
/>
Hide all status messages
</label>
</div>
<h2>Visual Aids</h2>
<div>
<label class="opt">
<input
:checked="store.state.settings.coloredNicks"
type="checkbox"
name="coloredNicks"
/>
Enable colored nicknames
</label>
<label class="opt">
<input
:checked="store.state.settings.autocomplete"
type="checkbox"
name="autocomplete"
/>
Enable autocomplete
</label>
</div>
<div>
<label class="opt">
<label for="nickPostfix" class="opt">
Nick autocomplete postfix
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="Nick autocomplete postfix (for example a comma)"
>
<button class="extra-help" />
</span>
</label>
<input
id="nickPostfix"
:value="store.state.settings.nickPostfix"
type="text"
name="nickPostfix"
class="input"
placeholder="Nick autocomplete postfix (e.g. ', ')"
/>
</label>
</div>
<h2>Theme</h2>
<div>
<label for="theme-select" class="sr-only">Theme</label>
<select
id="theme-select"
:value="store.state.settings.theme"
name="theme"
class="input"
>
<option
v-for="theme in store.state.serverConfiguration?.themes"
:key="theme.name"
:value="theme.name"
>
{{ theme.displayName }}
</option>
</select>
</div>
<div>
<h2>Custom Stylesheet</h2>
<label for="user-specified-css-input" class="sr-only">
Custom stylesheet. You can override any style with CSS here.
</label>
<textarea
id="user-specified-css-input"
:value="store.state.settings.userStyles"
class="input"
name="userStyles"
placeholder="/* You can override any style with CSS here */"
/>
</div>
</div>
</template>
<style>
textarea#user-specified-css-input {
height: 100px;
}
</style>
<script lang="ts">
import {defineComponent} from "vue";
import {useStore} from "../../js/store";
export default defineComponent({
name: "AppearanceSettings",
setup() {
const store = useStore();
return {
store,
};
},
});
</script>

View file

@ -0,0 +1,175 @@
<template>
<div>
<div v-if="canRegisterProtocol || hasInstallPromptEvent">
<h2>Native app</h2>
<button
v-if="hasInstallPromptEvent"
type="button"
class="btn"
@click.prevent="nativeInstallPrompt"
>
Add The Lounge to Home screen
</button>
<button
v-if="canRegisterProtocol"
type="button"
class="btn"
@click.prevent="registerProtocol"
>
Open irc:// URLs with The Lounge
</button>
</div>
<div v-if="store.state.serverConfiguration?.fileUpload">
<h2>File uploads</h2>
<div>
<label class="opt">
<input
:checked="store.state.settings.uploadCanvas"
type="checkbox"
name="uploadCanvas"
/>
Attempt to remove metadata from images before uploading
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="This option renders the image into a canvas element to remove metadata from the image.
This may break orientation if your browser does not support that."
>
<button class="extra-help" />
</span>
</label>
</div>
</div>
<div v-if="!store.state.serverConfiguration?.public">
<h2>Settings synchronisation</h2>
<label class="opt">
<input
:checked="store.state.settings.syncSettings"
type="checkbox"
name="syncSettings"
/>
Synchronize settings with other clients
</label>
<template v-if="!store.state.settings.syncSettings">
<div v-if="store.state.serverHasSettings" class="settings-sync-panel">
<p>
<strong>Warning:</strong> Checking this box will override the settings of
this client with those stored on the server.
</p>
<p>
Use the button below to enable synchronization, and override any settings
already synced to the server.
</p>
<button type="button" class="btn btn-small" @click="onForceSyncClick">
Sync settings and enable
</button>
</div>
<div v-else class="settings-sync-panel">
<p>
<strong>Warning:</strong> No settings have been synced before. Enabling this
will sync all settings of this client as the base for other clients.
</p>
</div>
</template>
</div>
<div v-if="!store.state.serverConfiguration?.public">
<h2>Automatic away message</h2>
<label class="opt">
<label for="awayMessage" class="sr-only">Automatic away message</label>
<input
id="awayMessage"
:value="store.state.settings.awayMessage"
type="text"
name="awayMessage"
class="input"
placeholder="Away message if The Lounge is not open"
/>
</label>
</div>
</div>
</template>
<style></style>
<script lang="ts">
import {computed, defineComponent, onMounted, ref} from "vue";
import {useStore} from "../../js/store";
import {BeforeInstallPromptEvent} from "../../js/types";
let installPromptEvent: BeforeInstallPromptEvent | null = null;
window.addEventListener("beforeinstallprompt", (e) => {
e.preventDefault();
installPromptEvent = e as BeforeInstallPromptEvent;
});
export default defineComponent({
name: "GeneralSettings",
setup() {
const store = useStore();
const canRegisterProtocol = ref(false);
const hasInstallPromptEvent = computed(() => {
// TODO: This doesn't hide the button after clicking
return installPromptEvent !== null;
});
onMounted(() => {
// Enable protocol handler registration if supported,
// and the network configuration is not locked
canRegisterProtocol.value =
!!window.navigator.registerProtocolHandler &&
!store.state.serverConfiguration?.lockNetwork;
});
const nativeInstallPrompt = () => {
if (!installPromptEvent) {
return;
}
installPromptEvent.prompt().catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
});
installPromptEvent = null;
};
const onForceSyncClick = () => {
store.dispatch("settings/syncAll", true).catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
});
store
.dispatch("settings/update", {
name: "syncSettings",
value: true,
sync: true,
})
.catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
});
};
const registerProtocol = () => {
const uri = document.location.origin + document.location.pathname + "?uri=%s";
// @ts-expect-error
// the third argument is deprecated but recommended for compatibility: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler
window.navigator.registerProtocolHandler("irc", uri, "The Lounge");
// @ts-expect-error
window.navigator.registerProtocolHandler("ircs", uri, "The Lounge");
};
return {
store,
canRegisterProtocol,
hasInstallPromptEvent,
nativeInstallPrompt,
onForceSyncClick,
registerProtocol,
};
},
});
</script>

View file

@ -0,0 +1,103 @@
<template>
<!-- 220px is the width of the sidebar, and we add 100px to allow for the text -->
<aside class="settings-menu">
<h2>Settings</h2>
<ul role="navigation" aria-label="Settings tabs">
<SettingTabItem name="General" class-name="general" to="" />
<SettingTabItem name="Appearance" class-name="appearance" to="appearance" />
<SettingTabItem name="Notifications" class-name="notifications" to="notifications" />
<SettingTabItem name="Account" class-name="account" to="account" />
</ul>
</aside>
</template>
<style>
.settings-menu {
position: fixed;
/* top: Header + (padding bottom of h2 - border) */
top: calc(45px + 5px);
/* Mid page minus width of container and 30 pixels for padding */
margin-left: calc(50% - 480px - 30px);
}
/** The calculation is mobile + 2/3 of container width. Fairly arbitrary. */
@media screen and (max-width: calc(768px + 320px)) {
.settings-menu {
position: static;
width: min(480px, 100%);
align-self: center;
margin: 0 auto;
padding: 0 15px;
}
}
.settings-menu ul {
padding: 0;
}
.settings-menu li {
font-size: 18px;
list-style: none;
}
.settings-menu button {
color: var(--body-color-muted);
width: 100%;
height: 100%;
display: inline-block;
text-align: left;
}
.settings-menu li:not(:last-of-type) button {
margin-bottom: 8px;
}
.settings-menu button::before {
width: 18px;
height: 18px;
display: inline-block;
content: "";
margin-right: 8px;
}
.settings-menu .appearance::before {
content: "\f108"; /* http://fontawesome.io/icon/desktop/ */
}
.settings-menu .account::before {
content: "\f007"; /* http://fontawesome.io/icon/user/ */
}
.settings-menu .messages::before {
content: "\f0e0"; /* http://fontawesome.io/icon/envelope/ */
}
.settings-menu .notifications::before {
content: "\f0f3"; /* http://fontawesome.io/icon/bell/ */
}
.settings-menu .general::before {
content: "\f013"; /* http://fontawesome.io/icon/cog/ */
}
.settings-menu button:hover,
.settings-menu button.active {
color: var(--body-color);
}
.settings-menu button.active {
cursor: default;
}
</style>
<script lang="ts">
import SettingTabItem from "./SettingTabItem.vue";
import {defineComponent} from "vue";
export default defineComponent({
name: "SettingsTabs",
components: {
SettingTabItem,
},
});
</script>

View file

@ -0,0 +1,188 @@
<template>
<div>
<template v-if="!store.state.serverConfiguration?.public">
<h2>Push Notifications</h2>
<div>
<button
id="pushNotifications"
type="button"
class="btn"
:disabled="
store.state.pushNotificationState !== 'supported' &&
store.state.pushNotificationState !== 'subscribed'
"
@click="onPushButtonClick"
>
<template v-if="store.state.pushNotificationState === 'subscribed'">
Unsubscribe from push notifications
</template>
<template v-else-if="store.state.pushNotificationState === 'loading'">
Loading
</template>
<template v-else> Subscribe to push notifications </template>
</button>
<div v-if="store.state.pushNotificationState === 'nohttps'" class="error">
<strong>Warning</strong>: Push notifications are only supported over HTTPS
connections.
</div>
<div v-if="store.state.pushNotificationState === 'unsupported'" class="error">
<strong>Warning</strong>:
<span>Push notifications are not supported by your browser.</span>
</div>
</div>
</template>
<h2>Browser Notifications</h2>
<div>
<label class="opt">
<input
id="desktopNotifications"
:checked="store.state.settings.desktopNotifications"
:disabled="store.state.desktopNotificationState === 'nohttps'"
type="checkbox"
name="desktopNotifications"
/>
Enable browser notifications<br />
<div v-if="store.state.desktopNotificationState === 'unsupported'" class="error">
<strong>Warning</strong>: Notifications are not supported by your browser.
</div>
<div
v-if="store.state.desktopNotificationState === 'nohttps'"
id="warnBlockedDesktopNotifications"
class="error"
>
<strong>Warning</strong>: Notifications are only supported over HTTPS
connections.
</div>
<div
v-if="store.state.desktopNotificationState === 'blocked'"
id="warnBlockedDesktopNotifications"
class="error"
>
<strong>Warning</strong>: Notifications are blocked by your browser.
</div>
</label>
</div>
<div>
<label class="opt">
<input
:checked="store.state.settings.notification"
type="checkbox"
name="notification"
/>
Enable notification sound
</label>
</div>
<div>
<div class="opt">
<button id="play" @click.prevent="playNotification">Play sound</button>
</div>
</div>
<div>
<label class="opt">
<input
:checked="store.state.settings.notifyAllMessages"
type="checkbox"
name="notifyAllMessages"
/>
Enable notification for all messages
</label>
</div>
<div v-if="!store.state.serverConfiguration?.public">
<label class="opt">
<label for="highlights" class="opt">
Custom highlights
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="If a message contains any of these comma-separated
expressions, it will trigger a highlight."
>
<button class="extra-help" />
</span>
</label>
<input
id="highlights"
:value="store.state.settings.highlights"
type="text"
name="highlights"
class="input"
autocomplete="off"
placeholder="Comma-separated, e.g.: word, some more words, anotherword"
/>
</label>
</div>
<div v-if="!store.state.serverConfiguration?.public">
<label class="opt">
<label for="highlightExceptions" class="opt">
Highlight exceptions
<span
class="tooltipped tooltipped-n tooltipped-no-delay"
aria-label="If a message contains any of these comma-separated
expressions, it will not trigger a highlight even if it contains
your nickname or expressions defined in custom highlights."
>
<button class="extra-help" />
</span>
</label>
<input
id="highlightExceptions"
:value="store.state.settings.highlightExceptions"
type="text"
name="highlightExceptions"
class="input"
autocomplete="off"
placeholder="Comma-separated, e.g.: word, some more words, anotherword"
/>
</label>
</div>
</div>
</template>
<script lang="ts">
import {computed, defineComponent} from "vue";
import {useStore} from "../../js/store";
import webpush from "../../js/webpush";
export default defineComponent({
name: "NotificationSettings",
setup() {
const store = useStore();
const isIOS = computed(
() =>
[
"iPad Simulator",
"iPhone Simulator",
"iPod Simulator",
"iPad",
"iPhone",
"iPod",
].includes(navigator.platform) ||
// iPad on iOS 13 detection
(navigator.userAgent.includes("Mac") && "ontouchend" in document)
);
const playNotification = () => {
const pop = new Audio();
pop.src = "audio/pop.wav";
// eslint-disable-next-line
pop.play();
};
const onPushButtonClick = () => {
webpush.togglePushSubscription();
};
return {
isIOS,
store,
playNotification,
onPushButtonClick,
};
},
});
</script>

View file

@ -0,0 +1,43 @@
<template>
<li :aria-label="name" role="tab" :aria-selected="route.name === name" aria-controls="settings">
<router-link v-slot:default="{navigate, isExactActive}" :to="'/settings/' + to" custom>
<button
:class="['icon', className, {active: isExactActive}]"
@click="navigate"
@keypress.enter="navigate"
>
{{ name }}
</button>
</router-link>
</li>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import {useRoute} from "vue-router";
export default defineComponent({
name: "SettingTabListItem",
props: {
name: {
type: String,
required: true,
},
className: {
type: String,
required: true,
},
to: {
type: String,
required: true,
},
},
setup() {
const route = useRoute();
return {
route,
};
},
});
</script>

View file

@ -0,0 +1,269 @@
<template>
<aside id="sidebar" ref="sidebar">
<div class="scrollable-area">
<div class="logo-container">
<img
:src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg.svg`"
class="logo"
alt="The Lounge"
role="presentation"
/>
<img
:src="`img/logo-${isPublic() ? 'horizontal-' : ''}transparent-bg-inverted.svg`"
class="logo-inverted"
alt="The Lounge"
role="presentation"
/>
<span
v-if="isDevelopment"
title="The Lounge has been built in development mode"
:style="{
backgroundColor: '#ff9e18',
color: '#000',
padding: '2px',
borderRadius: '4px',
fontSize: '12px',
}"
>DEVELOPER</span
>
</div>
<NetworkList />
</div>
<footer id="footer">
<span
class="tooltipped tooltipped-n tooltipped-no-touch"
aria-label="Connect to network"
><router-link
v-slot:default="{navigate, isActive}"
to="/connect"
role="tab"
aria-controls="connect"
>
<button
:class="['icon', 'connect', {active: isActive}]"
:aria-selected="isActive"
@click="navigate"
@keypress.enter="navigate"
/> </router-link
></span>
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Settings"
><router-link
v-slot:default="{navigate, isActive}"
to="/settings"
role="tab"
aria-controls="settings"
>
<button
:class="['icon', 'settings', {active: isActive}]"
:aria-selected="isActive"
@click="navigate"
@keypress.enter="navigate"
></button> </router-link
></span>
<span
class="tooltipped tooltipped-n tooltipped-no-touch"
:aria-label="
store.state.serverConfiguration?.isUpdateAvailable
? 'Help\n(update available)'
: 'Help'
"
><router-link
v-slot:default="{navigate, isActive}"
to="/help"
role="tab"
aria-controls="help"
>
<button
:aria-selected="route.name === 'Help'"
:class="[
'icon',
'help',
{notified: store.state.serverConfiguration?.isUpdateAvailable},
{active: isActive},
]"
@click="navigate"
@keypress.enter="navigate"
></button> </router-link
></span>
</footer>
</aside>
</template>
<script lang="ts">
import {defineComponent, nextTick, onMounted, onUnmounted, PropType, ref} from "vue";
import {useRoute} from "vue-router";
import {useStore} from "../js/store";
import NetworkList from "./NetworkList.vue";
export default defineComponent({
name: "Sidebar",
components: {
NetworkList,
},
props: {
overlay: {type: Object as PropType<HTMLElement | null>, required: true},
},
setup(props) {
const isDevelopment = process.env.NODE_ENV !== "production";
const store = useStore();
const route = useRoute();
const touchStartPos = ref<Touch | null>();
const touchCurPos = ref<Touch | null>();
const touchStartTime = ref<number>(0);
const menuWidth = ref<number>(0);
const menuIsMoving = ref<boolean>(false);
const menuIsAbsolute = ref<boolean>(false);
const sidebar = ref<HTMLElement | null>(null);
const toggle = (state: boolean) => {
store.commit("sidebarOpen", state);
};
const onTouchMove = (e: TouchEvent) => {
const touch = (touchCurPos.value = e.touches.item(0));
if (
!touch ||
!touchStartPos.value ||
!touchStartPos.value.screenX ||
!touchStartPos.value.screenY
) {
return;
}
let distX = touch.screenX - touchStartPos.value.screenX;
const distY = touch.screenY - touchStartPos.value.screenY;
if (!menuIsMoving.value) {
// tan(45°) is 1. Gestures in 0°-45° (< 1) are considered horizontal, so
// menu must be open; gestures in 45°-90° (>1) are considered vertical, so
// chat windows must be scrolled.
if (Math.abs(distY / distX) >= 1) {
// eslint-disable-next-line no-use-before-define
onTouchEnd();
return;
}
const devicePixelRatio = window.devicePixelRatio || 2;
if (Math.abs(distX) > devicePixelRatio) {
store.commit("sidebarDragging", true);
menuIsMoving.value = true;
}
}
// Do not animate the menu on desktop view
if (!menuIsAbsolute.value) {
return;
}
if (store.state.sidebarOpen) {
distX += menuWidth.value;
}
if (distX > menuWidth.value) {
distX = menuWidth.value;
} else if (distX < 0) {
distX = 0;
}
if (sidebar.value) {
sidebar.value.style.transform = "translate3d(" + distX.toString() + "px, 0, 0)";
}
if (props.overlay) {
props.overlay.style.opacity = `${distX / menuWidth.value}`;
}
};
const onTouchEnd = () => {
if (!touchStartPos.value?.screenX || !touchCurPos.value?.screenX) {
return;
}
const diff = touchCurPos.value.screenX - touchStartPos.value.screenX;
const absDiff = Math.abs(diff);
if (
absDiff > menuWidth.value / 2 ||
(Date.now() - touchStartTime.value < 180 && absDiff > 50)
) {
toggle(diff > 0);
}
document.body.removeEventListener("touchmove", onTouchMove);
document.body.removeEventListener("touchend", onTouchEnd);
store.commit("sidebarDragging", false);
touchStartPos.value = null;
touchCurPos.value = null;
touchStartTime.value = 0;
menuIsMoving.value = false;
void nextTick(() => {
if (sidebar.value) {
sidebar.value.style.transform = "";
}
if (props.overlay) {
props.overlay.style.opacity = "";
}
});
};
const onTouchStart = (e: TouchEvent) => {
if (!sidebar.value) {
return;
}
touchStartPos.value = touchCurPos.value = e.touches.item(0);
if (e.touches.length !== 1) {
onTouchEnd();
return;
}
const styles = window.getComputedStyle(sidebar.value);
menuWidth.value = parseFloat(styles.width);
menuIsAbsolute.value = styles.position === "absolute";
if (
!store.state.sidebarOpen ||
(touchStartPos.value?.screenX && touchStartPos.value.screenX > menuWidth.value)
) {
touchStartTime.value = Date.now();
document.body.addEventListener("touchmove", onTouchMove, {passive: true});
document.body.addEventListener("touchend", onTouchEnd, {passive: true});
}
};
onMounted(() => {
document.body.addEventListener("touchstart", onTouchStart, {passive: true});
});
onUnmounted(() => {
document.body.removeEventListener("touchstart", onTouchStart);
});
const isPublic = () => document.body.classList.contains("public");
return {
isDevelopment,
store,
route,
sidebar,
toggle,
onTouchStart,
onTouchMove,
onTouchEnd,
isPublic,
};
},
});
</script>

View file

@ -0,0 +1,19 @@
<template>
<button class="lt" aria-label="Toggle channel list" @click="store.commit('toggleSidebar')" />
</template>
<script lang="ts">
import {defineComponent} from "vue";
import {useStore} from "../js/store";
export default defineComponent({
name: "SidebarToggle",
setup() {
const store = useStore();
return {
store,
};
},
});
</script>

View file

@ -8,24 +8,38 @@
</tr>
</thead>
<tbody>
<tr
v-for="ban in channel.data"
:key="ban.hostmask"
>
<td class="hostmask">{{ ban.hostmask }}</td>
<tr v-for="ban in channel.data" :key="ban.hostmask">
<td class="hostmask"><ParsedMessage :network="network" :text="ban.hostmask" /></td>
<td class="banned_by">{{ ban.banned_by }}</td>
<td class="banned_at">{{ ban.banned_at | localetime }}</td>
<td class="banned_at">{{ localetime(ban.banned_at) }}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
<script lang="ts">
import ParsedMessage from "../ParsedMessage.vue";
import localeTime from "../../js/helpers/localetime";
import {defineComponent, PropType} from "vue";
import type {ClientNetwork, ClientChan} from "../../js/types";
export default defineComponent({
name: "ListBans",
props: {
network: Object,
channel: Object,
components: {
ParsedMessage,
},
};
props: {
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
},
setup() {
const localetime = (date: number | Date) => {
return localeTime(date);
};
return {
localetime,
};
},
});
</script>

View file

@ -1,9 +1,6 @@
<template>
<span v-if="channel.data.text">{{ channel.data.text }}</span>
<table
v-else
class="channel-list"
>
<table v-else class="channel-list">
<thead>
<tr>
<th class="channel">Channel</th>
@ -12,35 +9,28 @@
</tr>
</thead>
<tbody>
<tr
v-for="chan in channel.data"
:key="chan.channel"
>
<td class="channel"><ParsedMessage
:network="network"
:text="chan.channel"
/></td>
<tr v-for="chan in channel.data" :key="chan.channel">
<td class="channel"><ParsedMessage :network="network" :text="chan.channel" /></td>
<td class="users">{{ chan.num_users }}</td>
<td class="topic"><ParsedMessage
:network="network"
:text="chan.topic"
/></td>
<td class="topic"><ParsedMessage :network="network" :text="chan.topic" /></td>
</tr>
</tbody>
</table>
</template>
<script>
<script lang="ts">
import {defineComponent, PropType} from "vue";
import {ClientChan, ClientNetwork} from "../../js/types";
import ParsedMessage from "../ParsedMessage.vue";
export default {
export default defineComponent({
name: "ListChannels",
components: {
ParsedMessage,
},
props: {
network: Object,
channel: Object,
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
},
};
});
</script>

View file

@ -7,23 +7,33 @@
</tr>
</thead>
<tbody>
<tr
v-for="user in channel.data"
:key="user.hostmask"
>
<td class="hostmask">{{ user.hostmask }}</td>
<td class="when">{{ user.when | localetime }}</td>
<tr v-for="user in channel.data" :key="user.hostmask">
<td class="hostmask"><ParsedMessage :network="network" :text="user.hostmask" /></td>
<td class="when">{{ localetime(user.when) }}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
<script lang="ts">
import ParsedMessage from "../ParsedMessage.vue";
import localetime from "../../js/helpers/localetime";
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientChan} from "../../js/types";
export default defineComponent({
name: "ListIgnored",
props: {
network: Object,
channel: Object,
components: {
ParsedMessage,
},
};
props: {
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
},
setup() {
return {
localetime,
};
},
});
</script>

View file

@ -8,24 +8,36 @@
</tr>
</thead>
<tbody>
<tr
v-for="invite in channel.data"
:key="invite.hostmask"
>
<td class="hostmask">{{ invite.hostmask }}</td>
<tr v-for="invite in channel.data" :key="invite.hostmask">
<td class="hostmask">
<ParsedMessage :network="network" :text="invite.hostmask" />
</td>
<td class="invitened_by">{{ invite.invited_by }}</td>
<td class="invitened_at">{{ invite.invited_at | localetime }}</td>
<td class="invitened_at">{{ localetime(invite.invited_at) }}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
<script lang="ts">
import ParsedMessage from "../ParsedMessage.vue";
import localetime from "../../js/helpers/localetime";
import {defineComponent, PropType} from "vue";
import {ClientNetwork, ClientChan} from "../../js/types";
export default defineComponent({
name: "ListInvites",
props: {
network: Object,
channel: Object,
components: {
ParsedMessage,
},
};
props: {
network: {type: Object as PropType<ClientNetwork>, required: true},
channel: {type: Object as PropType<ClientChan>, required: true},
},
setup() {
return {
localetime: (date: Date) => localetime(date),
};
},
});
</script>

View file

@ -1,24 +1,84 @@
<template>
<span
:class="['user', $options.filters.colorClass(user.nick), { active: active }]"
:class="['user', {[nickColor]: store.state.settings.coloredNicks}, {active: active}]"
:data-name="user.nick"
role="button"
v-on="onHover ? { mouseover: hover } : {}"
>{{ user.mode }}{{ user.nick }}</span>
v-on="onHover ? {mouseenter: hover} : {}"
@click.prevent="openContextMenu"
@contextmenu.prevent="openContextMenu"
><slot>{{ mode }}{{ user.nick }}</slot></span
>
</template>
<script>
export default {
<script lang="ts">
import {computed, defineComponent, PropType} from "vue";
import {UserInMessage} from "../../shared/types/msg";
import eventbus from "../js/eventbus";
import colorClass from "../js/helpers/colorClass";
import type {ClientChan, ClientNetwork} from "../js/types";
import {useStore} from "../js/store";
type UsernameUser = Partial<UserInMessage> & {
mode?: string;
nick: string;
};
export default defineComponent({
name: "Username",
props: {
user: Object,
active: Boolean,
onHover: Function,
},
methods: {
hover() {
return this.onHover(this.user);
user: {
// TODO: UserInMessage shouldn't be necessary here.
type: Object as PropType<UsernameUser | UserInMessage>,
required: true,
},
active: Boolean,
onHover: {
type: Function as PropType<(user: UserInMessage) => void>,
required: false,
},
channel: {type: Object as PropType<ClientChan>, required: false},
network: {type: Object as PropType<ClientNetwork>, required: false},
},
};
setup(props) {
const mode = computed(() => {
// Message objects have a singular mode, but user objects have modes array
if (props.user.modes) {
return props.user.modes[0];
}
return props.user.mode;
});
// TODO: Nick must be ! because our user prop union includes UserInMessage
const nickColor = computed(() => colorClass(props.user.nick!));
const hover = () => {
if (props.onHover) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return props.onHover(props.user as UserInMessage);
}
return null;
};
const openContextMenu = (event: Event) => {
eventbus.emit("contextmenu:user", {
event: event,
user: props.user,
network: props.network,
channel: props.channel,
});
};
const store = useStore();
return {
mode,
nickColor,
hover,
openContextMenu,
store,
};
},
});
</script>

View file

@ -1,25 +0,0 @@
<template>
<span
:class="['user', $options.filters.colorClass(user.original.nick), { active: active }]"
:data-name="user.original.nick"
role="button"
@mouseover="hover"
v-html="user.original.mode + user.string"
/>
</template>
<script>
export default {
name: "UsernameFiltered",
props: {
user: Object,
active: Boolean,
onHover: Function,
},
methods: {
hover() {
this.onHover ? this.onHover(this.user.original) : null;
},
},
};
</script>

View file

@ -0,0 +1,66 @@
<template>
<div id="version-checker" :class="[store.state.versionStatus]">
<p v-if="store.state.versionStatus === 'loading'">Checking for updates</p>
<p v-if="store.state.versionStatus === 'new-version'">
The Lounge <b>{{ store.state.versionData?.latest.version }}</b>
<template v-if="store.state.versionData?.latest.prerelease"> (pre-release) </template>
is now available.
<br />
<a :href="store.state.versionData?.latest.url" target="_blank" rel="noopener">
Read more on GitHub
</a>
</p>
<p v-if="store.state.versionStatus === 'new-packages'">
The Lounge is up to date, but there are out of date packages Run
<code>thelounge upgrade</code> on the server to upgrade packages.
</p>
<template v-if="store.state.versionStatus === 'up-to-date'">
<p>The Lounge is up to date!</p>
<button
v-if="store.state.versionDataExpired"
id="check-now"
class="btn btn-small"
@click="checkNow"
>
Check now
</button>
</template>
<template v-if="store.state.versionStatus === 'error'">
<p>Information about latest release could not be retrieved.</p>
<button id="check-now" class="btn btn-small" @click="checkNow">Try again</button>
</template>
</div>
</template>
<script lang="ts">
import {defineComponent, onMounted} from "vue";
import socket from "../js/socket";
import {useStore} from "../js/store";
export default defineComponent({
name: "VersionChecker",
setup() {
const store = useStore();
const checkNow = () => {
store.commit("versionData", null);
store.commit("versionStatus", "loading");
socket.emit("changelog");
};
onMounted(() => {
if (!store.state.versionData) {
checkNow();
}
});
return {
store,
checkNow,
};
},
});
</script>

View file

@ -0,0 +1,93 @@
<template>
<div id="changelog" class="window" aria-label="Changelog">
<div class="header">
<SidebarToggle />
</div>
<div class="container">
<router-link id="back-to-help" to="/help">« Help</router-link>
<template
v-if="store.state.versionData?.current && store.state.versionData?.current.version"
>
<h1 class="title">
Release notes for {{ store.state.versionData.current.version }}
</h1>
<template v-if="store.state.versionData.current.changelog">
<h3>Introduction</h3>
<div
ref="changelog"
class="changelog-text"
v-html="store.state.versionData.current.changelog"
></div>
</template>
<template v-else>
<p>Unable to retrieve changelog for current release from GitHub.</p>
<p>
<a
v-if="store.state.serverConfiguration?.version"
:href="`https://github.com/thelounge/thelounge/releases/tag/v${store.state.serverConfiguration?.version}`"
target="_blank"
rel="noopener"
>View release notes for this version on GitHub</a
>
</p>
</template>
</template>
<p v-else>Loading changelog</p>
</div>
</div>
</template>
<script lang="ts">
import {defineComponent, onMounted, onUpdated, ref} from "vue";
import socket from "../../js/socket";
import {useStore} from "../../js/store";
import SidebarToggle from "../SidebarToggle.vue";
export default defineComponent({
name: "Changelog",
components: {
SidebarToggle,
},
setup() {
const store = useStore();
const changelog = ref<HTMLDivElement | null>(null);
const patchChangelog = () => {
if (!changelog.value) {
return;
}
const links = changelog.value.querySelectorAll("a");
links.forEach((link) => {
// Make sure all links will open a new tab instead of exiting the application
link.setAttribute("target", "_blank");
link.setAttribute("rel", "noopener");
if (link.querySelector("img")) {
// Add required metadata to image links, to support built-in image viewer
link.classList.add("toggle-thumbnail");
}
});
};
onMounted(() => {
if (!store.state.versionData) {
socket.emit("changelog");
}
patchChangelog();
});
onUpdated(() => {
patchChangelog();
});
return {
store,
};
},
});
</script>

View file

@ -0,0 +1,117 @@
<template>
<NetworkForm :handle-submit="handleSubmit" :defaults="defaults" :disabled="disabled" />
</template>
<script lang="ts">
import {defineComponent, ref} from "vue";
import socket from "../../js/socket";
import {useStore} from "../../js/store";
import NetworkForm, {NetworkFormDefaults} from "../NetworkForm.vue";
export default defineComponent({
name: "Connect",
components: {
NetworkForm,
},
props: {
queryParams: Object,
},
setup(props) {
const store = useStore();
const disabled = ref(false);
const handleSubmit = (data: Record<string, any>) => {
disabled.value = true;
socket.emit("network:new", data);
};
const parseOverrideParams = (params?: Record<string, string>) => {
if (!params) {
return {};
}
const parsedParams: Record<string, any> = {};
for (let key of Object.keys(params)) {
let value = params[key];
// Param can contain multiple values in an array if its supplied more than once
if (Array.isArray(value)) {
value = value[0];
}
// Support `channels` as a compatibility alias with other clients
if (key === "channels") {
key = "join";
}
if (
!Object.prototype.hasOwnProperty.call(
store.state.serverConfiguration?.defaults,
key
)
) {
continue;
}
// When the network is locked, URL overrides should not affect disabled fields
if (
store.state.serverConfiguration?.lockNetwork &&
["name", "host", "port", "tls", "rejectUnauthorized"].includes(key)
) {
continue;
}
if (key === "join") {
value = value
.split(",")
.map((chan) => {
if (!chan.match(/^[#&!+]/)) {
return `#${chan}`;
}
return chan;
})
.join(", ");
}
// Override server provided defaults with parameters passed in the URL if they match the data type
switch (typeof store.state.serverConfiguration?.defaults[key]) {
case "boolean":
if (value === "0" || value === "false") {
parsedParams[key] = false;
} else {
parsedParams[key] = !!value;
}
break;
case "number":
parsedParams[key] = Number(value);
break;
case "string":
parsedParams[key] = String(value);
break;
}
}
return parsedParams;
};
const defaults = ref<Partial<NetworkFormDefaults>>(
Object.assign(
{},
store.state.serverConfiguration?.defaults,
parseOverrideParams(props.queryParams)
)
);
return {
defaults,
disabled,
handleSubmit,
};
},
});
</script>

View file

@ -0,0 +1,879 @@
<template>
<div id="help" class="window" role="tabpanel" aria-label="Help">
<div class="header">
<SidebarToggle />
</div>
<div class="container">
<h1 class="title">Help</h1>
<h2 class="help-version-title">
<span>About The Lounge</span>
<small>
v{{ store.state.serverConfiguration?.version }} (<router-link
id="view-changelog"
to="/changelog"
>release notes</router-link
>)
</small>
</h2>
<div class="about">
<VersionChecker />
<template v-if="store.state.serverConfiguration?.gitCommit">
<p>
The Lounge is running from source (<a
:href="`https://github.com/thelounge/thelounge/tree/${store.state.serverConfiguration?.gitCommit}`"
target="_blank"
rel="noopener"
>commit <code>{{ store.state.serverConfiguration?.gitCommit }}</code></a
>).
</p>
<ul>
<li>
Compare
<a
:href="`https://github.com/thelounge/thelounge/compare/${store.state.serverConfiguration?.gitCommit}...master`"
target="_blank"
rel="noopener"
>between
<code>{{ store.state.serverConfiguration?.gitCommit }}</code> and
<code>master</code></a
>
to see what you are missing
</li>
<li>
Compare
<a
:href="`https://github.com/thelounge/thelounge/compare/${store.state.serverConfiguration?.version}...${store.state.serverConfiguration?.gitCommit}`"
target="_blank"
rel="noopener"
>between
<code>{{ store.state.serverConfiguration?.version }}</code> and
<code>{{ store.state.serverConfiguration?.gitCommit }}</code></a
>
to see your local changes
</li>
</ul>
</template>
<p>
<a
href="https://thelounge.chat/"
target="_blank"
rel="noopener"
class="website-link"
>Website</a
>
</p>
<p>
<a
href="https://thelounge.chat/docs/"
target="_blank"
rel="noopener"
class="documentation-link"
>Documentation</a
>
</p>
<p>
<a
href="https://github.com/thelounge/thelounge/issues/new"
target="_blank"
rel="noopener"
class="report-issue-link"
>Report an issue</a
>
</p>
</div>
<h2 v-if="isTouch">Gestures</h2>
<div v-if="isTouch" class="help-item">
<div class="subject gesture">Single-Finger Swipe Left</div>
<div class="description">
<p>Hide sidebar.</p>
</div>
</div>
<div v-if="isTouch" class="help-item">
<div class="subject gesture">Single-Finger Swipe Right</div>
<div class="description">
<p>Show sidebar.</p>
</div>
</div>
<div v-if="isTouch" class="help-item">
<div class="subject gesture">Two-Finger Swipe Left</div>
<div class="description">
<p>Switch to the next window in the channel list.</p>
</div>
</div>
<div v-if="isTouch" class="help-item">
<div class="subject gesture">Two-Finger Swipe Right</div>
<div class="description">
<p>Switch to the previous window in the channel list.</p>
</div>
</div>
<h2>Keyboard Shortcuts</h2>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the next lobby in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the previous lobby in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Collapse current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>Shift</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Expand current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the next window in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the previous window in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>Ctrl</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the next window with unread messages in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>Ctrl</kbd> <kbd></kbd></span>
<span v-else><kbd></kbd> <kbd></kbd> <kbd></kbd></span>
</div>
<div class="description">
<p>Switch to the previous window with unread messages in the channel list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>A</kbd></span>
<span v-else><kbd></kbd> <kbd>A</kbd></span>
</div>
<div class="description">
<p>Switch to the first window with unread messages.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>S</kbd></span>
<span v-else><kbd></kbd> <kbd>S</kbd></span>
</div>
<div class="description">
<p>Toggle sidebar.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>U</kbd></span>
<span v-else><kbd></kbd> <kbd>U</kbd></span>
</div>
<div class="description">
<p>Toggle channel user list.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>J</kbd></span>
<span v-else><kbd></kbd> <kbd>J</kbd></span>
</div>
<div class="description">
<p>Toggle jump to channel switcher.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>M</kbd></span>
<span v-else><kbd></kbd> <kbd>M</kbd></span>
</div>
<div class="description">
<p>Toggle recent mentions popup.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Alt</kbd> <kbd>/</kbd></span>
<span v-else><kbd></kbd> <kbd>/</kbd></span>
</div>
<div class="description">
<p>Switch to the help menu.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span><kbd>Esc</kbd></span>
</div>
<div class="description">
<p>
Close current contextual window (context menu, image viewer, topic edit,
etc) and remove focus from input.
</p>
</div>
</div>
<h2>Formatting Shortcuts</h2>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Ctrl</kbd> <kbd>K</kbd></span>
<span v-else><kbd></kbd> <kbd>K</kbd></span>
</div>
<div class="description">
<p>
Mark any text typed after this shortcut to be colored. After hitting this
shortcut, enter an integer in the range
<code>015</code> to select the desired color, or use the autocompletion
menu to choose a color name (see below).
</p>
<p>
Background color can be specified by putting a comma and another integer in
the range <code>015</code> after the foreground color number
(autocompletion works too).
</p>
<p>
A color reference can be found
<a
href="https://modern.ircdocs.horse/formatting.html#colors"
target="_blank"
rel="noopener"
>here</a
>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Ctrl</kbd> <kbd>B</kbd></span>
<span v-else><kbd></kbd> <kbd>B</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut as
<span class="irc-bold">bold</span>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Ctrl</kbd> <kbd>U</kbd></span>
<span v-else><kbd></kbd> <kbd>U</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut as
<span class="irc-underline">underlined</span>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Ctrl</kbd> <kbd>I</kbd></span>
<span v-else><kbd></kbd> <kbd>I</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut as
<span class="irc-italic">italics</span>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Ctrl</kbd> <kbd>S</kbd></span>
<span v-else><kbd></kbd> <kbd>S</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut as
<span class="irc-strikethrough">struck through</span>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Ctrl</kbd> <kbd>M</kbd></span>
<span v-else><kbd></kbd> <kbd>M</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut as
<span class="irc-monospace">monospaced</span>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<span v-if="!isApple"><kbd>Ctrl</kbd> <kbd>O</kbd></span>
<span v-else><kbd></kbd> <kbd>O</kbd></span>
</div>
<div class="description">
<p>
Mark all text typed after this shortcut to be reset to its original
formatting.
</p>
</div>
</div>
<h2>Autocompletion</h2>
<p>
To auto-complete nicknames, channels, commands, and emoji, type one of the
characters below to open a suggestion list. Use the <kbd></kbd> and
<kbd></kbd> keys to highlight an item, and insert it by pressing <kbd>Tab</kbd> or
<kbd>Enter</kbd> (or by clicking the desired item).
</p>
<p>Autocompletion can be disabled in settings.</p>
<div class="help-item">
<div class="subject">
<code>@</code>
</div>
<div class="description">
<p>Nickname</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>#</code>
</div>
<div class="description">
<p>Channel</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/</code>
</div>
<div class="description">
<p>Commands (see list of commands below)</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>:</code>
</div>
<div class="description">
<p>
Emoji (note: requires two search characters, to avoid conflicting with
common emoticons like <code>:)</code>)
</p>
</div>
</div>
<h2>Commands</h2>
<div class="help-item">
<div class="subject">
<code>/away [message]</code>
</div>
<div class="description">
<p>Mark yourself as away with an optional message.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/back</code>
</div>
<div class="description">
<p>Remove your away status (set with <code>/away</code>).</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ban nick</code>
</div>
<div class="description">
<p>
Ban (<code>+b</code>) a user from the current channel. This can be a
nickname or a hostmask.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/banlist</code>
</div>
<div class="description">
<p>Load the banlist for the current channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/collapse</code>
</div>
<div class="description">
<p>
Collapse all previews in the current channel (opposite of
<code>/expand</code>)
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/connect host [port]</code>
</div>
<div class="description">
<p>
Connect to a new IRC network. If <code>port</code> starts with a
<code>+</code> sign, the connection will be made secure using TLS.
</p>
<p>Alias: <code>/server</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ctcp target cmd [args]</code>
</div>
<div class="description">
<p>
Send a <abbr title="Client-to-client protocol">CTCP</abbr>
request. Read more about this on
<a
href="https://en.wikipedia.org/wiki/Client-to-client_protocol"
target="_blank"
rel="noopener"
>the dedicated Wikipedia article</a
>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/deop nick [...nick]</code>
</div>
<div class="description">
<p>
Remove op (<code>-o</code>) from one or several users in the current
channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/devoice nick [...nick]</code>
</div>
<div class="description">
<p>
Remove voice (<code>-v</code>) from one or several users in the current
channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/disconnect [message]</code>
</div>
<div class="description">
<p>Disconnect from the current network with an optionally-provided message.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/expand</code>
</div>
<div class="description">
<p>
Expand all previews in the current channel (opposite of
<code>/collapse</code>)
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/invite nick [channel]</code>
</div>
<div class="description">
<p>
Invite a user to the specified channel. If
<code>channel</code> is omitted, user will be invited to the current
channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ignore nick</code>
</div>
<div class="description">
<p>
Block any messages from the specified user on the current network. This can
be a nickname or a hostmask.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/ignorelist</code>
</div>
<div class="description">
<p>Load the list of ignored users for the current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/join channel [password]</code>
</div>
<div class="description">
<p>
Join a channel. Password is only needed in protected channels and can
usually be omitted.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/kick nick [reason]</code>
</div>
<div class="description">
<p>Kick a user from the current channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/kickban nick [reason]</code>
</div>
<div class="description">
<p>
Kick and ban (<code>+b</code>) a user from the current channel. Unlike
<code>/ban</code>, only nicknames (and not host masks) can be used.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/list</code>
</div>
<div class="description">
<p>Retrieve a list of available channels on this network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/me message</code>
</div>
<div class="description">
<p>
Send an action message to the current channel. The Lounge will display it
inline, as if the message was posted in the third person.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/mode flags [args]</code>
</div>
<div class="description">
<p>
Set the given flags to the current channel if the active window is a
channel, another user if the active window is a private message window, or
yourself if the current window is a server window.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/msg channel message</code>
</div>
<div class="description">
<p>Send a message to the specified channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/mute [...channel]</code>
</div>
<div class="description">
<p>
Prevent messages from generating any feedback for a channel. This turns off
the highlight indicator, hides mentions and inhibits push notifications.
Muting a network lobby mutes the entire network. Not specifying any channel
target mutes the current channel. Revert with <code>/unmute</code>.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/nick newnick</code>
</div>
<div class="description">
<p>Change your nickname on the current network.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/notice channel message</code>
</div>
<div class="description">
<p>Sends a notice message to the specified channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/op nick [...nick]</code>
</div>
<div class="description">
<p>Give op (<code>+o</code>) to one or several users in the current channel.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/part [channel]</code>
</div>
<div class="description">
<p>
Close the specified channel or private message window, or the current
channel if <code>channel</code> is omitted.
</p>
<p>Aliases: <code>/close</code>, <code>/leave</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/rejoin</code>
</div>
<div class="description">
<p>
Leave and immediately rejoin the current channel. Useful to quickly get op
from ChanServ in an empty channel, for example.
</p>
<p>Alias: <code>/cycle</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/query nick</code>
</div>
<div class="description">
<p>Send a private message to the specified user.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/quit [message]</code>
</div>
<div class="description">
<p>Disconnect from the current network with an optional message.</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/raw message</code>
</div>
<div class="description">
<p>Send a raw message to the current IRC network.</p>
<p>Aliases: <code>/quote</code>, <code>/send</code></p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/slap nick</code>
</div>
<div class="description">
<p>Slap someone in the current channel with a trout!</p>
</div>
</div>
<div v-if="store.state.settings.searchEnabled" class="help-item">
<div class="subject">
<code>/search query</code>
</div>
<div class="description">
<p>Search for messages in the current channel / user</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/topic [newtopic]</code>
</div>
<div class="description">
<p>
Get the topic in the current channel. If <code>newtopic</code> is specified,
sets the topic in the current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/unban nick</code>
</div>
<div class="description">
<p>
Unban (<code>-b</code>) a user from the current channel. This can be a
nickname or a hostmask.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/unignore nick</code>
</div>
<div class="description">
<p>
Unblock messages from the specified user on the current network. This can be
a nickname or a hostmask.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/unmute [...channel]</code>
</div>
<div class="description">
<p>
Un-mutes the given channel(s) or the current channel if no channel is
provided. See <code>/mute</code> for more information.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/voice nick [...nick]</code>
</div>
<div class="description">
<p>
Give voice (<code>+v</code>) to one or several users in the current channel.
</p>
</div>
</div>
<div class="help-item">
<div class="subject">
<code>/whois nick</code>
</div>
<div class="description">
<p>Retrieve information about the given user on the current network.</p>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import {defineComponent, ref} from "vue";
import {useStore} from "../../js/store";
import SidebarToggle from "../SidebarToggle.vue";
import VersionChecker from "../VersionChecker.vue";
export default defineComponent({
name: "Help",
components: {
SidebarToggle,
VersionChecker,
},
setup() {
const store = useStore();
const isApple = navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) || false;
const isTouch = navigator.maxTouchPoints > 0;
return {
isApple,
isTouch,
store,
};
},
});
</script>

View file

@ -0,0 +1,67 @@
<template>
<NetworkForm
v-if="networkData"
:handle-submit="handleSubmit"
:defaults="networkData"
:disabled="disabled"
/>
</template>
<script lang="ts">
import {defineComponent, onMounted, ref, watch} from "vue";
import {useRoute} from "vue-router";
import {switchToChannel} from "../../js/router";
import socket from "../../js/socket";
import {useStore} from "../../js/store";
import NetworkForm, {NetworkFormDefaults} from "../NetworkForm.vue";
export default defineComponent({
name: "NetworkEdit",
components: {
NetworkForm,
},
setup() {
const route = useRoute();
const store = useStore();
const disabled = ref(false);
const networkData = ref<NetworkFormDefaults | null>(null);
const setNetworkData = () => {
socket.emit("network:get", String(route.params.uuid || ""));
networkData.value = store.getters.findNetwork(String(route.params.uuid || ""));
};
const handleSubmit = (data: {uuid: string; name: string}) => {
disabled.value = true;
socket.emit("network:edit", data);
// TODO: move networks to vuex and update state when the network info comes in
const network = store.getters.findNetwork(data.uuid);
if (network) {
network.name = network.channels[0].name = data.name;
switchToChannel(network.channels[0]);
}
};
watch(
() => route.params.uuid,
() => {
setNetworkData();
}
);
onMounted(() => {
setNetworkData();
});
return {
disabled,
networkData,
handleSubmit,
};
},
});
</script>

View file

@ -0,0 +1,321 @@
<template>
<div id="chat-container" class="window">
<div
id="chat"
:class="{
'time-seconds': store.state.settings.showSeconds,
'time-12h': store.state.settings.use12hClock,
}"
>
<div
class="chat-view"
data-type="search-results"
aria-label="Search results"
role="tabpanel"
>
<div v-if="network && channel" class="header">
<SidebarToggle />
<span class="title"
>Searching in <span class="channel-name">{{ channel.name }}</span> for</span
>
<span class="topic">{{ route.query.q }}</span>
<MessageSearchForm :network="network" :channel="channel" />
<button
class="close"
aria-label="Close search window"
title="Close search window"
@click="closeSearch"
/>
</div>
<div v-if="network && channel" class="chat-content">
<div ref="chat" class="chat" tabindex="-1">
<div v-show="moreResultsAvailable" class="show-more">
<button
ref="loadMoreButton"
:disabled="
!!store.state.messageSearchPendingQuery ||
!store.state.isConnected
"
class="btn"
@click="onShowMoreClick"
>
<span v-if="store.state.messageSearchPendingQuery">Loading</span>
<span v-else>Show older messages</span>
</button>
</div>
<div
v-if="store.state.messageSearchPendingQuery && !offset"
class="search-status"
>
Searching
</div>
<div v-else-if="!messages.length && !offset" class="search-status">
No results found.
</div>
<div
class="messages"
role="log"
aria-live="polite"
aria-relevant="additions"
>
<div
v-for="(message, id) in messages"
:key="message.id"
class="result"
@click="jump(message, id)"
>
<DateMarker
v-if="shouldDisplayDateMarker(message, id)"
:key="message.id + '-date'"
:message="message"
/>
<Message
:key="message.id"
:channel="channel"
:network="network"
:message="message"
:data-id="message.id"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style>
.channel-name {
font-weight: 700;
}
</style>
<script lang="ts">
import socket from "../../js/socket";
import eventbus from "../../js/eventbus";
import SidebarToggle from "../SidebarToggle.vue";
import Message from "../Message.vue";
import MessageSearchForm from "../MessageSearchForm.vue";
import DateMarker from "../DateMarker.vue";
import {watch, computed, defineComponent, nextTick, ref, onMounted, onUnmounted} from "vue";
import type {ClientMessage} from "../../js/types";
import {useStore} from "../../js/store";
import {useRoute, useRouter} from "vue-router";
import {switchToChannel} from "../../js/router";
import {SearchQuery} from "../../../shared/types/storage";
export default defineComponent({
name: "SearchResults",
components: {
SidebarToggle,
Message,
DateMarker,
MessageSearchForm,
},
setup() {
const store = useStore();
const route = useRoute();
const router = useRouter();
const chat = ref<HTMLDivElement>();
const loadMoreButton = ref<HTMLButtonElement>();
const offset = ref(0);
const moreResultsAvailable = ref(false);
const oldScrollTop = ref(0);
const oldChatHeight = ref(0);
const messages = computed(() => {
const results = store.state.messageSearchResults?.results;
if (!results) {
return [];
}
return results;
});
const chan = computed(() => {
const chanId = parseInt(String(route.params.id || ""), 10);
return store.getters.findChannel(chanId);
});
const network = computed(() => {
if (!chan.value) {
return null;
}
return chan.value.network;
});
const channel = computed(() => {
if (!chan.value) {
return null;
}
return chan.value.channel;
});
const setActiveChannel = () => {
if (!chan.value) {
return;
}
store.commit("activeChannel", chan.value);
};
const closeSearch = () => {
if (!channel.value) {
return;
}
switchToChannel(channel.value);
};
const shouldDisplayDateMarker = (message: ClientMessage, id: number) => {
const previousMessage = messages.value[id - 1];
if (!previousMessage) {
return true;
}
return new Date(previousMessage.time).getDay() !== new Date(message.time).getDay();
};
const clearSearchState = () => {
offset.value = 0;
store.commit("messageSearchResults", null);
store.commit("messageSearchPendingQuery", null);
};
const doSearch = () => {
if (!network.value || !channel.value) {
return;
}
clearSearchState(); // this is a new search, so we need to clear anything before that
const query: SearchQuery = {
networkUuid: network.value.uuid,
channelName: channel.value.name,
searchTerm: String(route.query.q || ""),
offset: offset.value,
};
store.commit("messageSearchPendingQuery", query);
socket.emit("search", query);
};
const onShowMoreClick = () => {
if (!chat.value || !network.value || !channel.value) {
return;
}
offset.value += 100;
oldScrollTop.value = chat.value.scrollTop;
oldChatHeight.value = chat.value.scrollHeight;
const query: SearchQuery = {
networkUuid: network.value.uuid,
channelName: channel.value.name,
searchTerm: String(route.query.q || ""),
offset: offset.value,
};
store.commit("messageSearchPendingQuery", query);
socket.emit("search", query);
};
const jumpToBottom = async () => {
await nextTick();
const el = chat.value;
if (!el) {
return;
}
el.scrollTop = el.scrollHeight;
};
const jump = (message: ClientMessage, id: number) => {
// TODO: Implement jumping to messages!
// This is difficult because it means client will need to handle a potentially nonlinear message set
// (loading IntersectionObserver both before AND after the messages)
};
watch(
() => route.params.id,
() => {
doSearch();
setActiveChannel();
}
);
watch(
() => route.query,
() => {
doSearch();
setActiveChannel();
}
);
watch(messages, async () => {
moreResultsAvailable.value = !!(
messages.value.length && !(messages.value.length % 100)
);
if (!offset.value) {
await jumpToBottom();
} else {
await nextTick();
const el = chat.value;
if (!el) {
return;
}
const currentChatHeight = el.scrollHeight;
el.scrollTop = oldScrollTop.value + currentChatHeight - oldChatHeight.value;
}
});
onMounted(() => {
setActiveChannel();
doSearch();
eventbus.on("escapekey", closeSearch);
eventbus.on("re-search", doSearch);
});
onUnmounted(() => {
eventbus.off("escapekey", closeSearch);
eventbus.off("re-search", doSearch);
clearSearchState();
});
return {
chat,
loadMoreButton,
messages,
moreResultsAvailable,
network,
channel,
route,
offset,
store,
setActiveChannel,
closeSearch,
shouldDisplayDateMarker,
doSearch,
onShowMoreClick,
jumpToBottom,
jump,
};
},
});
</script>

View file

@ -0,0 +1,56 @@
<template>
<div id="settings" class="window" role="tabpanel" aria-label="Settings">
<div class="header">
<SidebarToggle />
</div>
<Navigation />
<div class="container">
<form ref="settingsForm" autocomplete="off" @change="onChange" @submit.prevent>
<router-view></router-view>
</form>
</div>
</div>
</template>
<script lang="ts">
import {defineComponent} from "vue";
import SidebarToggle from "../SidebarToggle.vue";
import Navigation from "../Settings/Navigation.vue";
import {useStore} from "../../js/store";
export default defineComponent({
name: "Settings",
components: {
SidebarToggle,
Navigation,
},
setup() {
const store = useStore();
const onChange = (event: Event) => {
const ignore = ["old_password", "new_password", "verify_password"];
const name = (event.target as HTMLInputElement).name;
if (ignore.includes(name)) {
return;
}
let value: boolean | string;
if ((event.target as HTMLInputElement).type === "checkbox") {
value = (event.target as HTMLInputElement).checked;
} else {
value = (event.target as HTMLInputElement).value;
}
void store.dispatch("settings/update", {name, value, sync: true});
};
return {
onChange,
};
},
});
</script>

View file

@ -0,0 +1,116 @@
<template>
<div id="sign-in" class="window" role="tabpanel" aria-label="Sign-in">
<form class="container" method="post" action="" @submit="onSubmit">
<img
src="img/logo-vertical-transparent-bg.svg"
class="logo"
alt="The Lounge"
width="256"
height="170"
/>
<img
src="img/logo-vertical-transparent-bg-inverted.svg"
class="logo-inverted"
alt="The Lounge"
width="256"
height="170"
/>
<label for="signin-username">Username</label>
<input
id="signin-username"
v-model="username"
class="input"
type="text"
name="username"
autocapitalize="none"
autocorrect="off"
autocomplete="username"
required
autofocus
/>
<div class="password-container">
<label for="signin-password">Password</label>
<RevealPassword v-slot:default="slotProps">
<input
id="signin-password"
v-model="password"
:type="slotProps.isVisible ? 'text' : 'password'"
class="input"
autocapitalize="none"
autocorrect="off"
autocomplete="current-password"
required
/>
</RevealPassword>
</div>
<div v-if="errorShown" class="error">Authentication failed.</div>
<button :disabled="inFlight" type="submit" class="btn">Sign in</button>
</form>
</div>
</template>
<script lang="ts">
import storage from "../../js/localStorage";
import socket from "../../js/socket";
import RevealPassword from "../RevealPassword.vue";
import {defineComponent, onBeforeUnmount, onMounted, ref} from "vue";
export default defineComponent({
name: "SignIn",
components: {
RevealPassword,
},
setup() {
const inFlight = ref(false);
const errorShown = ref(false);
const username = ref(storage.get("user") || "");
const password = ref("");
const onAuthFailed = () => {
inFlight.value = false;
errorShown.value = true;
};
const onSubmit = (event: Event) => {
event.preventDefault();
if (!username.value || !password.value) {
return;
}
inFlight.value = true;
errorShown.value = false;
const values = {
user: username.value,
password: password.value,
};
storage.set("user", values.user);
socket.emit("auth:perform", values);
};
onMounted(() => {
socket.on("auth:failed", onAuthFailed);
});
onBeforeUnmount(() => {
socket.off("auth:failed", onAuthFailed);
});
return {
inFlight,
errorShown,
username,
password,
onSubmit,
};
},
});
</script>

1189
client/css/bootstrap.css vendored

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
@font-face {
/* We use free solid icons - https://fontawesome.com/icons?s=solid&m=free */
font-family: "FontAwesome";
font-family: FontAwesome;
font-weight: normal;
font-style: normal;
src:

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
<!doctype html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
@ -9,7 +9,6 @@
<link rel="preload" as="script" href="js/bundle.vendor.js?v=<%- cacheBust %>">
<link rel="preload" as="script" href="js/bundle.js?v=<%- cacheBust %>">
<link rel="stylesheet" href="css/primer-tooltips.css?v=<%- cacheBust %>">
<link rel="stylesheet" href="css/style.css?v=<%- cacheBust %>">
<link id="theme" rel="stylesheet" href="themes/<%- theme %>.css" data-server-theme="<%- theme %>">
<% _.forEach(stylesheets, function(css) { %>
@ -23,7 +22,7 @@
<link id="favicon" rel="icon" sizes="16x16 32x32 64x64" href="favicon.ico" data-other="img/favicon-alerted.ico" type="image/x-icon">
<!-- Safari pinned tab icon -->
<link rel="mask-icon" href="img/icon-black-transparent-bg.svg" color="#415363">
<link rel="mask-icon" href="img/icon-black-transparent-bg.svg" color="#415364">
<link rel="manifest" href="thelounge.webmanifest">
@ -48,27 +47,22 @@
<meta name="theme-color" content="<%- themeColor %>">
</head>
<body class="signed-out<%- public ? " public" : "" %>" data-transports="<%- JSON.stringify(transports) %>">
<body class="<%- public ? " public" : "" %>" data-transports="<%- JSON.stringify(transports) %>">
<div id="app"></div>
<div id="loading">
<div class="window">
<div id="loading-status-container">
<img src="img/logo-vertical-transparent-bg.svg" class="logo" alt="The Lounge" width="256" height="170">
<img src="img/logo-vertical-transparent-bg-inverted.svg" class="logo-inverted" alt="The Lounge" width="256" height="170">
<p id="loading-page-message"><a href="https://enable-javascript.com/" target="_blank" rel="noopener">Your JavaScript must be enabled.</a></p>
<img src="img/logo-vertical-transparent-bg.svg" class="logo" alt="" width="256" height="170">
<img src="img/logo-vertical-transparent-bg-inverted.svg" class="logo-inverted" alt="" width="256" height="170">
<p id="loading-page-message">The Lounge requires a modern browser with JavaScript enabled.</p>
</div>
<div id="loading-reload-container">
<p id="loading-slow">This is taking longer than it should, there might be connectivity issues.</p>
<button id="loading-reload" class="btn">Reload page</button>
</div>
<script async src="js/loading-error-handlers.js?v=<%- cacheBust %>"></script>
</div>
</div>
<div id="viewport"></div>
<div id="context-menu-container"></div>
<div id="image-viewer"></div>
<div id="upload-overlay"></div>
<script src="js/loading-error-handlers.js?v=<%- cacheBust %>"></script>
<script src="js/bundle.vendor.js?v=<%- cacheBust %>"></script>
<script src="js/bundle.js?v=<%- cacheBust %>"></script>
</body>

Some files were not shown because too many files have changed in this diff Show more