Compare commits

...
This repository has been archived on 2022-08-19. You can view files and clone it, but cannot push or open issues or pull requests.

2754 commits

Author SHA1 Message Date
f9aa7b52f8
feat: switch to matrix.test.mystiq.app
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-19 17:41:05 +05:30
2e54866353
fix: submit path
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-18 17:51:23 +05:30
ce075eb32b
feat: set custom homeserver and bugreport endpoint
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-18 17:41:24 +05:30
02a50a19cb
feat: add ci badge
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-16 17:25:37 +05:30
a33d9981bd
fix: secrets
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2022-08-16 17:05:59 +05:30
8335a50308
feat: switch to python, debian doesn't have make installed by default
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 17:02:10 +05:30
ee9e73d8c7
fix: use debian latest img to get git with git branch --show-current
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 16:59:15 +05:30
63f77feb7b
fix: set project root
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 16:55:52 +05:30
04de39596f
feat: bump ci node to 16
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 16:52:22 +05:30
25b634bb78
fix: use same tests as github actions
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 16:47:23 +05:30
96c9ea8de7
fix: use node 14, same as github actions config
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 16:44:15 +05:30
d80e970117
feat: conditional deploy pipeline
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 16:38:53 +05:30
6db5f34ac2
feat: multi-pipeline workflow 2022-08-16 16:36:05 +05:30
df0000783d
feat: deploy to librepages
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2022-08-16 16:14:27 +05:30
Bruno Windels
c898bcb46a release v0.3.1 2022-08-02 12:16:55 +02:00
Bruno Windels
97391663d3 sdk version 0.1.0 2022-08-01 14:32:26 +02:00
R Midhun Suresh
7d3f22c106
Merge pull request #824 from vector-im/fix-dev-server-1
Fix develop server breaking due to import syntax
2022-08-01 17:29:52 +05:30
RMidhunSuresh
832597447a Add explaining doc 2022-08-01 17:01:36 +05:30
RMidhunSuresh
236a4ab49b Ignore error 2022-08-01 17:01:36 +05:30
RMidhunSuresh
ba8cdea6b4 Use default import if other not found 2022-08-01 17:01:36 +05:30
RMidhunSuresh
ef9f90bc36 Fix imports breaking on dev 2022-08-01 17:01:36 +05:30
R Midhun Suresh
67e94bd642
Merge pull request #825 from vector-im/fix-sdk-fail-1
Fix sdk build failing after derived theme implementation
2022-08-01 16:17:09 +05:30
R Midhun Suresh
f7839135a4
Merge pull request #823 from vector-im/fix-tmp-dir
Fix .tmp being created in `/`
2022-08-01 16:16:35 +05:30
RMidhunSuresh
4571ecd851 Specify theme as array 2022-07-29 23:45:58 +05:30
RMidhunSuresh
5091090795 Produce .tmp directory within root 2022-07-29 23:11:17 +05:30
Bruno Windels
db2b4e693c release v0.3.0 2022-07-29 17:10:24 +02:00
Bruno Windels
eee8412621
Merge pull request #822 from vector-im/bwindels/move-runtime-theme-test-out-of-root
move semi-automatic test for runtime themes into dedicated directory
2022-07-29 15:00:34 +00:00
Bruno Windels
5e83eca3b9 move semi-automatic test for runtime themes into dedicated directory 2022-07-29 16:43:28 +02:00
Bruno Windels
041e628520
Merge pull request #769 from vector-im/implement-derived-theme
Support for derived themes
2022-07-29 14:25:05 +00:00
Bruno Windels
4838e19c92
Merge pull request #811 from vector-im/bwindels/sharekeyswithinvitees
Key sharing based on room history visibility
2022-07-29 14:23:26 +00:00
Bruno Windels
cb0ac846c7 remove obsolete comment 2022-07-29 16:22:01 +02:00
Bruno Windels
b40ce6137e
Merge pull request #676 from vector-im/ts-conversion-domain-navigation
Convert /domain/navigation to typescript
2022-07-29 14:21:17 +00:00
Bruno Windels
fdefea5b88 Merge branch 'master' into ts-conversion-domain-navigation 2022-07-29 16:18:22 +02:00
RMidhunSuresh
39817dc36b Revert back option 2022-07-29 17:33:33 +05:30
RMidhunSuresh
708637e390 No need for this complex resolve 2022-07-29 16:45:25 +05:30
Bruno Windels
b6f795505d fix lint 2022-07-29 12:21:16 +02:00
Bruno Windels
10522cacef
Merge pull request #813 from vector-im/doc-derived-theming
[Documentation] - Add information about derived themes to doc
2022-07-29 10:16:41 +00:00
Bruno Windels
02116103a1
Merge pull request #816 from Kaki-In/restore_last
Opening the last opened room at start
2022-07-29 10:16:23 +00:00
Bruno Windels
06da5a8ae4
clarification 2022-07-29 10:14:58 +00:00
Bruno Windels
02bc7d1d7e
fix typo 2022-07-29 10:14:41 +00:00
Kaki In
09bc77073b
Merge branch 'vector-im:master' into restore_last 2022-07-29 12:06:49 +02:00
Bruno Windels
4a2e14925a
Merge pull request #812 from vector-im/doc-config
[Documentation] - Add type for config options
2022-07-29 10:05:27 +00:00
Bruno Windels
224ab2672a
Merge pull request #809 from Kaki-In/implement-join
Implemented /join
2022-07-29 10:03:18 +00:00
Bruno Windels
170460f5a9 add link to sygnal webpush docs as well 2022-07-29 12:02:09 +02:00
Bruno Windels
2a5e0302dc
Merge pull request #785 from vector-im/hs/log-when-storage-access-fails
Log the error when we can't get storage access
2022-07-29 09:47:58 +00:00
Kaki In
f512bfcfc1 Pretty syntaxed the RoomViewModel 2022-07-29 11:47:47 +02:00
Half-Shot
5b5c852401 Revert "use logging items"
This reverts commit d937b9b14b.
2022-07-29 10:44:37 +01:00
Kaki In
58a2d1f34c Restored the common.js indentation 2022-07-29 11:44:23 +02:00
Half-Shot
d937b9b14b use logging items 2022-07-29 10:39:41 +01:00
Bruno Windels
d3e93196e3
Merge pull request #777 from ibeckermayer/ibeckermayer/ts-conversion-loginviewmodel
TS conversion for `LoginViewModel`
2022-07-29 09:27:10 +00:00
Bruno Windels
62b3a67e33 write unit tests for correctly reading history visibility when needed 2022-07-28 17:09:41 +02:00
Bruno Windels
319ec37864 fix typos preventing to load the history visibility 2022-07-28 11:44:50 +02:00
Kaki In
f5dacb4e42 Fixed last check 2022-07-28 10:26:59 +02:00
Kaki In
302131c447 Review last checks 2022-07-28 10:14:21 +02:00
Kaki In
fb79326747 Forgot one change 2022-07-28 09:26:08 +02:00
Kaki In
3c64f7d49b Finals checks about https://github.com/vector-im/hydrogen-web/pull/809#pullrequestreview-1053501341
- joined the processJoinRoom and joinRoom methods;
 - fixed some precisions miss;
 - removed some useless code;
 - change the error message height from absolute (40px) to relative (auto)
2022-07-28 09:23:30 +02:00
Isaiah Becker-Mayer
a82df95b82 marking private methods as such 2022-07-27 22:09:30 -07:00
Isaiah Becker-Mayer
cadca70946 fixes linter errors and removes some unneeded async/await 2022-07-27 22:09:30 -07:00
Isaiah Becker-Mayer
8b91d8fac8 adds newline 2022-07-27 22:09:30 -07:00
Isaiah Becker-Mayer
a5b9cb6b95 removes unnecessary awaits 2022-07-27 22:09:30 -07:00
Isaiah Becker-Mayer
aeed978789 changes signature of emitChange to require changedProps 2022-07-27 22:09:30 -07:00
Isaiah Becker-Mayer
7b7b19476c updates some signatures to be more verbose, fixes wrong type for attemptLogin 2022-07-27 22:09:30 -07:00
Isaiah Becker-Mayer
ad0bd82bda creating default exports 2022-07-27 22:09:30 -07:00
Isaiah Becker-Mayer
d7657dcc4d first draft of fully typescriptified LoginViewModel.ts 2022-07-27 22:09:30 -07:00
Kaki In
176caf340f Placed the join command outside of the processCommand method 2022-07-27 16:42:44 +02:00
Kaki In
a40bb59dc0 Some fixes :
- fixed a pretty syntax miss (a !== b);
 - fixed a type error : replaced "msgtype" by "type" when instantied the "messinfo" variable;
 - some indentation fixes
2022-07-27 16:36:58 +02:00
Kaki In
ab64ce02b2 Separated the _processCommand and the joinRoom command
- renamed executeJoinCommand as joinRoom;
 - separated the joinRoom process and the parse and result process
2022-07-27 15:18:32 +02:00
Kaki In
2d3b6fe973 Canceled indentation modification. 2022-07-27 12:40:19 +02:00
Kaki In
550b9db4dc Separated the join instructions into a executeJoinCommand method 2022-07-27 12:21:00 +02:00
Bruno Windels
0df66b5aea track room before listing user ids when sharing key 2022-07-27 12:06:55 +02:00
Bruno Windels
f18520a2fe let loadMembers use own txn in case members haven't been fetched yet
if they haven't, it will need a network request, meaning that the txn
will get closed, so we can't reuse it afterwards
2022-07-27 11:39:50 +02:00
Bruno Windels
50b6ee91d7 don't need history visibility here 2022-07-27 11:39:36 +02:00
Kaki In
9b0ab0c8f1 Used "null" instead of "undefined"
When creating the this._lastSessionHash attribute of History
2022-07-27 09:19:36 +02:00
Bruno Windels
402cf17d22 Merge branch 'master' into bwindels/sharekeyswithinvitees 2022-07-27 09:17:31 +02:00
Bruno Windels
bfaba63f47 fix ts error 2022-07-26 17:55:21 +02:00
Bruno Windels
544afef902 test adding and removing when tracking multiple rooms 2022-07-26 17:41:26 +02:00
Bruno Windels
dd878bb8d6 also take rejecting invites into account to remove user identity 2022-07-26 16:58:07 +02:00
Bruno Windels
dea3852425 add some tests for sharing keys with invitees 2022-07-26 16:57:28 +02:00
Bruno Windels
4c17612b05 allow passing txn to loadMembers so we can do it as part of sync txn
to rewrite useridentities upon receiving new history visibility
2022-07-26 16:53:02 +02:00
Kaki In
f9f49b7640 Fixed an error and improving css
If the /join command success, an error was thrown, because of a copy-pasted command not well integrated
The button of the error on "theme.css" contains now an unicode cross. The :after/:before cross was disformed when opening the room informations.
2022-07-26 14:48:03 +02:00
Kaki In
0718f1e77e Fixed the https://github.com/vector-im/hydrogen-web/pull/816#discussion_r929692693 comment
Added the _lastSessionHash attribute inside the History constructor
2022-07-26 11:11:16 +02:00
Kaki In
09fd1a5113 Use "args.join" instead of "message.substring"
into RoomViewModel._processCommands
2022-07-26 10:37:05 +02:00
Kaki In
832b840a15 Merge remote-tracking branch 'origin' into restore_last 2022-07-26 10:06:31 +02:00
Kaki In
adfecf0778 Fix restoring the last url at start
The last session url is now remembered for being restored at the beginning of the session. Thanks for the help of @bwindels
2022-07-26 10:02:20 +02:00
Kaki In
5fa6793958
Merge branch 'vector-im:master' into implement-join 2022-07-25 16:30:50 +02:00
Kaki In
1e5179f835 - Application des différents commentaires du Pull Request (#809)
- Correction des erreurs d'indentations.
2022-07-25 15:22:06 +02:00
Bruno Windels
bc385e2cdc
Merge pull request #778 from vector-im/bwindels/uidocs
more detailed docs for IView, TemplateView and ListView
2022-07-25 13:02:22 +00:00
Kaki In
0bf021ea87 The room is now joined after having actualised the rooms list, to avoid the synchronisations waits that can sometimes disable to enter the room (message "You're not into this room" or simply "You're not in this room yet. *Join the room*") 2022-07-25 13:37:03 +02:00
RMidhunSuresh
fdd60a7516 Add documentation for derived themes 2022-07-25 11:38:50 +05:30
RMidhunSuresh
63bdbee39c Make optional fields optional 2022-07-25 11:33:22 +05:30
RMidhunSuresh
8a976861fb Add type 2022-07-25 11:31:14 +05:30
Bruno Windels
a23df8a545 pass history visibility to device tracker
and delegate adding and removing members to share keys with to it
2022-07-22 17:49:59 +02:00
Bruno Windels
17f42f523a add write method for when history visibility changes
also returning added and removed user ids
2022-07-22 17:49:26 +02:00
Bruno Windels
f6011f3f34 take history visibility into account in device tracker
and return added and removed userids to their userIdentity for the given
room, so room encryption can share and discard the keys for them
2022-07-22 17:48:26 +02:00
Bruno Windels
86c0e9e669 logic for whether a key should be shared by membership and h. visibility 2022-07-22 17:46:53 +02:00
Bruno Windels
f337940202 this migration shouldn't be needed anymore
and undoes the export of addRoomToIdentity, which is somewhat internal
2022-07-22 17:46:29 +02:00
Kaki In
b7fd22c7f9 SyntaxError fixed 2022-07-22 17:10:29 +02:00
Kaki In
66a59e6f4d Error of interpretation of the 403 status at the last update. Fixed 2022-07-22 17:09:43 +02:00
Kaki In
e345d0b33e Added the 403 status when joining an unknown room 2022-07-22 17:06:09 +02:00
Kaki In
be8962cec2 Fixed priority operations when checking request status 2022-07-22 16:59:48 +02:00
Kaki In
8b39346409 The error message can now be closed 2022-07-22 16:34:52 +02:00
Kaki In
fb58d9c9ef Corrected some syntax dismiss 2022-07-22 16:08:53 +02:00
Bruno Windels
22831e710c support async callback in iterateResponseStateEvents 2022-07-22 14:15:26 +02:00
Kaki In
faa8cae532 Added the possibility to join a room using /join (also added the global commands uses, and some others commands like /shrug .) 2022-07-21 13:55:23 +02:00
RMidhunSuresh
8d766ac504 Remove await within loop 2022-07-21 12:05:10 +05:30
Bruno Windels
c8a8eb10b5 get user ids for sharing a new key when the message is sent
rather than when the key happens to get sent
2022-07-20 15:21:33 +02:00
Bruno Windels
d79e5f7806 create key share operations for invitees when history visibility=invited 2022-07-20 15:20:23 +02:00
RMidhunSuresh
7feaa479c0 Typescript update to support .mjs files 2022-07-20 15:55:11 +05:30
RMidhunSuresh
1456e308a8 Add type and fix formatting 2022-07-20 15:36:02 +05:30
RMidhunSuresh
313e65e00c Write tests 2022-07-20 12:30:41 +05:30
RMidhunSuresh
612b878793 Update theme name 2022-07-19 21:21:35 +05:30
RMidhunSuresh
8aa96e8031 Update log label 2022-07-19 21:19:22 +05:30
RMidhunSuresh
7ac2c7c7fa Get tests to work 2022-07-19 21:06:55 +05:30
RMidhunSuresh
de02456641 Add explaining comment 2022-07-19 19:46:36 +05:30
RMidhunSuresh
994667205f Remove change 2022-07-19 19:38:36 +05:30
RMidhunSuresh
ecb3a66dfc WIP 2022-07-19 17:56:08 +05:30
RMidhunSuresh
e1ee258630 Change path 2022-07-19 17:56:08 +05:30
RMidhunSuresh
83b5d3b68e Change directory name 2022-07-19 17:56:08 +05:30
RMidhunSuresh
7a1591e0ce Move code 2022-07-19 17:56:08 +05:30
RMidhunSuresh
07db5450b7 Aliases can also be derived 2022-07-19 17:56:08 +05:30
RMidhunSuresh
081de5afa8 .js --> .mjs 2022-07-19 17:56:08 +05:30
RMidhunSuresh
dece42dce3 Do not store all the manifests in memory 2022-07-19 17:56:08 +05:30
RMidhunSuresh
b29287c47e await in loop --> Promise.all() 2022-07-19 17:56:08 +05:30
RMidhunSuresh
9bdf9c500b Add return types 2022-07-19 17:56:08 +05:30
RMidhunSuresh
9e2d355573 Add logging 2022-07-19 17:56:08 +05:30
RMidhunSuresh
ce5db47708 Support using derived theme as default theme 2022-07-19 17:56:08 +05:30
RMidhunSuresh
da0a918c18 This code should only run once 2022-07-19 17:56:08 +05:30
RMidhunSuresh
043cc9f12c Use ThemeManifest type 2022-07-19 17:56:08 +05:30
RMidhunSuresh
80fb953688 Don't fail on erros; expect the code to throw! 2022-07-19 17:56:08 +05:30
RMidhunSuresh
f15e23762a Add more missing keys to type 2022-07-19 17:56:08 +05:30
RMidhunSuresh
f440457875 Use ThemeManifest type where possible 2022-07-19 17:56:08 +05:30
RMidhunSuresh
a8cab98666 Add mroe missing types 2022-07-19 17:56:08 +05:30
RMidhunSuresh
ac7be0c7a1 WIP 2022-07-19 17:56:08 +05:30
RMidhunSuresh
d731eab51c Support fetching text 2022-07-19 17:56:08 +05:30
RMidhunSuresh
f7b302d34f Don't optimzie colors 2022-07-19 17:56:08 +05:30
RMidhunSuresh
5ba74b1d75 Use script to copy over runtime theme after build 2022-07-19 17:56:08 +05:30
RMidhunSuresh
c5f4a75d4b Split code so that it can be reused 2022-07-19 17:56:08 +05:30
RMidhunSuresh
2f3db89e0a Let ts know that we can use replaceAll() 2022-07-19 17:56:08 +05:30
RMidhunSuresh
1ef382f3a9 Add gruvbox color scheme 2022-07-19 17:56:08 +05:30
RMidhunSuresh
161e29b36e Use existing code 2022-07-19 17:56:08 +05:30
RMidhunSuresh
2947f9f6ff Remove console.log 2022-07-19 17:56:08 +05:30
RMidhunSuresh
c873804543 produce asset hashed icons 2022-07-19 17:56:08 +05:30
RMidhunSuresh
43e8cc9e52 Add svgo for optimizing svgs as dev dependency 2022-07-19 17:56:08 +05:30
RMidhunSuresh
bf87ed7eae Do not add variables to root for runtime theme 2022-07-19 17:56:08 +05:30
RMidhunSuresh
8c02541b69 WIP - 1 2022-07-19 17:56:08 +05:30
RMidhunSuresh
599e519f22 Convert color code to use es6 module 2022-07-19 17:56:08 +05:30
RMidhunSuresh
d5e24bf6e8 Convert color.js to color.mjs 2022-07-19 17:56:08 +05:30
Bruno Windels
bb5711db7e
Merge pull request #802 from vector-im/fix-dev-server
Fix bug that stops hydrogen from running in dev server
2022-07-19 10:22:36 +00:00
RMidhunSuresh
88808b0b06 Fix bug preventing yarn start 2022-07-19 15:50:01 +05:30
R Midhun Suresh
c9bca52e82
Merge pull request #760 from vector-im/refactor-rollup-plugin
Refactor theme builder plugin
2022-07-11 16:54:18 +05:30
RMidhunSuresh
6718198d9c Continue with other items if this throws 2022-07-11 12:40:24 +05:30
Bruno Windels
7b9e681d55 sdk v0.0.15 2022-07-07 15:25:17 +02:00
R Midhun Suresh
8291aea2f7
Merge pull request #790 from vector-im/fix-hide-composer
Pass childOptions to LowPowerLevelViewModel
2022-07-07 18:19:08 +05:30
RMidhunSuresh
f073f40e31 Fix error 2022-07-07 18:16:33 +05:30
R Midhun Suresh
963324c767
Merge pull request #789 from vector-im/support-pl-room-creation
Support power_level_content_override option on room creation
2022-07-07 17:42:19 +05:30
R Midhun Suresh
eac75644e7
Merge pull request #788 from vector-im/pl-composer
Disable composer when user lacks powerlevel needed to send messages
2022-07-07 17:35:29 +05:30
RMidhunSuresh
0bdbb96036 Use same kind 2022-07-07 17:26:43 +05:30
RMidhunSuresh
d292e1f5ad Extract into function 2022-07-07 17:23:23 +05:30
RMidhunSuresh
cd9e00b847 Support power_level_content_override 2022-07-07 17:17:05 +05:30
RMidhunSuresh
3941b7e3f0 Rename method 2022-07-07 16:45:18 +05:30
RMidhunSuresh
efd9f70e92 WIP 2022-07-07 16:39:45 +05:30
Isaiah Becker-Mayer
204948db64 changing filename to ts 2022-07-06 21:06:36 -04:00
Will Hunt
a85d2c96d6
Log the error when we can't get storage access
This is quite useful when debugging why a session isn't working properly.
2022-07-06 10:06:00 +01:00
R Midhun Suresh
28b686dae7
Merge pull request #784 from vector-im/fix-build-race
Fix build error caused due to race in postcss plugin
2022-07-05 20:13:57 +05:30
RMidhunSuresh
dd82469ab4 Don't assume object is available 2022-07-05 20:07:48 +05:30
Bruno Windels
3bf6a46a39 release sdk 0.0.14 2022-07-05 16:02:47 +02:00
Bruno Windels
e42e76a21c
Merge pull request #782 from vector-im/image-view-fix
Do not render images as links if lightboxUrl is empty
2022-07-05 14:00:53 +00:00
RMidhunSuresh
8ec0bd7295 Check if lightbox url is available 2022-07-05 17:55:51 +05:30
Bruno Windels
ff2129f36a
Merge pull request #773 from vector-im/madlittlemods/consistent-test-selector
Add a couple consistent selectors to reference in tests
2022-07-04 14:19:09 +00:00
Bruno Windels
1aa2ff5c10
Merge pull request #781 from vector-im/bwindels/fixlint-2022-7-4
fix lint
2022-07-04 14:18:05 +00:00
Bruno Windels
34ce8a8e3c fix lint 2022-07-04 16:15:59 +02:00
Bruno Windels
652e2c6d3b
Merge pull request #780 from vector-im/bwindels/update-olm-3.2.8
update olm to 3.2.8
2022-07-04 14:15:04 +00:00
Bruno Windels
c0445f2182 update lock file 2022-07-04 15:40:17 +02:00
Bruno Windels
b76fd1d792 update olm to 3.2.8 2022-07-04 15:39:11 +02:00
R Midhun Suresh
751dfa66a8
Merge pull request #758 from vector-im/document-theming
Document theming in Hydrogen
2022-07-04 17:20:53 +05:30
RMidhunSuresh
a3c6d744f5 Add link to ts file 2022-07-04 17:18:50 +05:30
R Midhun Suresh
b9f316e7c3 Better sentence structure
Co-authored-by: Bruno Windels <274386+bwindels@users.noreply.github.com>
2022-07-04 17:16:43 +05:30
R Midhun Suresh
d448ee1722 Fix typo
Co-authored-by: Bruno Windels <274386+bwindels@users.noreply.github.com>
2022-07-04 17:16:43 +05:30
RMidhunSuresh
da87470996 Store images in source tree 2022-07-04 17:16:43 +05:30
RMidhunSuresh
b319c0acb0 Remvoe stray newlines 2022-07-04 17:16:43 +05:30
RMidhunSuresh
e90e573bf9 Add doc 2022-07-04 17:16:43 +05:30
R Midhun Suresh
a68f0bba39
Merge pull request #752 from vector-im/theme-document-manifest
Create a type for theme-manifest
2022-07-04 17:11:08 +05:30
Bruno Windels
ca94c65dac clarify LazyListView constraints 2022-07-04 10:19:56 +02:00
Bruno Windels
fba3275f5b
Merge pull request #746 from vector-im/madlittlemods/assets-path-for-assets
Import SDK assets from the `assets/` directory
2022-07-04 06:53:37 +00:00
Bruno Windels
fc93acfd8d some rewording 2022-07-01 14:09:06 +02:00
Bruno Windels
d398e490eb some rewording 2022-07-01 13:59:57 +02:00
Bruno Windels
0ab611b013 more detailed docs for IView, TemplateView and ListView 2022-07-01 13:08:50 +02:00
Bruno Windels
bb923b8eb9 bump sdk version 2022-06-30 10:54:11 +02:00
Bruno Windels
73cd96fe3a abort release script on error 2022-06-30 10:54:00 +02:00
Bruno Windels
4929839fe9 release v0.2.33 2022-06-30 10:51:11 +02:00
Eric Eastwood
c59f65e43b Add a couple consistent selectors to reference in tests
Using `data-testid` because it seems generic out of the list from:

 - https://docs.cypress.io/guides/core-concepts/cypress-app#Uniqueness
 - https://docs.cypress.io/guides/references/best-practices#How-It-Works
2022-06-29 12:56:20 +02:00
Eric Eastwood
fd3a0f0126 Merge branch 'master' into madlittlemods/assets-path-for-assets 2022-06-28 16:35:54 +02:00
Eric Eastwood
ccfd63dfeb Restore backwards compatible theme paths
See https://github.com/vector-im/hydrogen-web/pull/746#discussion_r901347536
2022-06-28 16:35:30 +02:00
Eric Eastwood
5b54280ac2
Ignore macOS metadata .DS_Store (#770) 2022-06-28 05:08:24 -05:00
Bruno Windels
bd5bf7d456
Merge pull request #761 from vector-im/hs/node-15-replaceal
Require node 15+
2022-06-25 18:22:35 +00:00
Bruno Windels
ad8ad22cc1
Merge pull request #767 from vector-im/bwindels/download-media
Menu option to download attached image or video of event
2022-06-25 18:21:17 +00:00
Bruno Windels
3369bda2f0 offer menu options to download media
also always show status (before sendStatus), not just when isPending
as we are recycling it to show download status as well
2022-06-25 20:15:33 +02:00
Bruno Windels
7430aa7aab allow download media in media view model 2022-06-25 20:14:32 +02:00
Bruno Windels
3bc453d5ca
Merge pull request #766 from vector-im/bwindels/fix-765
Also allow undefined, which means at the end of the paginated direction
2022-06-25 17:40:26 +00:00
Bruno Windels
84bac0afe9 Also allow undefined, which means at the end of the paginated direction
we already detect the end by chunk.length===0, so we just need to not throw
2022-06-25 19:37:36 +02:00
Will Hunt
9cb7d89097 Require node 15.
We use replaceAll in scripts/postcss/svg-colorizer.js which is a ES2021 feature. https://node.green/#ES2021-features--String-prototype-replaceAll
2022-06-24 13:27:09 +01:00
RMidhunSuresh
d688fa4737 Get the theme-collection id from manifest 2022-06-23 15:06:22 +05:30
RMidhunSuresh
0dfd24af22 Update info on path
path is now relative to the manifest!
2022-06-21 12:52:10 +05:30
RMidhunSuresh
34eac94da3 Make everything optional
Now typescript will force us to validate everything.
2022-06-20 21:27:02 +05:30
RMidhunSuresh
fbdd512e06 Split functions into smaller functions 2022-06-20 21:10:11 +05:30
RMidhunSuresh
5eec724712 Locations must be relative to manifest 2022-06-20 20:35:06 +05:30
RMidhunSuresh
93165cb947 runtime theme chunks should also be stored in map
There will be more than one runtime theme file when multiple theme
collections exist.
2022-06-20 13:46:14 +05:30
RMidhunSuresh
e3372f0f2b Don't use theme-name in manifest file names 2022-06-20 12:54:18 +05:30
R Midhun Suresh
5a3cf03f0b
Merge pull request #759 from vector-im/move-scope-down
Refactor out global variables in postcss plugins
2022-06-20 12:14:06 +05:30
R Midhun Suresh
c050ade03c
Merge pull request #756 from vector-im/themeing-improvement-1
Improve code quality in css-url-variables plugin
2022-06-20 11:19:47 +05:30
RMidhunSuresh
cc29dc045d Move scope down in css-url-processor 2022-06-17 16:38:13 +05:30
RMidhunSuresh
09b2437e72 Move scope of variables down in compile-variables 2022-06-17 16:35:18 +05:30
RMidhunSuresh
cfd347335b Move scope of variables down
This was causing icons to be repeated in the css-file
2022-06-16 21:29:33 +05:30
RMidhunSuresh
d322f380ad Fix typo here
This was causing the icons section to be omitted from the source section
of the manifest.
2022-06-16 21:26:16 +05:30
RMidhunSuresh
f658dc2e4b Make comment clearer 2022-06-15 15:06:16 +05:30
RMidhunSuresh
7a3eabf39c Formatting fix 2022-06-15 15:04:33 +05:30
RMidhunSuresh
48da6c782c Remove base key 2022-06-15 15:04:12 +05:30
RMidhunSuresh
b00bbc7daf Fix formatting 2022-06-15 15:03:41 +05:30
RMidhunSuresh
9fbe8a4e32 Change description of version key 2022-06-15 15:02:15 +05:30
Bruno Windels
623939c671 release v0.2.32 2022-06-15 11:29:29 +02:00
Bruno Windels
fccc41f4b9
Merge pull request #753 from vector-im/bwindels/rageshake-submit
Allow sending logs to rageshake server
2022-06-15 11:28:54 +02:00
Bruno Windels
3b66ed8c17 fix type 2022-06-15 11:24:16 +02:00
Bruno Windels
8fe8981ffa add options to send logs to server in settings ui 2022-06-15 11:14:06 +02:00
Bruno Windels
375d8b066c complete settings view model for logs ui 2022-06-15 11:13:46 +02:00
Bruno Windels
69ada73dd4 cleanup rageshake code 2022-06-15 11:13:05 +02:00
Bruno Windels
2129a97588 remove unused param 2022-06-15 11:12:49 +02:00
Bruno Windels
4caabae895 extract map -> formdata conversion and also suppor this for xhr 2022-06-15 10:15:15 +02:00
RMidhunSuresh
d0375141f8 WIP - write type for manifest 2022-06-15 12:11:15 +05:30
Bruno Windels
a644621889 basic support for sending rageshake in view model 2022-06-14 18:46:02 +02:00
Bruno Windels
4ed7e01dfd release v0.2.31 2022-06-14 16:00:35 +02:00
Bruno Windels
e643ffb334
Merge pull request #751 from vector-im/fix-theming-watch
Fix: don't crash on platforms that don't have a preferred color scheme
2022-06-14 16:00:13 +02:00
RMidhunSuresh
d00ea39dc4 No need to throw here 2022-06-14 19:27:18 +05:30
RMidhunSuresh
69d8e6031e This isn't used anywhere 2022-06-14 19:26:59 +05:30
Bruno Windels
abee9baf60 release v0.2.30 2022-06-14 10:15:00 +02:00
Bruno Windels
d4aaa8117b
Merge pull request #742 from vector-im/theme-chooser-improvements
Theme chooser improvements
2022-06-14 10:14:29 +02:00
RMidhunSuresh
be66969c9a Remove font section from manifest 2022-06-14 11:52:45 +05:30
R Midhun Suresh
7bce0d848f
Merge pull request #750 from vector-im/madlittlemods/fix-broken-hydrogen-dev
Fix Vite not being able to analyze dynamic CSS styles import in dev on Windows
2022-06-13 20:04:25 +05:30
RMidhunSuresh
53a8915ffc Parellelize code 2022-06-12 17:05:31 +05:30
RMidhunSuresh
b5fd3656a7 Fix code breaking on dev server 2022-06-12 16:53:25 +05:30
R Midhun Suresh
acffd15002
Add comment
Co-authored-by: Bruno Windels <274386+bwindels@users.noreply.github.com>
2022-06-12 16:52:21 +05:30
R Midhun Suresh
989ecd785a
Lowercase string
Co-authored-by: Bruno Windels <274386+bwindels@users.noreply.github.com>
2022-06-12 16:51:58 +05:30
RMidhunSuresh
9a5a002293 Remove test-variant 2022-06-08 13:35:58 +05:30
Eric Eastwood
2cfd08e500 Remove debug logging 2022-06-07 23:47:38 -05:00
Eric Eastwood
2b4a7f05a6 Fix Vite not being able analyze dynamic CSS styles import in dev
Fix:
```
$ yarn start
[vite] warning:
@theme/default
1  |  import "C:\Users\MLM\Documents\GitHub\element\hydrogen-web\src\platform\web\ui\css\themes\element\theme.css";import "@theme/element/light/variables.css"
   |          ^
The above dynamic import cannot be analyzed by vite.
See https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations for supported dynamic import formats. If this is intended to be left as-is, you can use the /* @vite-ignore */ comment inside the import() call to suppress this warning.

  Plugin: vite:import-analysis
  File: @theme/default
```

And in the browser, it results in none of the styles loading because of the following error:
```
Uncaught SyntaxError: Invalid Unicode escape sequence (at default:formatted:1:163)
```

---

Before:
```
import { injectQuery as __vite__injectQuery } from "/@vite/client";import "__vite__injectQuery(C:\Users\MLM\Documents\GitHub\element\hydrogen-web\src\platform\web\ui\css\themes\element\theme.css, 'import')";import "/@id/__x00__@theme/element/light/variables.css"
```

After:
```
import "/ui/css/themes/element/theme.css";import "/@id/__x00__@theme/element/light/variables.css"
```
2022-06-07 23:41:45 -05:00
RMidhunSuresh
d31f127982 Add explaining comment 2022-06-07 13:28:56 +05:30
RMidhunSuresh
d08cfe3a29 Add more logging 2022-06-07 11:57:57 +05:30
RMidhunSuresh
51a837d459 Remove unuseed import 2022-06-06 17:26:39 +05:30
RMidhunSuresh
2f0f7143b5 Simplify code 2022-06-06 17:20:36 +05:30
RMidhunSuresh
0dac00f327 themeVariant is optional 2022-06-06 17:20:16 +05:30
RMidhunSuresh
a639fc5467 Rever to sensisble defaults 2022-06-06 12:20:06 +05:30
RMidhunSuresh
258a604cc6 Don't make defaultTheme compulsory 2022-06-06 12:19:48 +05:30
RMidhunSuresh
a2cbac9e0c Move code into method 2022-06-06 11:53:13 +05:30
RMidhunSuresh
71c3fb39a2 store theme-name and variant in settings 2022-06-05 20:52:47 +05:30
RMidhunSuresh
43244fa026 Add explaining comment 2022-06-05 20:52:47 +05:30
RMidhunSuresh
9e88bc3098 Fix bugs 2022-06-05 20:52:47 +05:30
RMidhunSuresh
b74f4b612b Change UI 2022-06-05 20:52:47 +05:30
RMidhunSuresh
8de91291dd Add more methods to ThemeLoader 2022-06-05 20:52:47 +05:30
RMidhunSuresh
dc2d1ce700 Remove id 2022-06-05 20:52:47 +05:30
RMidhunSuresh
12a8e94243 Move code into ThemeLoader 2022-06-05 20:52:47 +05:30
RMidhunSuresh
9e79b632a8 Extract variable 2022-06-05 20:52:47 +05:30
RMidhunSuresh
efb1a67470 Make method name a verb 2022-06-05 20:52:47 +05:30
RMidhunSuresh
e3235ea3eb Rename themeName --> themeId 2022-06-05 20:52:47 +05:30
RMidhunSuresh
46d2792dac Modify comment 2022-06-05 20:52:47 +05:30
RMidhunSuresh
8ad0b8a726 rename themeName --> variantName 2022-06-05 20:52:47 +05:30
RMidhunSuresh
e8e4c33bae Rephrase comment 2022-06-05 20:52:47 +05:30
RMidhunSuresh
cb03e97e78 Use default theme intially 2022-06-05 20:52:47 +05:30
RMidhunSuresh
f6cec938a7 Add default theme to mapping 2022-06-05 20:52:47 +05:30
RMidhunSuresh
bbec2effe5 Add typing 2022-06-05 20:52:47 +05:30
RMidhunSuresh
d4084da299 Extract code into function 2022-06-05 20:52:47 +05:30
RMidhunSuresh
1f00c8f635 Add a temporary theme to test this PR 2022-06-05 20:52:47 +05:30
RMidhunSuresh
0b98473e85 Render a radio button for default variants 2022-06-05 20:52:47 +05:30
RMidhunSuresh
3afbe1148e Use the new built-asset format in ThemeLoader 2022-06-05 20:52:47 +05:30
RMidhunSuresh
809c522571 Change the format of built-asset 2022-06-05 20:52:47 +05:30
RMidhunSuresh
4474458f4b getActiveTheme should never return undefined
Instead it should throw an error.

This is useful for when we do setTheme(await getActiveTheme()) because
setTheme expects a string.
2022-06-05 20:52:47 +05:30
Eric Eastwood
9d8a578dce Better comment 2022-05-31 15:35:48 -05:00
Eric Eastwood
38c3774869 Import assets from the assets/ directory
> Will be easier towards the future when adding more assets. Probably best to keep style.css for now for backwards compat though.
>
> *-- https://github.com/vector-im/hydrogen-web/pull/693#discussion_r853844282*
2022-05-31 15:30:56 -05:00
Bruno Windels
8b2299852e
Merge pull request #744 from vector-im/bwindels/fix-tracker-changed-key-check
Fix: device with changed key not being properly ignored
2022-05-31 13:51:17 +02:00
Bruno Windels
c62c8da10b fix changed key not being ignored 2022-05-31 13:39:35 +02:00
Bruno Windels
bc51644868 reassignment is not used later on, remove 2022-05-31 13:39:23 +02:00
Bruno Windels
3d3d590334 add failing test for device with changed key being returned 2022-05-31 13:39:05 +02:00
Bruno Windels
11d7535c23 add some basic tests (with mock utils) for DeviceTracker 2022-05-31 13:38:34 +02:00
Bruno Windels
a49d7eae5d
Merge pull request #693 from vector-im/madlittlemods/686-682-local-friendly-development-and-commonjs
Make the SDK friendly to locally link and develop on
2022-05-30 14:45:16 +02:00
Bruno Windels
1b2a6b5d0e
Merge branch 'master' into madlittlemods/686-682-local-friendly-development-and-commonjs 2022-05-30 14:15:19 +02:00
RMidhunSuresh
ba647d012d Fix type in observeNavigation 2022-05-29 20:38:14 +05:30
RMidhunSuresh
fc873757d8 WIP 2022-05-27 22:42:21 +05:30
RMidhunSuresh
ec1cc89cf9 Make URLRouter in options conditional on generic
URLRouter can be passed in option to vm only if the SegmentType used
contains session.
ViewModel.urlCreator returns undefined when used with a SegmentType that
lacks session.
2022-05-27 22:42:21 +05:30
RMidhunSuresh
a336623f3a Generic parameter should extend object 2022-05-27 22:42:21 +05:30
RMidhunSuresh
9300347e9b Give defaultt type 2022-05-27 22:42:21 +05:30
RMidhunSuresh
f49d580d49 WIP 2022-05-27 22:42:21 +05:30
RMidhunSuresh
263948faa3 Remove unwanted export 2022-05-27 22:42:21 +05:30
RMidhunSuresh
52f0690c70 Add return type 2022-05-27 22:42:21 +05:30
RMidhunSuresh
7a24059337 Remove empty line 2022-05-27 22:42:21 +05:30
RMidhunSuresh
4fd1918202 Remove comment 2022-05-27 22:42:21 +05:30
RMidhunSuresh
4ae3a5bf7a Use undefined instead of null 2022-05-27 22:42:21 +05:30
RMidhunSuresh
5be00f051f Use subtype instead of whole SegmentType 2022-05-27 22:42:21 +05:30
RMidhunSuresh
e7f4ce6175 Mark methods as private 2022-05-27 22:42:21 +05:30
RMidhunSuresh
09bc0f1b60 Extract complex type as type alias 2022-05-27 22:42:21 +05:30
RMidhunSuresh
76d04ee277 Make defaultSessionId optional 2022-05-27 22:42:21 +05:30
RMidhunSuresh
f28dfc6964 Type createRouter function 2022-05-27 22:42:21 +05:30
RMidhunSuresh
c14e4f3eed Use segment type 2022-05-27 22:42:21 +05:30
RMidhunSuresh
5d42f372f6 Pass as separate arguments to constructor 2022-05-27 22:42:21 +05:30
RMidhunSuresh
4c3e0a6ff0 Convert URLRouter.js to typescript 2022-05-27 22:42:21 +05:30
RMidhunSuresh
d9bfca10e1 Type function 2022-05-27 22:42:21 +05:30
RMidhunSuresh
bf2fb52691 Fix formatting 2022-05-27 22:42:21 +05:30
RMidhunSuresh
646cbe0fff Make all keys string 2022-05-27 22:42:21 +05:30
RMidhunSuresh
92e8fc8ad3 Remove deprecated method 2022-05-27 22:42:21 +05:30
RMidhunSuresh
92c79c853d Convert index.js to typescript 2022-05-27 22:42:21 +05:30
RMidhunSuresh
55229252d7 Type allowsChild 2022-05-27 22:42:21 +05:30
RMidhunSuresh
3efc426fed Complete converting Navigation.js to ts 2022-05-27 22:42:21 +05:30
RMidhunSuresh
04d5b9bfda WIP - 2 2022-05-27 22:42:21 +05:30
RMidhunSuresh
66f6c4aba1 WIP 2022-05-27 22:42:18 +05:30
Bruno Windels
ed8c98558d release v0.2.29 2022-05-18 21:45:45 +02:00
Bruno Windels
514d5c0a50
add notes about client side caching 2022-05-18 19:44:39 +00:00
Bruno Windels
13428bd03c allow updating cache of unhashed assets (like config) in service worker 2022-05-18 21:41:47 +02:00
Bruno Windels
1555b0f4bc put a message in container node when config file is not found 2022-05-18 21:41:31 +02:00
Bruno Windels
0e46aed0df rename config file to config.sample.json when packaging 2022-05-18 20:52:18 +02:00
Bruno Windels
7b0591be46 explain that push section of config usually doesn't need to be touched 2022-05-18 20:51:50 +02:00
Bruno Windels
f21e103270 add newlines to config file when rewriting with theme stuff 2022-05-18 20:46:38 +02:00
Bruno Windels
7a197c0a1a add deployment instruction now that we have a config file 2022-05-18 20:44:04 +02:00
Bruno Windels
8a5f1ed9cd Merge remote-tracking branch 'origin/move-config-root' 2022-05-18 20:40:12 +02:00
Bruno Windels
36ddd61318
Merge pull request #724 from vector-im/theme-chooser
Implement theme chooser in settings
2022-05-18 20:22:38 +02:00
Bruno Windels
03ab1ee2c7 log theme being loaded 2022-05-18 17:48:03 +02:00
RMidhunSuresh
a550788788 Remove some logging + use wrapOrRun 2022-05-18 18:56:28 +05:30
RMidhunSuresh
683ffa9ed3 injectServiceWorker plugin should accept callback 2022-05-18 17:31:17 +05:30
RMidhunSuresh
7952a34d64 Add logging 2022-05-18 16:09:09 +05:30
RMidhunSuresh
7426d17e33 Precache config and theme manifest 2022-05-18 16:07:26 +05:30
RMidhunSuresh
660a08db3e Give a better name 2022-05-18 14:41:52 +05:30
RMidhunSuresh
1b22a48b54 Treat theme-manifests the same way as config 2022-05-18 14:23:41 +05:30
Eric Eastwood
b725269c7a Clean up index.html in the right spot 2022-05-18 00:21:56 -05:00
Eric Eastwood
639358b146 Merge branch 'master' into madlittlemods/686-682-local-friendly-development-and-commonjs
Conflicts:
	scripts/sdk/base-manifest.json
2022-05-12 12:05:45 -05:00
RMidhunSuresh
34e8b60917 Create config.json in root 2022-05-12 16:05:33 +05:30
RMidhunSuresh
9ba1534390 Remove unused import 2022-05-12 16:03:06 +05:30
RMidhunSuresh
4ddfd3b508 built-asset --> built-assets 2022-05-12 14:56:58 +05:30
RMidhunSuresh
e63440527a Move condition to binding 2022-05-12 13:43:19 +05:30
RMidhunSuresh
0984aeb570 Move code to ThemeLoader 2022-05-12 13:39:57 +05:30
RMidhunSuresh
654e83a5f9 Remove method 2022-05-12 13:28:11 +05:30
RMidhunSuresh
b306344739 Add explaining comment 2022-05-12 12:55:08 +05:30
R Midhun Suresh
4231037345
Update src/platform/web/Platform.js
Co-authored-by: Bruno Windels <274386+bwindels@users.noreply.github.com>
2022-05-12 12:48:41 +05:30
R Midhun Suresh
d5bc9f5d7d
Update src/platform/web/Platform.js
Co-authored-by: Bruno Windels <274386+bwindels@users.noreply.github.com>
2022-05-12 12:48:34 +05:30
Bruno Windels
6fde6bbf6b bump sdk version 2022-05-11 14:58:57 +02:00
RMidhunSuresh
cc88245933 Create themeLoader only if not dev 2022-05-11 15:46:12 +05:30
RMidhunSuresh
174adc0755 Move platform dependent code to Platform 2022-05-11 15:38:37 +05:30
RMidhunSuresh
c26dc04b52 Fix type 2022-05-11 15:03:32 +05:30
RMidhunSuresh
2761789f45 Move theme code to separate file 2022-05-11 14:58:14 +05:30
RMidhunSuresh
213f87378b Use t.if instead of t.map 2022-05-11 12:46:12 +05:30
RMidhunSuresh
855298bdaf Read from manifest 2022-05-11 12:40:32 +05:30
RMidhunSuresh
e8a4ab5ecc built-asset must be a mapping
A mapping from theme-name to location of css file
2022-05-10 16:58:06 +05:30
RMidhunSuresh
5204fe5c99 This emitFile is no longer needed 2022-05-10 14:22:37 +05:30
RMidhunSuresh
c39f0d2efb Don't show theme chooser on dev 2022-05-10 14:12:36 +05:30
RMidhunSuresh
bb3368959f Use sh instead of bash 2022-05-10 14:12:36 +05:30
RMidhunSuresh
af9cbd727f Remove existing stylesheets when changing themes 2022-05-10 14:12:36 +05:30
RMidhunSuresh
12a70469eb Fix formatting 2022-05-10 14:12:36 +05:30
RMidhunSuresh
c611d3f85c Select current theme in dropdown 2022-05-10 14:12:36 +05:30
RMidhunSuresh
ecb83bb277 Store and load theme from setting 2022-05-10 14:12:36 +05:30
RMidhunSuresh
daae7442bb Create theme chooser 2022-05-10 14:12:36 +05:30
RMidhunSuresh
cc2c74fdff Generate theme summary on build 2022-05-10 14:12:36 +05:30
RMidhunSuresh
541cd96eeb Add script to cleanup after build 2022-05-10 14:12:36 +05:30
RMidhunSuresh
f16a2e5d22 Don't add asset hash to manifest json on build 2022-05-10 14:12:36 +05:30
Bruno Windels
b7675f46c4 bump sdk version 2022-05-10 09:59:38 +02:00
R Midhun Suresh
a06474d7ac
Merge pull request #731 from vector-im/fix-tilescollection
Newly created tiles must be given a copy of tilesOptions
2022-05-10 12:33:46 +05:30
Bruno Windels
e903d3a6a4 mark options as readonly 2022-05-09 14:12:31 +02:00
Bruno Windels
3888291758 updateOptions is unused,not the best idea since options is/can be shared 2022-05-09 14:10:50 +02:00
Bruno Windels
6beff7e552 override emitChange so no need to clone option object for all tiles
instead, we don't store the emitChange in the options but rather on
the tile itself.
2022-05-09 14:09:45 +02:00
RMidhunSuresh
139a87de99 Pass a copy of the options to the tiles 2022-05-08 19:14:51 +05:30
Eric Eastwood
e54482e4c0 Add some comments 2022-05-05 17:57:25 -05:00
Eric Eastwood
75098b4712 Merge branch 'master' into madlittlemods/686-682-local-friendly-development-and-commonjs 2022-05-05 17:50:33 -05:00
Eric Eastwood
d053d4388f Update Vite to avoid flakey errors in our PostCSS plugins
Fix https://github.com/vector-im/hydrogen-web/issues/722

Updating Vite to includes fixes from
https://github.com/vitejs/vite/issues/7822 -> https://github.com/vitejs/vite/pull/7827
2022-05-05 14:58:43 -05:00
Bruno Windels
23b621492f
Merge pull request #726 from vector-im/flow-registration
Allow passing in flowSelector from startRegistration method
2022-04-27 11:18:53 +02:00
RMidhunSuresh
83664a1b13 viewClassForTile is needed for TimelineView 2022-04-27 12:38:12 +05:30
RMidhunSuresh
c07a42292c Include Platform change in sdk docs 2022-04-27 12:28:48 +05:30
RMidhunSuresh
049a477008 Pass flowSelector from Client.startRegistration 2022-04-27 12:27:19 +05:30
Bruno Windels
fa34315210 undo refactoring typo from #723 2022-04-25 16:44:31 +02:00
Bruno Windels
bec8cea583 fix for breaking in #725 2022-04-25 14:17:07 +02:00
Bruno Windels
3536d12680
Merge pull request #725 from vector-im/bwindels/templateview-ts
add typing for text bindings in template view
2022-04-25 12:42:37 +02:00
Bruno Windels
ab893f63b5 remove unneeded assignment 2022-04-25 12:40:25 +02:00
Bruno Windels
6c57c96cb9 add typing for text bindings in template view 2022-04-25 12:07:28 +02:00
R Midhun Suresh
6ba5fbeebb
Merge pull request #723 from vector-im/implement-609
Read config.json on app start
2022-04-22 15:00:02 +05:30
RMidhunSuresh
d8da128780 remove await 2022-04-22 14:34:16 +05:30
RMidhunSuresh
7a33c2e00d await 2022-04-22 12:26:29 +05:30
RMidhunSuresh
5a94a2feba Move handleConfigRequest inside handleRequest 2022-04-22 12:22:30 +05:30
RMidhunSuresh
c6691cf1cb Simplify code 2022-04-22 12:10:25 +05:30
RMidhunSuresh
826835e518 No need to rewrite to index.html 2022-04-22 12:07:53 +05:30
RMidhunSuresh
b6e55ef59c Remove comment 2022-04-21 14:46:55 +05:30
RMidhunSuresh
4f23944581 Use named param in Legacy Platform 2022-04-21 14:17:47 +05:30
RMidhunSuresh
1cdc76f5a4 Use undefine instead of null 2022-04-21 14:14:38 +05:30
RMidhunSuresh
468b7e1595 Cache config.json 2022-04-21 12:52:42 +05:30
Eric Eastwood
ce289baba6 Remove extra space 2022-04-20 17:32:12 -05:00
Eric Eastwood
f1e07b6842 Explain what is being deleted by the strange syntax
See https://github.com/vector-im/hydrogen-web/pull/693#discussion_r815284713
2022-04-20 11:59:49 -05:00
Eric Eastwood
e9cee2e6a4 Merge branch 'master' into madlittlemods/686-682-local-friendly-development-and-commonjs
Conflicts:
	scripts/sdk/build.sh
2022-04-20 11:58:39 -05:00
Eric Eastwood
5f8a171c2c
Fix asset build throwing and swallowing errors (#721)
- Fix `svg-colorizer` throwing errors with Windows file paths
 - Fix `css-url-parser` swallowing errors because it was `async`
 - Fail SDK build script (`yarn build:sdk`, `build.sh`) overall when some commands are failing
2022-04-20 11:55:48 -05:00
RMidhunSuresh
6cd3c8ee2b Read config from URL 2022-04-20 12:42:07 +05:30
RMidhunSuresh
2cfcd4653f Use named params 2022-04-20 12:00:33 +05:30
Eric Eastwood
f56dc582a5 Fix tests after theme updates 2022-04-20 00:39:32 -05:00
Eric Eastwood
f61bf6090e Enable extended globs for removing all but some filename !(filename)
See https://github.com/vector-im/hydrogen-web/pull/693#discussion_r853534719
2022-04-19 17:28:09 -05:00
Eric Eastwood
12d6447b06 Merge branch 'master' into madlittlemods/686-682-local-friendly-development-and-commonjs
Conflicts:
	package.json
	scripts/sdk/base-manifest.json
	scripts/sdk/build.sh
2022-04-19 17:19:13 -05:00
Bruno Windels
480c5c1584 update SDK docs with new style location 2022-04-14 13:49:54 +02:00
Bruno Windels
2d6cbcfce0 release v0.2.28 2022-04-14 10:38:17 +02:00
Bruno Windels
78f352b839 avoid white ring around avatars in dark theme 2022-04-14 10:38:08 +02:00
Bruno Windels
cbdd7548da release v0.2.27 2022-04-14 09:53:21 +02:00
Bruno Windels
3b74e2ea7e
Merge pull request #712 from vector-im/theme-convert
Theming - Convert existing theme to use new theming architecture
2022-04-14 09:52:38 +02:00
RMidhunSuresh
3f4dddc004 Add backwards compatibility 2022-04-13 17:49:38 +05:30
RMidhunSuresh
5170329c79 Remove unsued imports 2022-04-13 17:44:07 +05:30
RMidhunSuresh
2d8a3d9f9b Fix SDK build 2022-04-13 17:12:38 +05:30
RMidhunSuresh
83dffef47d Use new theme config 2022-04-13 14:26:40 +05:30
RMidhunSuresh
23aac5cb45 Make theme-name lowercase in manifest 2022-04-13 14:26:40 +05:30
RMidhunSuresh
f7bfab6e08 Add develop only script tag to index.html 2022-04-13 14:26:40 +05:30
RMidhunSuresh
5e7432b5de Make badge font color always white 2022-04-13 14:26:40 +05:30
RMidhunSuresh
2de0450e97 Make colors better looking for dark variant 2022-04-13 14:26:40 +05:30
RMidhunSuresh
f26b51e5da Change colors in more css files 2022-04-13 14:26:40 +05:30
RMidhunSuresh
bf74c3c67b Add more colors to manifest 2022-04-13 14:26:40 +05:30
RMidhunSuresh
3d304be211 Convert theme.css
- Use color variables
- Use colorized icons
2022-04-13 14:26:40 +05:30
RMidhunSuresh
698d47e221 Enable plugins in config 2022-04-13 14:26:40 +05:30
RMidhunSuresh
3e2a2b7942 Add theme manifest 2022-04-13 14:26:40 +05:30
RMidhunSuresh
061dc5f824 Replace icon colors with predefined color 2022-04-13 14:26:40 +05:30
R Midhun Suresh
366e75b242
Merge pull request #716 from vector-im/vite-plugin-dev
Theming - Support theming in dev server
2022-04-13 14:20:04 +05:30
R Midhun Suresh
b76fb70579
Merge pull request #717 from vector-im/fix-css-url-processor
Theming - Fix css-url-processor
2022-04-13 14:19:36 +05:30
R Midhun Suresh
aacd0e6dfb
Merge pull request #718 from vector-im/fix-css-compile-variables
Theming - Some more changes for compile-variables plugin
2022-04-13 14:19:21 +05:30
RMidhunSuresh
bf0cdcd3f1 Add explaining comment 2022-04-13 13:39:20 +05:30
RMidhunSuresh
825c9847fe Don't hardcode theme/variant names 2022-04-13 12:56:14 +05:30
RMidhunSuresh
14523ecc5d Use new theme option in vite-config 2022-04-13 12:40:49 +05:30
RMidhunSuresh
efef7147af Modify jsdoc comment 2022-04-12 21:02:30 +05:30
RMidhunSuresh
39bc827aaf Invert operation for dark theme 2022-04-12 20:58:14 +05:30
RMidhunSuresh
bb9954a36c Let derive function know if theme is dark 2022-04-12 20:57:43 +05:30
RMidhunSuresh
0b241db058 Produce a mapping of aliases to resolved colors 2022-04-12 20:57:03 +05:30
RMidhunSuresh
743bd0db1c Support dark mode and remove dev script tag 2022-04-12 20:39:04 +05:30
RMidhunSuresh
25a8521efc Use hash instead of UUID 2022-04-12 20:15:14 +05:30
RMidhunSuresh
36782fb4fe Use unique filenames
Otherwise newly produced svgs will replace other svgs produced earlier
in the build.
2022-04-12 19:44:29 +05:30
RMidhunSuresh
6456d4ef76 Cache cssPath 2022-04-10 14:59:42 +05:30
RMidhunSuresh
49535807bf Do not run plugin on runtime theme 2022-04-10 14:59:08 +05:30
RMidhunSuresh
0a95eb0940 Fix formatting 2022-04-10 14:52:26 +05:30
RMidhunSuresh
ff98ef4465 Support theming in dev server 2022-04-10 14:49:19 +05:30
Bruno Windels
a6b6fef6d2 sdk release 0.0.10 2022-04-08 17:48:20 +02:00
Bruno Windels
c9bc080aef
Merge pull request #713 from vector-im/bwindels/fix-request-responsecode-error
fix error thrown during request when response code is not used
2022-04-08 15:26:12 +02:00
Bruno Windels
4cbd149c25
Merge pull request #715 from vector-im/bwindels/rename-viewclassfortile
Some timeline refactoring and also make reply tiles of correct custom view class
2022-04-08 15:19:39 +02:00
Bruno Windels
cf780ce259 also apply custom tiles in reply preview in composer 2022-04-08 15:16:22 +02:00
Bruno Windels
d21d10e4f2 pass in viewClassForTile from SessionView
so you can also use custom tiles when using the grid view
2022-04-08 15:15:21 +02:00
Bruno Windels
1fea14dd10 ensure other parameters don't get passed to TemplateView parent ctors 2022-04-08 15:04:38 +02:00
Bruno Windels
1f0cb542c8 pass viewClassForTile to tile views, so they can create reply view with correct subtile 2022-04-08 15:02:07 +02:00
Bruno Windels
57f50cc416 fix lint warnings 2022-04-08 15:01:27 +02:00
Bruno Windels
cda96a35ee rename viewClassForEntry to viewClassForTile 2022-04-08 15:01:06 +02:00
Bruno Windels
e977a6829b
Merge pull request #714 from vector-im/bwindels/custom-tiles
Allow custom timeline tiles for SDK usage
2022-04-08 14:29:54 +02:00
Bruno Windels
ac4bb8ca15 export tile view & view models from SDK 2022-04-08 14:27:08 +02:00
Bruno Windels
a913671f0c make tileClassForEntry optional, as otherwise it is a breaking change 2022-04-08 14:19:34 +02:00
Bruno Windels
5445db2a42 allow injecting the tilesCreator from the Root/Session/RoomViewModel
this changes the API slightly to be more future-proof,
as we'll expose it in the SDK now.

The function now returns a SimpleTile constructor, rather than an
instance. This allows us to test if an entry would render in the
timeline without creating a tile, which is something we might want in
the matrix layer later on.

The function is now called tileClassForEntry, analogue to what we
do in TimelineView.
2022-04-08 12:52:30 +02:00
Bruno Windels
220f35ae03 fix typescript error 2022-04-08 11:52:21 +02:00
Bruno Windels
6aa79cf6e2 allow to inject custom tile view creator fn into timeline view 2022-04-07 17:25:20 +02:00
Bruno Windels
88482292e1
Merge pull request #700 from vector-im/ajbura-patch-2
Add observeNavigation in ViewModel
2022-04-07 14:08:40 +02:00
Bruno Windels
9755062563 fix error thrown during request when response code is not used 2022-04-07 10:35:00 +02:00
R Midhun Suresh
0a225292f0
Merge pull request #704 from vector-im/vite-plugin
Theming - Rollup plugin to enumerate and compile themes (and their variants)
2022-04-07 11:57:03 +05:30
R Midhun Suresh
1b18b1f815
Merge pull request #707 from vector-im/css-url-processor
Theming - Postcss plugin to process URLs
2022-04-07 11:54:53 +05:30
RMidhunSuresh
c0fb8a2c77 Throw error if no replacements were made 2022-04-07 11:53:11 +05:30
RMidhunSuresh
f2b4f2e069 Remove console.log 2022-04-07 11:53:11 +05:30
RMidhunSuresh
7046fcc7c7 Find list of resolved colors from result
and also throw only if secondary color was provided
2022-04-07 11:53:11 +05:30
RMidhunSuresh
8c6400ab2c utf-8 --> utf8 2022-04-07 11:53:11 +05:30
RMidhunSuresh
5d5eb93baa Implement plugin 2022-04-07 11:53:11 +05:30
R Midhun Suresh
4ded893880
Merge pull request #703 from vector-im/css-url-variables-plugin
Theming - Postcss plugin to replace URL values with css variable
2022-04-07 11:44:46 +05:30
RMidhunSuresh
bfd73ae52a Pass derive function as argument 2022-04-07 11:37:20 +05:30
RMidhunSuresh
6d724e27e7 No need to check if icons are already written 2022-04-07 11:35:24 +05:30
RMidhunSuresh
2dd655cd9a Check if icon is in shared var 2022-04-07 11:35:24 +05:30
RMidhunSuresh
9a96112146 Rename function name 2022-04-07 11:35:24 +05:30
RMidhunSuresh
545ff2ec32 Add explaining comment 2022-04-07 11:35:24 +05:30
RMidhunSuresh
5e702171ce Remove console.log 2022-04-07 11:35:24 +05:30
RMidhunSuresh
cd4fce0c6f Populate shared map with collected icons 2022-04-07 11:35:24 +05:30
RMidhunSuresh
1a50effd86 Only extract into variables if file is svg 2022-04-07 11:35:24 +05:30
RMidhunSuresh
b7a47ae901 Give function better name 2022-04-07 11:35:24 +05:30
RMidhunSuresh
0a186dd11b Fix css logic 2022-04-07 11:35:24 +05:30
RMidhunSuresh
f07a3ea5b5 Remove css specific syntax from map 2022-04-07 11:35:24 +05:30
RMidhunSuresh
2d4ec5380e Initialize variables later 2022-04-07 11:35:24 +05:30
RMidhunSuresh
6b4bb762aa Remove unused variable 2022-04-07 11:35:24 +05:30
RMidhunSuresh
97ade0659c Add explaining comment 2022-04-07 11:35:24 +05:30
RMidhunSuresh
b59d6970fc Fix code duplication in tests 2022-04-07 11:35:21 +05:30
RMidhunSuresh
cbff912476 Improve code quality 2022-04-07 11:34:11 +05:30
RMidhunSuresh
3ae2b4dab4 Use two url() in test 2022-04-07 11:34:11 +05:30
RMidhunSuresh
f897e5132c Implement url to variables plugin 2022-04-07 11:34:11 +05:30
R Midhun Suresh
e0bc9b31a9
Merge pull request #709 from vector-im/compile-variables-improvement
Theming - postcss-compile-variables improvement
2022-04-07 11:31:59 +05:30
RMidhunSuresh
f75ee86c0e Change comment 2022-04-06 12:30:26 +05:30
RMidhunSuresh
7f9af5b5fa Add icon to manifest 2022-04-06 12:30:26 +05:30
RMidhunSuresh
b0f082e81f Add derived variables to source section 2022-04-06 12:30:26 +05:30
RMidhunSuresh
d5b5e10230 Produce manifest.jsom 2022-04-06 12:30:26 +05:30
RMidhunSuresh
86c45b5b99 Emit runtime bundle 2022-04-06 12:30:26 +05:30
RMidhunSuresh
32eb95734a Add default themes to index html 2022-04-06 12:30:26 +05:30
RMidhunSuresh
1f6efb4db3 Write plugin code 2022-04-06 12:30:26 +05:30
RMidhunSuresh
48d0242c80 Also derive variables in URLs 2022-04-06 12:23:55 +05:30
Eric Eastwood
2401b7f453 Add way to test whether SDK works in ESM and CommonJS 2022-04-05 19:24:27 -05:00
Eric Eastwood
dd06d78a72 Avoid ERR_REQUIRE_ESM errors when requiring SDK 2022-04-05 18:17:14 -05:00
Eric Eastwood
95d17303c3 Update Vite which includes fixes to importing *.js?url with exports
Update to Vite which includes https://github.com/vitejs/vite/pull/7098
2022-04-05 17:16:55 -05:00
Eric Eastwood
d247bc4e28 Merge branch 'master' into madlittlemods/686-682-local-friendly-development-and-commonjs
Conflicts:
	package.json
	scripts/sdk/base-manifest.json
2022-04-05 17:15:30 -05:00
RMidhunSuresh
454345c9b2 Always set map 2022-04-05 15:08:35 +05:30
RMidhunSuresh
76789eacf1 Use array instead of Set 2022-04-01 20:43:42 +05:30
RMidhunSuresh
859449ed60 Write test for map population 2022-04-01 16:41:00 +05:30
RMidhunSuresh
918a3e42b1 Populate compiled variables map 2022-04-01 16:23:33 +05:30
RMidhunSuresh
4350d2f264 Don't derive variables for runtime theme 2022-04-01 16:20:58 +05:30
RMidhunSuresh
2015fa2d7a Move postcss-value-parser to dev dependency 2022-03-27 20:18:42 +05:30
RMidhunSuresh
e8bd1f3390 Pass result as message 2022-03-27 20:06:26 +05:30
R Midhun Suresh
66304ed7e0
Merge pull request #701 from vector-im/css-compile-variables-plugin
Theming - Postcss plugin to compile variables
2022-03-24 12:14:46 +05:30
RMidhunSuresh
72785e7c3e Remove -- from everywhere 2022-03-23 20:39:24 +05:30
RMidhunSuresh
59ca8e6309 Add explanation of plugin 2022-03-23 17:25:12 +05:30
RMidhunSuresh
5d4323cd1d Remove stray "--" from code 2022-03-23 17:12:14 +05:30
RMidhunSuresh
19a6d669a9 Extract base variables from css 2022-03-14 23:26:37 +05:30
RMidhunSuresh
bca1648df6 Move plugin to /scripts and create eslintrc 2022-03-14 11:35:10 +05:30
RMidhunSuresh
4020ade70c Remove redundant comment 2022-03-10 17:51:25 +05:30
RMidhunSuresh
2c068cc3ce typo 2022-03-10 17:42:12 +05:30
RMidhunSuresh
6f4a7e074a Change confusing doc 2022-03-10 17:27:12 +05:30
RMidhunSuresh
9f77df0bff Match regex only if declaration is a variable 2022-03-10 17:24:32 +05:30
RMidhunSuresh
ff10297bf8 Explicitly convert to number 2022-03-10 17:22:02 +05:30
RMidhunSuresh
f732164b5f Formatting change 2022-03-10 17:21:38 +05:30
RMidhunSuresh
5210123977 Document options 2022-03-10 17:19:04 +05:30
RMidhunSuresh
1663782954 Throw after fetching value 2022-03-10 16:05:13 +05:30
RMidhunSuresh
63c1f2a7a3 Add node as env to eslint 2022-03-09 17:22:45 +05:30
RMidhunSuresh
96fa83b508 Add license header 2022-03-09 17:22:11 +05:30
RMidhunSuresh
79f363fb9d Move code to callback and fix alias code 2022-03-09 17:20:05 +05:30
Bruno Windels
ca211f929b
Merge pull request #702 from vector-im/bwindels/observablemapts
convert (Base)ObservableMap to typescript
2022-03-09 11:53:59 +01:00
Bruno Windels
6150e91c3f fix type error again 2022-03-09 11:51:11 +01:00
Bruno Windels
762925d4a5 fix type error 2022-03-09 11:44:49 +01:00
Bruno Windels
21080d2110 fix tests 2022-03-09 11:41:26 +01:00
Bruno Windels
6d7c983e8e convert (Base)ObservableMap to typescript 2022-03-09 11:33:49 +01:00
RMidhunSuresh
a83850ebf3 Use postcss value parser to find variables 2022-03-09 11:48:53 +05:30
RMidhunSuresh
41f6b6ab6b Use startsWith instead of regex testing 2022-03-07 13:25:53 +05:30
RMidhunSuresh
a5d46bb40c Move over tests to Hydrogen using impunity 2022-03-07 13:10:44 +05:30
RMidhunSuresh
f170ef0206 Switch over to off-color 2022-03-07 11:38:39 +05:30
Ajay Bura
e07abfa02a
Add missing type 2022-03-07 11:33:51 +05:30
RMidhunSuresh
b6f5e68e9e Format file 2022-03-07 11:33:44 +05:30
RMidhunSuresh
92084e8005 Move all code under the Once event
Apparently the other events are common to all plugins.
2022-03-07 11:32:30 +05:30
Bruno Windels
8b8233ff00
Merge pull request #691 from vector-im/madlittlemods/only-crypto-in-secure-context
Only initialize `Crypto` when olm is provided
2022-03-03 17:33:50 +01:00
RMidhunSuresh
60d60e9572 WIP 2022-03-03 19:58:46 +05:30
Ajay Bura
61ce2f9e3d
Add observeNavigation in ViewModel 2022-03-03 15:36:25 +05:30
Eric Eastwood
2f4c639cef Only initialize Crypto when olm is provided
See https://github.com/vector-im/hydrogen-web/pull/691#discussion_r816988082
2022-03-02 03:17:59 -06:00
Eric Eastwood
c09964dc30
Add data-event-id="$xxx" attributes to timeline items for easy selecting in end-to-end tests (#690)
Split out from https://github.com/vector-im/hydrogen-web/pull/653

Example test assertions: db6d3797d7/test/e2e-tests.js (L248-L252)

```js
// Make sure the $abc event on the page has "foobarbaz" text in it
assert.match(
  dom.document.querySelector(`[data-event-id="$abc"]`).outerHTML,
  new RegExp(`.*foobarbaz.*`)
);
```
2022-03-01 18:36:14 -06:00
Bruno Windels
2e1283d199
Merge pull request #670 from vector-im/bwindels/ts-olm
Convert olm code to typescript
2022-03-01 18:53:22 +01:00
Bruno Windels
62ce111938
Merge pull request #692 from ryushar/ryushar/typescriptify
Convert domain/avatar.js and domain/LogoutViewModel.js to Typescript
2022-03-01 18:50:19 +01:00
Bruno Windels
770f7aea00
Merge pull request #689 from vector-im/madlittlemods/add-more-html-elements
Add more HTML form and SVG elements
2022-03-01 18:43:34 +01:00
Bruno Windels
b6d9993ed0 remove unused import 2022-03-01 17:08:49 +01:00
Bruno Windels
643ab1a5f3 cant export this for some reason 2022-03-01 15:48:42 +01:00
Bruno Windels
42141c7063 bump SDK version 2022-03-01 15:45:24 +01:00
Bruno Windels
1087d62705
Merge pull request #695 from vector-im/ajbura-patch-1
Export some more symbols from the SDK
2022-03-01 15:44:51 +01:00
Bruno Windels
ee8e45926f also export observable value classes 2022-03-01 15:42:04 +01:00
Bruno Windels
4c50dbf7ec make SDK exports explicit 2022-03-01 15:41:44 +01:00
Ajay Bura
4a4856a29e
export module 2022-02-28 17:19:01 +05:30
Eric Eastwood
0023ab34ba Add a placeholder for upgrading vite to comment on 2022-02-26 05:19:59 -06:00
Eric Eastwood
8fb2b2755a Fix typos pointing to wrong files 2022-02-26 03:08:16 -06:00
Eric Eastwood
cd007b40e1 Make the SDK friendly to locally link and develop on
Fix https://github.com/vector-im/hydrogen-web/issues/686
Fix https://github.com/vector-im/hydrogen-web/issues/682

Instead of deleting the whole `target/` directory, leave it alone so the symlink
driving the `npm link`/`yarn link` stays in tact.

Leave Vite builds in their build directories (`/lib-build`/`/asset-build`)
so you can `vite build --watch` to build on local changes and still have a
consisent place to reference in the `package.json` `exports`. Previously,
everything relied on `build.sh` which does a bunch of moving and renaming
and made it hard to rebuild on changes.

Add back support for CommonJS (adding the `package.json` `exports`).

The last piece is making sure the `?url` imports (`import workerPath from 'hydrogen-view-sdk/main.js?url';`)
work still. It looks like this may have just been solved via
https://github.com/vitejs/vite/issues/6725 -> https://github.com/vitejs/vite/pull/7073
(literally 2 days ago) and we just need to wait for the next Vite release 🎉
2022-02-26 01:12:00 -06:00
Tushar
17acda7741 typescriptify domain/LogoutViewModel.js 2022-02-25 16:45:07 +05:30
Tushar
7055f02f16 typescriptify domain/avatar.js 2022-02-25 15:52:54 +05:30
Eric Eastwood
0935f2d23a Only try to use window.crypto.subtle in secure contexts to avoid it throwing and stopping all JavaScript
Relevant error if you crypto is used in a non-secure context like a local LAN IP `http://192.168.1.151:3050/`
```
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'deriveBits')
	at new Crypto
	at new Platform
	at mountHydrogen
```

For my use-case with https://github.com/matrix-org/matrix-public-archive, I don't need crypto/encryption at all.

Docs:

 - https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
 - https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle
    - "Secure context: This feature is available only in secure contexts (HTTPS), in some or all supporting browsers."

---

Related to https://github.com/vector-im/hydrogen-web/issues/579
2022-02-25 01:59:48 -06:00
Eric Eastwood
b993331e06 Add more HTML form and SVG elements
Split off from https://github.com/vector-im/hydrogen-web/pull/653

Personally using `select`, `option`, and `path` currently in https://github.com/matrix-org/matrix-public-archive
but added a few extra SVG elements that seemed common to me.
2022-02-25 01:40:52 -06:00
Bruno Windels
8adc5a9fae these were public actually 2022-02-18 17:24:55 +01:00
Bruno Windels
3f9f0e98c7 remove unused olm property in SenderKeyDecryption 2022-02-18 17:21:27 +01:00
Bruno Windels
82299e5aea
Update src/matrix/e2ee/olm/Decryption.ts
Co-authored-by: R Midhun Suresh <hi@midhun.dev>
2022-02-18 17:18:33 +01:00
Bruno Windels
3330530f68
Update src/matrix/e2ee/DecryptionResult.ts
Co-authored-by: R Midhun Suresh <hi@midhun.dev>
2022-02-18 17:18:25 +01:00
Bruno Windels
620409b3f0 fixup: ctor argument order
as it was an object before, order didn't matter
2022-02-18 17:17:24 +01:00
Bruno Windels
78e0bb1ff0 replace isPreKeyMessage with const enum 2022-02-18 17:00:56 +01:00
Bruno Windels
347edb5988 remove unused storage property 2022-02-18 16:47:47 +01:00
Bruno Windels
0ff1a01b42
Merge pull request #669 from vector-im/bwindels/contrib
Add guide for new contributers
2022-02-17 17:46:57 +01:00
Bruno Windels
91fd0e433a remove changelog notes remainder 2022-02-17 17:44:44 +01:00
Bruno Windels
cdd6112971 finish adapting contribution guide 2022-02-17 17:39:45 +01:00
Bruno Windels
ac48a5a4df bump SDK version to 0.0.8 2022-02-17 10:10:23 +01:00
Bruno Windels
49f6a2c2eb
Merge pull request #679 from vector-im/bwindels/fix-vm-ctor-default-options
always pass options to ViewModel constructor
2022-02-17 10:10:19 +01:00
Bruno Windels
2821f4d396
Merge pull request #680 from vector-im/bwindels/export-roomstatus
export RoomStatus in SDK
2022-02-17 09:51:12 +01:00
Bruno Windels
2472f11ec0 export RoomStatus 2022-02-17 09:47:57 +01:00
Bruno Windels
7f1fed6f8c always pass options to ViewModel constructor 2022-02-17 09:24:18 +01:00
Bruno Windels
d971fd1a47
Merge pull request #678 from vector-im/fix-viewmodel-error
Check options exist on emitChange
2022-02-17 09:08:54 +01:00
RMidhunSuresh
498a43327f Check if options exist in emitChange 2022-02-17 11:30:04 +05:30
Bruno Windels
d9acc83182
Merge pull request #675 from vector-im/bwindels/fix-lint-timeline-import
fix lint
2022-02-16 18:05:52 +01:00
Bruno Windels
60f5da60bb fix lint 2022-02-16 18:01:24 +01:00
Bruno Windels
e3e90ed167 convert olm/Encryption to TS 2022-02-16 18:00:13 +01:00
Bruno Windels
eb5ca200f2 missed rename here 2022-02-16 18:00:03 +01:00
Bruno Windels
61b264be3b bump sdk version to 0.0.7 2022-02-16 10:20:53 +01:00
Bruno Windels
37cec04e9c
Merge pull request #671 from vector-im/token-auth-registration
Implement token authenticated registration
2022-02-16 10:20:33 +01:00
RMidhunSuresh
7a9298328f Return _type from getter 2022-02-16 14:37:18 +05:30
RMidhunSuresh
a76bcd1739 Changes in TokenAuth 2022-02-16 13:36:24 +05:30
RMidhunSuresh
60bc4450f3 Use type from server 2022-02-16 13:21:04 +05:30
RMidhunSuresh
ed151c8567 Return token stage from createRegistrationStage 2022-02-16 12:33:59 +05:30
RMidhunSuresh
c40801efd9 Implement the registration stage 2022-02-16 12:33:24 +05:30
Bruno Windels
a4fd1615dd convert decryption 2022-02-15 18:21:29 +01:00
Bruno Windels
74c640f937 convert Session 2022-02-15 18:21:12 +01:00
Bruno Windels
7aeda70ff6 convert DecryptionResult 2022-02-15 18:20:14 +01:00
Bruno Windels
c6dde63abd
Merge pull request #668 from vector-im/bwindels/ts-viewmodel
convert ViewModel to typescript
2022-02-15 15:38:22 +01:00
Bruno Windels
dea1e7eaf3 bump sdk version 2022-02-15 11:31:50 +01:00
Bruno Windels
7179758c50 also here 2022-02-15 08:22:09 +01:00
Bruno Windels
1a159f9e9a WIP 2022-02-14 18:01:04 +01:00
Bruno Windels
1795f58ba5 rename imports 2022-02-14 17:53:59 +01:00
Bruno Windels
4d82dd22b6 convert ViewModel to typescript 2022-02-14 17:50:17 +01:00
Bruno Windels
460780d602
Merge pull request #666 from vector-im/madlittlemods/explicit-domparser-document-for-consistent-return-with-linkedom-ssr
Fix missing reply text when message body is parsed as HTML in `linkedom` (SSR)
2022-02-14 09:57:31 +01:00
Eric Eastwood
dfed04166e Fix missing reply text when message body is parsed as HTML in [linkedom](https://github.com/WebReflection/linkedom) (SSR).
- [`linkedom`](https://github.com/WebReflection/linkedom) is being used https://github.com/matrix-org/matrix-public-archive to server-side render (SSR) Hydrogen (`hydrogen-view-sdk`)
 - This is being fixed by using a explicit HTML wrapper boilerplate with `DOMParser` to get a matching result in the browser and `linkedom`.

Currently `parseHTML` is only used for HTML content bodies in events. Events with replies have content bodies that look like `<mx-reply>Hello</mx-reply> What's up` so they're parsed as HTML to strip out the `<mx-reply>` part.

Before | After
---  |  ---
![](https://user-images.githubusercontent.com/558581/153692011-2f0e7114-fcb4-481f-b217-49f461b1740a.png) | ![](https://user-images.githubusercontent.com/558581/153692016-52582fdb-abd9-439d-9dce-3f04da6959db.png)

Before:
```js
// Browser (Chrome, Firefox)
new DOMParser().parseFromString(`<div>foo</div>`, "text/html").body.outerHTML;
// '<body><div>foo</div></body>'

// `linkedom` 
new DOMParser().parseFromString(`<div>foo</div>`, "text/html").body.outerHTML;
// '<body></body>'
```

After (consistent matching output):

```js
// Browser (Chrome, Firefox)
new DOMParser().parseFromString(`<!DOCTYPE html><html><body><div>foo</div></body></html>`, "text/html").body.outerHTML;
// '<body><div>foo</div></body>'

// `linkedom`
new DOMParser().parseFromString(`<!DOCTYPE html><html><body><div>foo</div></body></html>`, "text/html").body.outerHTML;
// '<body><div>foo</div></body>'
```

`linkedom` goal is to be close to the current DOM standard, but [not too close](https://github.com/WebReflection/linkedom#faq). Focused on the streamlined cases for server-side rendering (SSR).

Here is some context around getting `DOMParser` to interpret things better. The conclusion was to only support the explicit standard cases with a `<html><body></body></html>` specified instead of adding the magic HTML document creation and massaging that the browser does.

 - https://github.com/WebReflection/linkedom/issues/106
 - https://github.com/WebReflection/linkedom/pull/108

 ---

Part of https://github.com/vector-im/hydrogen-web/pull/653 to support server-side rendering Hydrogen for the [`matrix-public-archive`](https://github.com/matrix-org/matrix-public-archive) project.
2022-02-11 20:10:46 -06:00
Bruno Windels
75e2618f70
Merge pull request #664 from vector-im/bwindels/onlylogsummarykeys
dont log summary valued, as they can contain PII
2022-02-11 18:41:26 +01:00
Bruno Windels
9685ef4dd3 dont log summary valued, as they can contain PII 2022-02-11 18:39:37 +01:00
Bruno Windels
750f3cd8ff release v0.2.26 2022-02-11 17:20:54 +01:00
Bruno Windels
34ec0e2c82
Merge pull request #663 from vector-im/bwindels/fix-reply-not-loading
Fix replies from /context not loading in e2ee rooms
2022-02-11 17:20:06 +01:00
Bruno Windels
ea8f3e5a6a remove argument that is already bound in BaseRoom, making decryption fail 2022-02-11 17:14:56 +01:00
Bruno Windels
a184ad528f
Merge pull request #654 from vector-im/bwindels/create-room
Create rooms
2022-02-11 16:56:24 +01:00
Bruno Windels
57b1542688 use private topic field as public one got removed as not needed in view 2022-02-11 09:37:56 +01:00
Bruno Windels
175f869c83 fix lint 2022-02-10 20:07:27 +01:00
Bruno Windels
a442b4b009 Merge branch 'master' into bwindels/create-room 2022-02-10 20:05:40 +01:00
Bruno Windels
d65b25f084 also adjust m.direct if the room has already been replaced 2022-02-10 20:00:01 +01:00
Bruno Windels
2765f48a64 create user id array in m.direct if it doesn't exist already 2022-02-10 19:59:44 +01:00
Bruno Windels
d2008a336b fix lint errors 2022-02-10 19:54:47 +01:00
Bruno Windels
ff46d382ac adjust m.direct when creating a DM 2022-02-10 19:54:15 +01:00
Bruno Windels
3adb2c3254 fix ts errors 2022-02-10 16:44:40 +01:00
Bruno Windels
8526461d3c split up create code into separate files 2022-02-10 16:43:32 +01:00
Bruno Windels
15eecbb463 cleanup 2022-02-10 16:28:44 +01:00
Bruno Windels
30c8ea29b2 fix bug where the wrong left panel tile is removed when accepting invite
because when comparing a tile to itself it wasn't returned 0
2022-02-10 16:27:32 +01:00
Bruno Windels
b0d790543a push to navigation in SessionViewModel rather than RVO 2022-02-10 14:57:48 +01:00
Bruno Windels
2c1b29e637 remove logging 2022-02-10 14:39:41 +01:00
Bruno Windels
75bbde598d also consider rooms without a name and just you and the other a DM
as we don't process m.direct account data yet
2022-02-10 14:39:18 +01:00
Bruno Windels
955a6bd6f9 styling for button in member details to open DM 2022-02-10 14:38:12 +01:00
Bruno Windels
147810864f add support to set alias and federation flag in create room 2022-02-10 14:09:18 +01:00
Bruno Windels
4c0167ed74 don't show spinner in left panel when room creation fails 2022-02-10 11:19:43 +01:00
Bruno Windels
024a6c06aa handle offline error nicer 2022-02-10 11:11:15 +01:00
Bruno Windels
b5536830d0 improve RoomBeingCreatedView, allow removing the roombeingcreated 2022-02-10 11:07:29 +01:00
Bruno Windels
20493f9e87 cleanup 2022-02-10 11:07:13 +01:00
Bruno Windels
e8c20c28b2 allow passing label into LoadingView
also doesn't need to be a template view, as it doesn't have bindings
or event handlers
2022-02-10 11:06:44 +01:00
Bruno Windels
f12841b2d3 better error handling in RoomBeingCreated 2022-02-10 11:06:20 +01:00
Bruno Windels
d6d1af13d0 rename RoomBeingCreated.localId to id 2022-02-10 11:03:52 +01:00
Bruno Windels
bbb1683dbf fixup: login view styling 2022-02-10 09:40:42 +01:00
Bruno Windels
fed42f13ad textarea styling 2022-02-10 09:40:30 +01:00
Bruno Windels
5f6308e7c4 fix homeserver field style in login view 2022-02-10 09:40:19 +01:00
Bruno Windels
74f7879cb6 fix unrelated bug: invite sorting order wasn't stable in left panel
as the timestamp is the same when you receive the invite during your
first sync
2022-02-10 09:40:03 +01:00
Bruno Windels
5c085efc10 create room view and view model 2022-02-09 19:02:51 +01:00
Bruno Windels
a1e14c4eec rename to not have conflict between method name and instance of CreateRoomViewModel 2022-02-09 19:02:18 +01:00
Bruno Windels
4b1be30dc0 improve form-row classes so they can work with create room form 2022-02-09 19:01:35 +01:00
Bruno Windels
8523f6feaf setup navigation for create room form 2022-02-09 19:00:41 +01:00
Bruno Windels
83d2b58bad add avatar support to creating room 2022-02-09 19:00:00 +01:00
Bruno Windels
afe8e17a6f remove debugging code 2022-02-08 17:00:06 +01:00
Bruno Windels
743f2270e5 have a single tile view that supports all 3 view models 2022-02-08 16:22:44 +01:00
Bruno Windels
5325b0b466 cleanup logging 2022-02-08 14:58:29 +01:00
Bruno Windels
d7b024eac1 unrelated fix: encode user name in matrix.to link 2022-02-08 14:35:14 +01:00
Bruno Windels
45c8e3a793 mark room as DM based on synced state events,rather than just inviteData
as that does not work for rooms we create ourselves
2022-02-08 14:34:34 +01:00
Bruno Windels
e04463c143 WIP for finding DM room 2022-02-07 18:58:53 +01:00
Bruno Windels
26fa2a5d60 add option 2022-02-07 18:58:43 +01:00
Bruno Windels
e1fbd1242e WIP 4 2022-02-07 16:30:44 +01:00
Bruno Windels
b868734378 change sdk version 2022-02-07 11:05:28 +01:00
Bruno Windels
0bb3cfcfad WIP3 2022-02-04 17:49:10 +01:00
Bruno Windels
3ff39a9549
Merge pull request #661 from vector-im/sdk-additions
Export more code from SDK
2022-02-04 17:43:23 +01:00
Bruno Windels
94709fd316
Merge pull request #623 from vector-im/registration
Bootstrap enough registration functionality for embedded-hydrogen work
2022-02-04 17:43:02 +01:00
RMidhunSuresh
4a0db9f984 Add required exports 2022-02-04 18:28:17 +05:30
RMidhunSuresh
28931f4103 Use async/await 2022-02-04 17:48:42 +05:30
RMidhunSuresh
f7f32ac806 responseCodeReject may not exist 2022-02-04 17:39:52 +05:30
RMidhunSuresh
a163cee18d Remove dead imports 2022-02-04 17:25:30 +05:30
RMidhunSuresh
0828ac12b1 Fix params 2022-02-04 17:25:15 +05:30
RMidhunSuresh
b59f916824 Merge branch 'registration' of github.com:vector-im/hydrogen-web into registration 2022-02-04 17:16:32 +05:30
R Midhun Suresh
2ac63e78ca
mark method as internal
Co-authored-by: Bruno Windels <bruno@windels.cloud>
2022-02-04 17:16:15 +05:30
RMidhunSuresh
028b96e4c5 Let type also be undefined 2022-02-04 17:11:33 +05:30
RMidhunSuresh
22d5505a2b Create registration stage in Registration itself 2022-02-04 16:50:22 +05:30
RMidhunSuresh
e66549a067 Remove dead code 2022-02-04 16:40:49 +05:30
RMidhunSuresh
e8c480426a Remove error code 2022-02-04 16:37:43 +05:30
RMidhunSuresh
891375a885 Rename allowerErrors -> allowedStatusCodes 2022-02-04 16:35:47 +05:30
RMidhunSuresh
32af7e6f09 Make more changes
- make setter a method
- lazily create promise
2022-02-04 16:23:39 +05:30
Bruno Windels
0b04612d6c WIP2 2022-02-04 11:16:58 +01:00
RMidhunSuresh
3d8b9cce41 Fix responseCode in Request 2022-02-04 15:41:37 +05:30
Bruno Windels
bc09ede09f WIP 2022-02-03 17:57:35 +01:00
RMidhunSuresh
b6e1d4a7d5 Implement responseCode() 2022-02-03 19:41:14 +05:30
RMidhunSuresh
89a97537b0 Make methods private + some props readonly 2022-02-03 19:41:14 +05:30
RMidhunSuresh
8a3c0afba6 Fix incorrect types 2022-02-03 19:41:11 +05:30
RMidhunSuresh
0ad0ecfcc2 Check response code instead of existence of props 2022-02-03 19:40:25 +05:30
RMidhunSuresh
c4894f2c24 completed is not always present 2022-02-03 19:40:25 +05:30
RMidhunSuresh
e64f4ad7b2 Refactor code
- Move all code that does /register to Registration.ts
- RegistrationStage only deals with the generation of auth data
- Change API so that undefined is returned instead of string when
  registration is over
2022-02-03 19:40:25 +05:30
R Midhun Suresh
2aad5546bf No need for Object.assign here either
Co-authored-by: Bruno Windels <brunow@matrix.org>
2022-02-03 19:40:25 +05:30
RMidhunSuresh
7bacbec5e9 Remove type directory 2022-02-03 19:40:25 +05:30
RMidhunSuresh
e13040a49e Don't mutate flows 2022-02-03 19:40:25 +05:30
R Midhun Suresh
30cb9f6d15 Use includes instead of elaborate find
Co-authored-by: Bruno Windels <brunow@matrix.org>
2022-02-03 19:40:25 +05:30
RMidhunSuresh
a351a185a0 Give proper names 2022-02-03 19:40:25 +05:30
RMidhunSuresh
fe0add01ee Use union of types for RegistrationResponse 2022-02-03 19:40:25 +05:30
RMidhunSuresh
a249a1b2b5 Implement flow seclector 2022-02-03 19:40:25 +05:30
RMidhunSuresh
6798a5e429 Move types to types.ts 2022-02-03 19:40:25 +05:30
RMidhunSuresh
3a67da8830 Refactor type
- Change name
- Move union type down
2022-02-03 19:40:25 +05:30
RMidhunSuresh
1d4b079d0c Type RegistrationResponse 2022-02-03 19:40:25 +05:30
RMidhunSuresh
49ade61ef6 Fill in ts types + change names 2022-02-03 19:40:25 +05:30
RMidhunSuresh
b482d478b4 Add a tos getter 2022-02-03 19:40:25 +05:30
RMidhunSuresh
ac7108b882 Throw error instead of returning it 2022-02-03 19:40:25 +05:30
RMidhunSuresh
7bb7189c6a No need for this export 2022-02-03 19:40:25 +05:30
RMidhunSuresh
6eba60bd75 Use typescript style that was agreed on earlier 2022-02-03 19:40:25 +05:30
RMidhunSuresh
5de1fc1453 Remove unnecessary getters 2022-02-03 19:40:25 +05:30
RMidhunSuresh
2f3865d8cc firstStage should be a local variable 2022-02-03 19:40:25 +05:30
RMidhunSuresh
2d4c106542 REFACTOR: Inline method 2022-02-03 19:40:25 +05:30
RMidhunSuresh
a91ba4370d Change type to show that username is optional 2022-02-03 19:40:25 +05:30
RMidhunSuresh
550a560f40 Remove space 2022-02-03 19:40:25 +05:30
RMidhunSuresh
5f11790f6b Object.assign is overkill here 2022-02-03 19:40:25 +05:30
RMidhunSuresh
e8dbbd876c Give default values to parameters 2022-02-03 19:40:25 +05:30
RMidhunSuresh
755f934eb2 No need to explicitly pass in inhibitLogin 2022-02-03 19:40:25 +05:30
RMidhunSuresh
5e93e048ab Don't cache GET requests 2022-02-03 19:40:25 +05:30
RMidhunSuresh
bb6a885116 Specify what errors are ignored in options 2022-02-03 19:40:25 +05:30
RMidhunSuresh
420c12f202 Copy over username only if it exists 2022-02-03 19:40:25 +05:30
RMidhunSuresh
792d5c62c5 Return username when registration is completed 2022-02-03 19:40:25 +05:30
RMidhunSuresh
fa2e2bc8f3 Allow register without providing username 2022-02-03 19:40:25 +05:30
RMidhunSuresh
170d7a5e55 Add startRegistration method 2022-02-03 19:40:25 +05:30
RMidhunSuresh
8ab8726b8f Implement m.login.terms stage 2022-02-03 19:40:25 +05:30
RMidhunSuresh
18e2fc1089 Pass in params to BaseRegistrationStage 2022-02-03 19:40:25 +05:30
RMidhunSuresh
a59b67ec45 Fix errors 2022-02-03 19:40:25 +05:30
RMidhunSuresh
d76a059525 Temporary fix for 401 errors 2022-02-03 19:40:25 +05:30
RMidhunSuresh
d28ab919bb Implement dummy registration logic 2022-02-03 19:40:25 +05:30
RMidhunSuresh
eb146830ba Implement registration endpoint 2022-02-03 19:40:25 +05:30
RMidhunSuresh
618d02d838 fetch registration flows 2022-02-03 19:40:25 +05:30
Bruno Windels
348de312f9 draft code in matrix layer to create room 2022-02-02 10:19:49 +01:00
Bruno Windels
65dcf8bc36 release v0.2.25 2022-02-01 12:34:42 +01:00
Bruno Windels
2e3616e05d call cursor.update during backup field migration, needs new version 2022-02-01 12:31:10 +01:00
Bruno Windels
00c5e747d2 log total backed up keys during flush operation 2022-02-01 12:30:45 +01:00
Bruno Windels
b29ecd339d add more logging to backup storage migration 2022-02-01 12:18:28 +01:00
Bruno Windels
c6820eccab release v0.2.24 2022-02-01 11:58:47 +01:00
Bruno Windels
247d13f97a
Merge pull request #651 from vector-im/bwindels/write-session-backup
Session backup writing
2022-02-01 11:54:53 +01:00
Bruno Windels
f4fa013ebc mark as not configured yet when re-enabling key backup 2022-02-01 11:32:53 +01:00
Bruno Windels
f4bb420f35 mark key backup properly as disabled 2022-02-01 11:27:42 +01:00
Bruno Windels
02f06724d0 don't block reenabling 4s if already enabled 2022-02-01 11:26:00 +01:00
Bruno Windels
fd4eb6b50d distinguish between "waiting to go online" vs "backup not configured" 2022-02-01 11:08:13 +01:00
Bruno Windels
997666164c remove unused enum variants 2022-01-31 17:37:44 +01:00
Bruno Windels
9c599d53aa allow to inject max delay in key backup 2022-01-31 17:31:01 +01:00
Bruno Windels
62acd458c6 also ask for new key if backup version is not found 2022-01-31 17:30:51 +01:00
Bruno Windels
17275a5390 backup 200 keys per request 2022-01-31 17:30:15 +01:00
Bruno Windels
830786b2fd fixes and cleanup 2022-01-31 16:26:14 +01:00
Bruno Windels
06a1421e97 add backupWriteStatus so binding can take multiple fields into account 2022-01-31 16:26:06 +01:00
Bruno Windels
6541aacf98 don't discount already finished keys in total for previous iterations 2022-01-31 16:23:48 +01:00
Bruno Windels
dacaa86386 fix percentage calculation 2022-01-31 16:22:22 +01:00
Bruno Windels
a757fb3696 better error handling in key backup, cleanup and not overuse observables 2022-01-31 14:37:05 +01:00
Bruno Windels
7eb0d347f5 flush key backup after coming online 2022-01-31 14:36:35 +01:00
Bruno Windels
ae5cc17290 mark all inbound sessions to be backed up again when changing version 2022-01-31 14:36:04 +01:00
Bruno Windels
d9e6164a5c fix ts errors 2022-01-28 16:40:32 +01:00
Bruno Windels
a97d235cf5 flush after enabling key backup 2022-01-28 16:36:42 +01:00
Bruno Windels
c9b5ce6508 clean up key backup vm using flatMap to avoid subscription handling 2022-01-28 16:36:13 +01:00
Bruno Windels
e0df003aba add flatMap operator on observable value 2022-01-28 16:35:49 +01:00
Bruno Windels
c340746a87 also remove text nodes when updating message body
fixes #649
2022-01-28 16:04:56 +01:00
Bruno Windels
eabd303c8e count on the index if we're using one, don't always take the store 2022-01-28 15:14:58 +01:00
Bruno Windels
bd2c70b923 adapt key backup view(model) to changes in session, show backup progress 2022-01-28 15:14:23 +01:00
Bruno Windels
504f420293 make keyBackup an observable and don't have separate needs-key flag 2022-01-28 15:13:58 +01:00
Bruno Windels
eb134a6c47 only take into account non-backed up keys for counting 2022-01-28 13:18:03 +01:00
Bruno Windels
7d3e3b992b some more typing 2022-01-28 13:14:38 +01:00
Bruno Windels
c47bdd5715 flush key backup when creating a new room key 2022-01-28 13:14:11 +01:00
Bruno Windels
b692b3ec4f move key backup operation and flush bookkeeping inside KeyBackup
so we can flush from other places than Session
2022-01-28 13:13:23 +01:00
Bruno Windels
ebc7f1ecd7 needs to be awaited 2022-01-28 13:11:52 +01:00
Bruno Windels
b30db544a3 use idb key range to select non-backed up keys 2022-01-28 13:11:32 +01:00
Bruno Windels
a499689bd8 also write room key that we create ourselves with RoomKey infrastructure
so all keys are written in one place and the flags are always correct
2022-01-28 13:10:48 +01:00
Bruno Windels
c81dde53e7 store key source in inbound session 2022-01-28 10:03:30 +01:00
Bruno Windels
dd2b41ff95 use backup flag in key backup rather than separate store 2022-01-27 16:07:18 +01:00
Bruno Windels
48e72f9b69 replace SessionsNeedingBackup store with backup field on inbound session 2022-01-27 16:00:46 +01:00
Bruno Windels
6f1484005b stop key backup when on the wrong version
users can then enter the new key in the settings to start backing up
again
2022-01-27 15:14:29 +01:00
Bruno Windels
0b4954a9ca log key backup upload requests 2022-01-27 14:20:04 +01:00
Bruno Windels
bf08c0d850 deal with errors when enabling key backup
fixes #449
2022-01-27 14:19:37 +01:00
Bruno Windels
e80acd4d57 add migration when backup is enabled 2022-01-26 16:30:40 +01:00
Bruno Windels
60ed276b8a add progress notification and cancellation to key backup flush 2022-01-26 15:19:31 +01:00
Bruno Windels
554aa45d48 add support for progress notifications in abortable operation 2022-01-26 15:18:23 +01:00
Bruno Windels
524090e27d support idb store/index.count 2022-01-26 15:12:11 +01:00
Bruno Windels
a791641b34 move types to separate file 2022-01-26 12:10:20 +01:00
Bruno Windels
85155a43bb cleanup types 2022-01-26 10:17:31 +01:00
Bruno Windels
cfb94206f9 move curve25519 code to separate file 2022-01-26 10:13:01 +01:00
Bruno Windels
86caa5f9b1 rename session backup to key backup to be consistent with RoomKey 2022-01-26 09:51:48 +01:00
Bruno Windels
933a1b4636 draft of session backup writing + some refactoring 2022-01-25 18:48:19 +01:00
Bruno Windels
ffece4f357 move some validation of into session backup 2022-01-25 18:48:03 +01:00
Bruno Windels
8f4e3c62ce add hs endpoint for backup keys upload 2022-01-25 18:47:42 +01:00
Bruno Windels
290aaad63a add sessionsNeedingBackup store 2022-01-25 18:47:27 +01:00
Bruno Windels
a3e294bb60 small cleanup 2022-01-25 18:45:39 +01:00
Bruno Windels
5d87d8bde3 change store.get return type when no value is found to undefined
IDBRequest.result is undefined according to the official TS type decls.
2022-01-25 18:43:44 +01:00
Bruno Windels
993a86ddb2 convert SessionBackup to typescript and pass in keyloader 2022-01-20 11:16:08 +01:00
Bruno Windels
a4d924acd1 make KeyLoader use proper olm types 2022-01-20 11:15:48 +01:00
Bruno Windels
30438846e9
Merge pull request #645 from vector-im/bwindels/fix-mobile-multiline
remove enterkeyhint attribute as it prevents entering newlines on android
2022-01-18 10:39:35 +01:00
Bruno Windels
e6fee75952 remove enterkeyhint attribute as it prevents entering newlines on android
on Android, by default (without the above attribute set to "send"), you
press enter twice to submit a field. The first time, enter, Android
seems to prevent sending logic by setting the key property on the event
to "Unidentified", but does insert a newline. The second consecutive enter,
it will be set to "Enter" and we'll send.

Having enterkeyhint to send will disable all of that. So we're going with
the default behaviour, which, IIRC, was a bit annoying on iOS as well.
2022-01-18 09:42:01 +01:00
Bruno Windels
acc9167991
Merge pull request #644 from vector-im/bwindels/fix-images-in-replies
fix images not loading in replies
2022-01-17 16:51:09 +01:00
Bruno Windels
b0e8506cb5 ensure images load in reply preview in timeline 2022-01-17 16:48:36 +01:00
Bruno Windels
f379bf2341 ensure images load in reply preview in composer 2022-01-17 16:48:17 +01:00
Bruno Windels
454d2d3666
Merge pull request #643 from vector-im/bwindels/separate-logout-view
Show logout in separate view so it's clear something is happening
2022-01-17 16:40:49 +01:00
Bruno Windels
57bf730241 mention it's better to not close the app 2022-01-17 16:33:57 +01:00
Bruno Windels
4bc421527f also add extra classes to legacy spinner 2022-01-17 16:31:13 +01:00
Bruno Windels
05d23cc745 hook up logout view 2022-01-17 16:31:02 +01:00
Bruno Windels
4c5b884af7 create and hook up logout viewmodel, on /logout/<id> path 2022-01-17 16:30:22 +01:00
Bruno Windels
c6c1d3b3d8 refactor logout in client so we don't need a fully loaded session
instead, we pass the session id in
this will make it easier to first dispose the client when leaving the
/session/<id> and just creating a client without fully loading it
to log out. This way sync is already not running anymore.
2022-01-17 16:29:01 +01:00
Bruno Windels
164d72830f create subclass for inline template views (e.g. without sub classing) 2022-01-17 16:25:48 +01:00
Bruno Windels
c10435e242
Merge pull request #642 from vector-im/update-node-faq
Update node version in FAQ
2022-01-17 09:18:07 +01:00
RMidhunSuresh
2dc9b63051 Update node version in FAQ 2022-01-17 12:49:55 +05:30
Bruno Windels
d673c8714e release v0.2.23 2022-01-14 19:19:18 +01:00
Bruno Windels
412db33c36 click here labels are so nineties 2022-01-14 19:18:12 +01:00
Bruno Windels
000c8b27c3
Merge pull request #637 from vector-im/bwindels/timeline-readme
add basic readme for updates in the timeline
2022-01-14 19:16:25 +01:00
Bruno Windels
46c61953f6
Merge pull request #612 from vector-im/threading-fallback-reply
Threading fallback - PR 2 - Support rich reply
2022-01-14 19:10:39 +01:00
Bruno Windels
a8a8355ea4 fix unit test 2022-01-14 19:05:53 +01:00
Bruno Windels
3d00881508 don't look in remoteEntries when already found 2022-01-14 19:05:30 +01:00
Bruno Windels
7197e5427f don't emit an update when the context entry is loaded sync
also load context entries in parallel
2022-01-14 18:16:52 +01:00
Bruno Windels
3243ce2a90 fix unit test that failed after it finished
crashing the runner on node 16
2022-01-14 18:15:46 +01:00
Bruno Windels
65929194b0 fix lint warnings 2022-01-14 16:23:55 +01:00
Bruno Windels
184a16a194 also define param 2022-01-14 16:23:12 +01:00
Bruno Windels
8201a85c47 ensure these have a fn for tilesCreator 2022-01-14 16:20:38 +01:00
Bruno Windels
2321228981 use this._entry here (once updated by super.updateEntry) 2022-01-14 16:20:14 +01:00
Bruno Windels
5f99c2360c also try to create replyTile from ctor just in case update doesn't come 2022-01-14 16:12:43 +01:00
Bruno Windels
ad335d5088 pass in tilesCreator everywhere, although not needed right now 2022-01-14 16:06:29 +01:00
Bruno Windels
1ea4a347e2 encode url components 2022-01-14 15:53:17 +01:00
Bruno Windels
b578f4ac84 actually add LocationView 2022-01-14 15:50:19 +01:00
Bruno Windels
052ff02571 move TileView type too so we don't have to repeat imports 2022-01-14 15:47:22 +01:00
Bruno Windels
3c59004e72 Merge branch 'master' into threading-fallback-reply 2022-01-14 15:43:24 +01:00
Bruno Windels
17ebc8a066
Merge pull request #611 from vector-im/threading-fallback-relation
Threading fallback - PR 1 - Link events with their related event
2022-01-14 15:35:27 +01:00
Bruno Windels
9220b6675b
Merge pull request #641 from vector-im/bwindels/location-tile
add location tile view so we don't throw when a location is shared
2022-01-14 15:30:39 +01:00
Bruno Windels
18a76025c7 add location tile view so we don't throw when a location is shared 2022-01-14 15:27:46 +01:00
RMidhunSuresh
dac2d5e685 Pass everything down into updateEntry 2022-01-14 19:26:23 +05:30
RMidhunSuresh
0af9f10166 don't store tilesCreator 2022-01-14 19:11:40 +05:30
RMidhunSuresh
d18f4d341c store replyFlags on this 2022-01-14 18:31:22 +05:30
Bruno Windels
b5a1c419ca
Merge pull request #640 from vector-im/bwindels/lazyloadimageswhenpartiallyinview
load image in timeline from when it is partially visible
2022-01-14 13:59:57 +01:00
Bruno Windels
1f9be978b7 load image in timeline from when it is partially visible 2022-01-14 13:57:11 +01:00
RMidhunSuresh
41fffdf155 Remove even more stray new lines 2022-01-14 18:17:49 +05:30
RMidhunSuresh
51215fda16 Rename tileCreator -> tilesCreator 2022-01-14 18:17:49 +05:30
RMidhunSuresh
d639e169ec Move tileCreator to BaseMessageTile 2022-01-14 18:17:49 +05:30
RMidhunSuresh
e1b9b1161d Split ifs and remove ?. abuse 2022-01-14 18:17:49 +05:30
RMidhunSuresh
846e637716 Remove stray newline 2022-01-14 18:17:49 +05:30
RMidhunSuresh
58dd25b58d track reply-tile 2022-01-14 18:17:49 +05:30
RMidhunSuresh
a77b9d9027 Move update logic to BaseMessageTile 2022-01-14 18:17:49 +05:30
RMidhunSuresh
ef5a377bc6 Hide reply option on pending tile 2022-01-14 18:17:49 +05:30
RMidhunSuresh
951af49e04 Emit change on reply tile 2022-01-14 18:17:49 +05:30
RMidhunSuresh
455b747a1c Don't check param for reply 2022-01-14 18:17:49 +05:30
RMidhunSuresh
28a534ee49 Fix reply nesting 2022-01-14 18:17:49 +05:30
RMidhunSuresh
f9f7f6cc6f Fix test 2022-01-14 18:17:49 +05:30
RMidhunSuresh
7f91653208 Rename replyTextTile -> replyTile 2022-01-14 18:17:49 +05:30
RMidhunSuresh
086e0c0320 Inline methods 2022-01-14 18:17:49 +05:30
RMidhunSuresh
273c44424f Throw if viewClass returns undefined 2022-01-14 18:17:49 +05:30
RMidhunSuresh
b134fa7409 Format swtich case properly 2022-01-14 18:17:49 +05:30
RMidhunSuresh
fee6447e22 Don't call render() 2022-01-14 18:17:49 +05:30
RMidhunSuresh
e99cd41ed0 Change check 2022-01-14 18:17:49 +05:30
RMidhunSuresh
af5a008d0f Move links to vm 2022-01-14 18:17:49 +05:30
RMidhunSuresh
27a9f5dd02 Use DOMPurify to remove mx-reply 2022-01-14 18:17:49 +05:30
RMidhunSuresh
cfefe6962a Remove stray space 2022-01-14 18:17:49 +05:30
RMidhunSuresh
88f9ad09a2 Move method as local function 2022-01-14 18:17:49 +05:30
RMidhunSuresh
0ae3c60d6d Remove .js file from rebase 2022-01-14 18:17:49 +05:30
RMidhunSuresh
c34d574385 No need to export renderPart 2022-01-14 18:17:49 +05:30
RMidhunSuresh
2a124d4195 simplify css 2022-01-14 18:17:49 +05:30
RMidhunSuresh
e352867f5a Remove unnecessary ctor 2022-01-14 18:17:49 +05:30
RMidhunSuresh
f645065db7 Remove unused getter 2022-01-14 18:17:49 +05:30
RMidhunSuresh
d69059de68 Use different flag 2022-01-14 18:17:49 +05:30
RMidhunSuresh
0c3f16e5f6 Use 's' flag with regex if available 2022-01-14 18:17:49 +05:30
RMidhunSuresh
cba044eff1 Remove comment 2022-01-14 18:17:49 +05:30
RMidhunSuresh
dee22f7120 Implement render flags 2022-01-14 18:17:49 +05:30
RMidhunSuresh
46b69b3873 Render error 2022-01-14 18:17:49 +05:30
RMidhunSuresh
687aa5a7e3 Remove dead code 2022-01-14 18:17:49 +05:30
RMidhunSuresh
4df3654166 Prevent reply previews from being nested 2022-01-14 18:17:49 +05:30
RMidhunSuresh
4d63b41127 Make reply preview flush left 2022-01-14 18:17:49 +05:30
RMidhunSuresh
1b9f970d7f WIP: Render the whole view instead of messageBody 2022-01-14 18:17:49 +05:30
RMidhunSuresh
7f1b3e25e8 Use t instead of tag 2022-01-14 18:17:49 +05:30
RMidhunSuresh
f01d5d95d9 Reuse code from timeline view 2022-01-14 18:17:49 +05:30
RMidhunSuresh
89d6968139 Show decryption error as well 2022-01-14 18:17:49 +05:30
RMidhunSuresh
2773642406 No need to handle redaction specially 2022-01-14 18:17:49 +05:30
RMidhunSuresh
13cba84445 Remove mapSideEffect 2022-01-14 18:17:49 +05:30
RMidhunSuresh
bb45d0eae9 Render non-text messages as well 2022-01-14 18:17:49 +05:30
RMidhunSuresh
df22db256b No need to pass tileCreator as argument 2022-01-14 18:17:49 +05:30
RMidhunSuresh
e0dc853d74 Fill matrix.to links 2022-01-14 18:17:49 +05:30
RMidhunSuresh
91912bdb8d Create tile using tileCreator 2022-01-14 18:17:49 +05:30
RMidhunSuresh
54004eef4d Integrate into update mechanism 2022-01-14 18:17:49 +05:30
RMidhunSuresh
aa3bb9c6ef Remove allowReplies 2022-01-14 18:17:49 +05:30
RMidhunSuresh
73c5562fd3 Remove code from BaseTextTile 2022-01-14 18:17:49 +05:30
RMidhunSuresh
4a12acf157 Improve error code 2022-01-14 18:17:49 +05:30
RMidhunSuresh
67da746b48 Render error 2022-01-14 18:17:49 +05:30
RMidhunSuresh
545aae31d9 WIP 2022-01-14 18:17:49 +05:30
RMidhunSuresh
3aa29cfc65 Do not remove reply preview 2022-01-14 18:17:49 +05:30
RMidhunSuresh
99f4eb6843 Minimize manual dom manipulation where possible 2022-01-14 18:17:49 +05:30
RMidhunSuresh
61f4d0719f Refactor code 2022-01-14 18:17:49 +05:30
RMidhunSuresh
d6233e7c77 Render static avatar 2022-01-14 18:17:49 +05:30
RMidhunSuresh
540aa6c546 Use contextEntry and pass avatarUrl 2022-01-14 18:17:49 +05:30
RMidhunSuresh
31573b3599 Render reply 2022-01-14 18:17:49 +05:30
RMidhunSuresh
e88ee31991 Add getter for reply body 2022-01-14 18:17:49 +05:30
RMidhunSuresh
f6cf3b378b Strip reply fallback 2022-01-14 18:17:49 +05:30
RMidhunSuresh
35a13842af Implement context endpoint 2022-01-14 18:17:49 +05:30
RMidhunSuresh
65f957f023 WIP 2022-01-14 18:17:49 +05:30
R Midhun Suresh
4fb0a84d0a
Return property from super
Co-authored-by: Bruno Windels <brunow@matrix.org>
2022-01-14 18:16:38 +05:30
RMidhunSuresh
30b8e5b5ea use withReply 2022-01-14 18:15:26 +05:30
RMidhunSuresh
8cd430ac07 Improve test logic 2022-01-14 17:48:25 +05:30
RMidhunSuresh
75012eda9c Fix tests 2022-01-14 17:28:31 +05:30
RMidhunSuresh
e9a49fdf74 Use hsApi mock 2022-01-14 17:07:06 +05:30
RMidhunSuresh
315acf2fbc Remove dead code from test 2022-01-14 16:54:16 +05:30
RMidhunSuresh
310790c84e Use mock storage 2022-01-14 16:51:06 +05:30
RMidhunSuresh
277638b107 Override methods in NonPersistedEventEntry
This will prevent redactions to entries fetched from hs showing "message
is being redacted" and will instead show "message is redacted"
2022-01-14 16:15:16 +05:30
RMidhunSuresh
b238357c53 Use emitUpdateForEntry 2022-01-14 16:14:42 +05:30
RMidhunSuresh
4fa32bac2f check only in remoteEntries 2022-01-14 16:14:06 +05:30
Bruno Windels
58f2192a7e add basic readme for updates in the timeline 2022-01-14 11:13:21 +01:00
RMidhunSuresh
3c28ee1adf Remove unused getter 2022-01-13 21:05:18 +05:30
RMidhunSuresh
2c4610c132 add param to emitUpdateForEntry 2022-01-13 19:20:37 +05:30
RMidhunSuresh
239d16747d Clean test code; try not to peek into internals 2022-01-13 19:14:28 +05:30
RMidhunSuresh
764541d3ca Remove unused method 2022-01-13 18:32:18 +05:30
RMidhunSuresh
ca1831fef6 update contextForEntries 2022-01-13 14:38:05 +05:30
Bruno Windels
1ed8d48ced release SDK 0.0.4 2022-01-12 18:39:13 +01:00
Bruno Windels
48e6bba100
Merge pull request #634 from vector-im/bwindels/fix-sdk-build2
Adjust SDK to not do asset imports anymore in file provided from SDK
2022-01-12 18:37:28 +01:00
Bruno Windels
8c1596d869 update SDK docs to not use paths/vite anymore 2022-01-12 18:32:15 +01:00
Bruno Windels
93eca757d3 dont add paths/vite to sdk output, as it does not work 2022-01-12 18:31:55 +01:00
Bruno Windels
3f60ef8da7 release sdk version 0.0.3 2022-01-12 17:51:48 +01:00
Bruno Windels
5d15fce343
Merge pull request #633 from vector-im/bwindels/fix-sdk-build1
Attempt to fix SDK build and dev server errors for consuming app
2022-01-12 17:48:08 +01:00
Bruno Windels
f526098293 also remove ts types, as we get errors for the untyped files
that don't exist
2022-01-12 17:41:00 +01:00
Bruno Windels
d7290bf750 remove exports field to try and prevent vite bug resolving asset url
downside is that we can't export cjs version anymore
2022-01-12 17:14:52 +01:00
RMidhunSuresh
2f4c0623d0 Restore earlier name 2022-01-12 19:20:32 +05:30
RMidhunSuresh
ed88184757 Remove statement 2022-01-12 19:14:38 +05:30
RMidhunSuresh
d0f7570f5e Fix tests 2022-01-12 18:44:17 +05:30
Bruno Windels
a5eb386f48
Merge pull request #632 from vector-im/bwindels/move-bs58-to-dev-deps
put bs58 in devDeps as we bundle it in the sdk
2022-01-12 10:13:31 +01:00
Bruno Windels
b76f97be93 put bs58 in devDeps as we bundle it in the sdk 2022-01-12 10:11:04 +01:00
RMidhunSuresh
acafae7d3a Implement offline support for context entries 2022-01-11 20:58:27 +05:30
RMidhunSuresh
a59bf7c002 Fix looking in allEntries 2022-01-11 20:57:29 +05:30
RMidhunSuresh
5c1813888c Check in all entries for context 2022-01-11 14:57:22 +05:30
RMidhunSuresh
73733ce145 Guard entry from storage being processed by method 2022-01-11 14:49:59 +05:30
RMidhunSuresh
bf6dfcfcad update comment 2022-01-11 13:28:35 +05:30
RMidhunSuresh
f605608098 getTrackedEntry -> findLoadedEventById 2022-01-11 13:20:42 +05:30
RMidhunSuresh
31a8227e53 stylistic change 2022-01-11 13:14:13 +05:30
RMidhunSuresh
62dcb61536 Rename updateEntry -> emitUpdateForEntry 2022-01-11 13:11:50 +05:30
RMidhunSuresh
fda211e7b3 Remove dead code 2022-01-11 13:10:40 +05:30
RMidhunSuresh
63b6564f70 Pass prop change 2022-01-11 11:54:41 +05:30
RMidhunSuresh
93bbeee400 Don't pass relatedEntry in param 2022-01-11 11:49:06 +05:30
RMidhunSuresh
66fa8d84a7 Make setAsContextOf private 2022-01-10 18:51:12 +05:30
RMidhunSuresh
091b55a265 Rename method and add comment 2022-01-10 18:05:33 +05:30
RMidhunSuresh
ec8f6e8e0a use addLocalRelation 2022-01-10 12:58:45 +05:30
RMidhunSuresh
7ad73bb453 Move check down 2022-01-07 19:56:31 +05:30
RMidhunSuresh
3fecce6fe6 Fix tests 2022-01-07 19:39:51 +05:30
RMidhunSuresh
9d161a0bcf Refactor + put redaction in NonPersistedEventEntry 2022-01-07 19:38:57 +05:30
RMidhunSuresh
8cc04e4c25 Keep calls internal to class 2022-01-07 17:50:36 +05:30
RMidhunSuresh
0a09a50ab9 Move line into if 2022-01-07 17:29:17 +05:30
RMidhunSuresh
c6484f1eac Replace entry in contextEntryNotInTimeline 2022-01-07 17:11:42 +05:30
Bruno Windels
68214156d9
Merge pull request #608 from vector-im/ts-migration-doc
Clarify approach to type data objects in doc
2022-01-06 14:39:06 +01:00
Bruno Windels
314843f5f2
Merge pull request #624 from vector-im/build-fix
Add hash-bang to fix sdk build error
2022-01-06 13:22:10 +01:00
RMidhunSuresh
cfbb6d4250 Add explaining comment 2022-01-06 15:37:58 +05:30
RMidhunSuresh
7adce08eee add more jsdoc comments 2022-01-06 15:33:00 +05:30
RMidhunSuresh
f76217dcce Change method name 2022-01-06 15:14:13 +05:30
RMidhunSuresh
a2ab36480f Add jsdoc comment 2022-01-06 15:02:44 +05:30
RMidhunSuresh
90c9018aa4 Update comment 2022-01-06 12:07:10 +05:30
RMidhunSuresh
595deb3a3d Also copy over contextEntry from otherEntry 2022-01-06 12:07:10 +05:30
RMidhunSuresh
78f97c6532 Remove await from tests 2022-01-06 12:07:10 +05:30
RMidhunSuresh
9f1764c325 Update comment 2022-01-06 12:07:10 +05:30
RMidhunSuresh
4418700589 Add test for move code 2022-01-06 12:07:10 +05:30
RMidhunSuresh
d2c7eec8e0 No need to delete before update on map 2022-01-06 12:07:10 +05:30
RMidhunSuresh
41cf6460d0 Remove dead code 2022-01-06 12:07:10 +05:30
RMidhunSuresh
8ec75ce4bb Rename methods 2022-01-06 12:07:10 +05:30
RMidhunSuresh
a060d54468 Make tests pass 2022-01-06 12:07:10 +05:30
RMidhunSuresh
3fe824dbd1 Propagate updates 2022-01-06 12:07:10 +05:30
RMidhunSuresh
7ef79c92f5 Remove entry from map 2022-01-06 12:07:10 +05:30
RMidhunSuresh
2d5bb82077 Fix bug 2022-01-06 12:07:10 +05:30
RMidhunSuresh
6f8001bd82 Add tests 2022-01-06 12:07:10 +05:30
RMidhunSuresh
640a3fb9fa Check if contextEvent was found 2022-01-06 12:07:10 +05:30
RMidhunSuresh
05d2defa2d Rename fetchedEntries --> contextEntriesNotInTimeline 2022-01-06 12:07:10 +05:30
RMidhunSuresh
c3bef6d4d2 Rename dependents --> contextForEntries 2022-01-06 12:07:10 +05:30
RMidhunSuresh
d1818d2a57 Reuse code in getOrLoadEntry 2022-01-06 12:07:10 +05:30
RMidhunSuresh
f5fadf700e Move event to remoteEntries if needed 2022-01-06 12:07:10 +05:30
RMidhunSuresh
d924dbb723 Add explaining comment 2022-01-06 12:07:10 +05:30
RMidhunSuresh
544dca3b18 Use _updateEntry 2022-01-06 12:07:10 +05:30
RMidhunSuresh
39f68e8c2f Refactor out magic string 2022-01-06 12:07:10 +05:30
RMidhunSuresh
5c0bbdd4c8 Move methods into Timeline 2022-01-06 12:07:10 +05:30
RMidhunSuresh
51b7b21082 Implement readById() in TimelineReader 2022-01-06 12:07:10 +05:30
RMidhunSuresh
0da94e51e0 Use map and fetch from Map if available 2022-01-06 12:07:10 +05:30
RMidhunSuresh
4a6293dcdc Made code more readable 2022-01-06 12:07:10 +05:30
RMidhunSuresh
287212956b findAndUpdate instead of update 2022-01-06 12:07:10 +05:30
RMidhunSuresh
7a91dd9595 Improve comment 2022-01-06 12:07:10 +05:30
RMidhunSuresh
4a81e06e96 Track fetched entries for redactions 2022-01-06 12:07:10 +05:30
RMidhunSuresh
ea89c272b9 Support redaction changes in remoteEntries 2022-01-06 12:07:10 +05:30
RMidhunSuresh
c690de9f7b Support decryption on entries fetched from hs 2022-01-06 12:07:10 +05:30
RMidhunSuresh
7cc3d4b91a Emit updated entries 2022-01-06 12:07:10 +05:30
RMidhunSuresh
053dcf39a5 Use NonPersistedEventEntry 2022-01-06 12:07:10 +05:30
RMidhunSuresh
d191b327c6 Change comment 2022-01-06 12:07:10 +05:30
RMidhunSuresh
06864a65b7 Add contextEventId 2022-01-06 12:07:10 +05:30
RMidhunSuresh
764e38f8c9 Use 'context' instead of 'related' 2022-01-06 12:07:10 +05:30
RMidhunSuresh
696980aca4 Parse display name and avatar of event 2022-01-06 12:07:10 +05:30
RMidhunSuresh
0c42f53a2f Implement context endpoint 2022-01-06 12:07:06 +05:30
RMidhunSuresh
e901142661 await on loading related events 2022-01-06 11:59:58 +05:30
RMidhunSuresh
2265d198a6 Formatting fix 2022-01-06 11:59:58 +05:30
RMidhunSuresh
b753507b8d WIP 2022-01-06 11:59:58 +05:30
Bruno Windels
196e3726cb
Merge pull request #630 from vector-im/bwindels/otk-count-cleanup
Missing OTK count in sync doesn't mean 0, but rather no change
2022-01-05 14:29:51 +01:00
Bruno Windels
c9d11d6f19 missing otk count does not mean 0 but rather no change 2022-01-05 14:26:15 +01:00
Bruno Windels
aabfbf507e typo in comments 2022-01-05 14:25:42 +01:00
RMidhunSuresh
205de7e5c5 Add hash-bang to fix build error 2021-12-27 15:51:25 +05:30
Bruno Windels
908f9a7ce3 try to export stylesheet 2021-12-22 18:04:30 +01:00
Bruno Windels
203a5fd88c
Merge pull request #622 from vector-im/bwindels/sdk-refactoring
Some API cleanup ahead of first SDK release
2021-12-22 17:58:20 +01:00
Bruno Windels
13e77636a9 export paths from vite.js as required by Platform, reorder ctor params
make it easier for SDK users
2021-12-22 17:48:08 +01:00
Bruno Windels
6247ced7b7 dont export, the ctor of these classes is not a public API 2021-12-22 17:24:58 +01:00
Bruno Windels
ba27d20b24 only pass platform into Client
simplifying the API for SDK
2021-12-22 17:20:37 +01:00
Bruno Windels
9238961992 cache olm and olm worker promise inside Platform
as prep to call them every time a Client is created
2021-12-22 17:19:10 +01:00
Bruno Windels
fe26f48c47 rename SessionContainer to Client 2021-12-22 17:09:52 +01:00
Bruno Windels
b5fe65d0cc
Merge pull request #617 from vector-im/bwindels/sdk-build
SDK build
2021-12-22 16:54:03 +01:00
Bruno Windels
24afe1e496 add licenses to readme of things we actually bundle
rather than just depend on
2021-12-22 16:45:08 +01:00
Bruno Windels
5f389e654a add description 2021-12-22 16:37:53 +01:00
Bruno Windels
c31215bc2a less logging during build 2021-12-22 16:31:19 +01:00
Bruno Windels
c3ff571af7 update SDK doc, use it as sdk package readme 2021-12-22 16:31:19 +01:00
Bruno Windels
441fa13bfd change sdk package name to hydrogen-view-sdk
as we might want to also have a lower level sdk later on
2021-12-22 16:31:19 +01:00
Bruno Windels
3bee4b4585 bundle bs58 to avoid pain of bundle transitive dependency for lib users
bs58 depends on safe-buffer, which depends on buffer, which is a bit
of a pain to bundle as it is a built-in node module. You'd typically
replace buffer with a browser polyfill in your build system but:
 a) this is somewhat a pain to setup for simple apps
 b) the polyfill is way more than we need (6kb), so we prefer to bundle
    our minimal buffer replacement that uses Uint8Array. Since it is
    a transitive dependency, we need to bundle bs58 and all of its
    transitive dependencies (2.5kb) as well, so if users of hydrogen-sdk
    also use any of these, they'll be double included in their bundle.
2021-12-22 16:31:19 +01:00
Bruno Windels
b48280905e include path/vite in sdk bundle 2021-12-22 16:31:19 +01:00
Bruno Windels
163dae647b move output of both lib and asset build around for coherent package 2021-12-22 16:31:19 +01:00
Bruno Windels
c921091957 run two vite builds for the sdk build, assets & js separately 2021-12-22 16:31:19 +01:00
Bruno Windels
6add3f1da3 WIP 2021-12-22 16:31:19 +01:00
Bruno Windels
ceb0b5793b somewhat works, but not everything we need
it's missing still:
 - non-css assets like the download sandbox and the olm worker aren't written for some reason
 - the es and cjs lib.js entry points end up in assets with a hash for some reason
 - in these entry files, apart from our exports, something is adding an import statement for every import that was found in the tree
 - all assets are hashed even though the config tries to disable that
 - tests are included
2021-12-22 16:31:18 +01:00
Bruno Windels
14b854ad4f make tsconfig file to build declaration files 2021-12-22 16:31:18 +01:00
Bruno Windels
df6000c706 basic sdk build config file for es and cjs 2021-12-22 16:31:18 +01:00
Bruno Windels
c11f0774eb move common parts of build config to separate file and merge with it 2021-12-22 16:31:18 +01:00
Bruno Windels
f2b822e5d2 move deps that are not used for sdk to devDependencies 2021-12-22 16:31:18 +01:00
Bruno Windels
2d2005934a WIP 2021-12-22 16:31:18 +01:00
Bruno Windels
363cd5b046 include css 2021-12-22 16:31:18 +01:00
Bruno Windels
8922d2aaf2 prototype of sdk build 2021-12-22 16:31:18 +01:00
RMidhunSuresh
10368500f2 Fix formatting 2021-12-10 12:12:52 +05:30
RMidhunSuresh
5ef7ab32df Update doc 2021-12-10 12:09:18 +05:30
Bruno Windels
dacdc1aec6
Merge pull request #597 from vector-im/ts-conversion-matrix-ssss
Convert matrix/ssss to typescript
2021-12-09 18:54:25 +01:00
Bruno Windels
589a002d67
Merge pull request #588 from vector-im/ts-conversion-matrix-net
Convert /matrix/net to typescript
2021-12-09 18:51:33 +01:00
Bruno Windels
21a41e192b Merge branch 'master' into ts-conversion-matrix-net 2021-12-09 18:49:54 +01:00
Bruno Windels
5ea29297cc fix typescript errors 2021-12-09 18:44:44 +01:00
Bruno Windels
c5c08ea34b
Merge pull request #586 from vector-im/bwindels/log-signature-failure
log signature verification failure in logger, not console
2021-12-09 18:40:01 +01:00
Bruno Windels
8d315f2741 Merge branch 'master' into bwindels/log-signature-failure 2021-12-09 18:34:36 +01:00
Bruno Windels
cd0d9dcbba
Merge pull request #548 from vector-im/bwindels/vite-mvp
Convert develop server and build system to using Vite
2021-12-09 18:29:06 +01:00
Bruno Windels
ba84387722 remove commented out code 2021-12-09 18:15:22 +01:00
Bruno Windels
0ec86b6dc1 Merge branch 'master' into bwindels/vite-mvp 2021-12-09 18:07:17 +01:00
Bruno Windels
5c5193ef48 remove old build system and unused dependencies
some of these are for the ie11 legacy build, which has been
postponed. They will be brougth back when we bring back the legacy build
2021-12-09 18:04:11 +01:00
Bruno Windels
d9ff4a8484 sw.js is not part of the sdk yet, so just put the path in index.html 2021-12-09 17:12:08 +01:00
Bruno Windels
dea7e7b4f5 enable minification and source maps 2021-12-09 16:42:35 +01:00
Bruno Windels
62827b92b7 implement placeholder replacement so it still works with minification 2021-12-09 16:37:31 +01:00
Bruno Windels
9a82f88e1f log swSource as build fails in CI 2021-12-09 15:13:19 +01:00
Bruno Windels
23e0d3f2ff get notification badge icon url through import now we transpile the sw 2021-12-09 15:13:05 +01:00
Bruno Windels
a4fac68393 use same method for setting version and build hash placeholder in sw
also better naming in service worker plugin
2021-12-09 14:36:12 +01:00
Bruno Windels
f934262e35 also use global hash var here 2021-12-09 12:22:17 +01:00
Bruno Windels
14dffa4ad4 remove leftover logging 2021-12-09 12:21:34 +01:00
Bruno Windels
8e4da396ea replace global hash in given chunks 2021-12-09 12:15:17 +01:00
Bruno Windels
c344032c0a transpile service worker and cleanup build plugin 2021-12-09 11:39:28 +01:00
Bruno Windels
180681b602 manifest ends up in assets folder, index.html in parent folder 2021-12-08 18:29:32 +01:00
Bruno Windels
fb8149b6cf add base to manifest path, just for completeness 2021-12-08 18:29:07 +01:00
Bruno Windels
4c2c99fc07 actually remove lookbehind 2021-12-08 18:05:57 +01:00
Bruno Windels
c8b0354d07 dont use lookbehind in regular expressions, safari & older firefoxes choke on them 2021-12-08 18:00:37 +01:00
Bruno Windels
c87628b614 cleanup 2021-12-06 15:40:15 +01:00
Bruno Windels
5bd28da4f3 loading olm from the worker was broken, reading the wrong global 2021-12-06 15:35:08 +01:00
Bruno Windels
0e2a22f509 also look in chunks for cacheable assets for service worker 2021-12-06 15:34:39 +01:00
Bruno Windels
91e69a2bd0 fix icons in manifest not being found 2021-12-06 15:25:44 +01:00
Bruno Windels
155cd4c9bd make olmPath absolute if it isn't already 2021-12-06 13:49:14 +01:00
RMidhunSuresh
734ecccb9c Use object instead of Record here 2021-12-03 17:34:23 +05:30
Bruno Windels
9a3f74c6fb load service worker in production mode, adjust development flag 2021-12-03 10:42:38 +01:00
RMidhunSuresh
e2abc312d3 Fix typescript errors 2021-12-03 11:48:01 +05:30
RMidhunSuresh
d6378133d8 Remove length property 2021-12-03 11:40:26 +05:30
RMidhunSuresh
49a56efa82 Remove comment 2021-12-03 11:40:26 +05:30
RMidhunSuresh
640cd88b6e make type string 2021-12-03 11:40:26 +05:30
RMidhunSuresh
66b4f9bfe5 LogItem --> ILogItem 2021-12-03 11:40:26 +05:30
RMidhunSuresh
0541cf8f2b Change object to Record 2021-12-03 11:40:26 +05:30
RMidhunSuresh
bf93bd79c9 types.js --> types 2021-12-03 11:40:26 +05:30
RMidhunSuresh
f89b937ee7 Use object instead of Record 2021-12-03 11:40:26 +05:30
RMidhunSuresh
82de3c9867 Prefer type over interface 2021-12-03 11:40:26 +05:30
RMidhunSuresh
b328c54da8 Change type from Ctor to Options 2021-12-03 11:40:26 +05:30
RMidhunSuresh
e9cea73357 Remove comment 2021-12-03 11:40:26 +05:30
R Midhun Suresh
3fbf65355d Rename Ctor to Options
Co-authored-by: Bruno Windels <brunow@matrix.org>
2021-12-03 11:40:26 +05:30
RMidhunSuresh
b5438f2ba8 Do not set content-length 2021-12-03 11:40:26 +05:30
RMidhunSuresh
4f43398db0 Fix promise resolve type 2021-12-03 11:40:26 +05:30
RMidhunSuresh
05121e32b1 Pull interface out for HomeServerApi 2021-12-03 11:40:26 +05:30
RMidhunSuresh
a8870f2d24 Extract ctor types out 2021-12-03 11:40:26 +05:30
RMidhunSuresh
238b9aafb1 Convert replay.js to ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
2e6b909173 No need to pass undefined 2021-12-03 11:40:26 +05:30
RMidhunSuresh
4bdcafad4b Rename file to types.ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
69e67ad5ac Make more functions private 2021-12-03 11:40:26 +05:30
RMidhunSuresh
2dd050bd90 Change object to Record 2021-12-03 11:40:26 +05:30
RMidhunSuresh
9b315d1564 Replace object with Record 2021-12-03 11:40:26 +05:30
RMidhunSuresh
57d24dcf90 Treat wrapper hsapi as HomeServerApi 2021-12-03 11:40:26 +05:30
RMidhunSuresh
8387215efd Add comment 2021-12-03 11:40:26 +05:30
RMidhunSuresh
885abc59be Add return types 2021-12-03 11:40:26 +05:30
RMidhunSuresh
7403cbc389 WIP - HomeServerApi.js to ts conversion 2021-12-03 11:40:26 +05:30
RMidhunSuresh
145b40f28d Fomatting fix 2021-12-03 11:40:26 +05:30
RMidhunSuresh
cf54b78af7 Convert RequestScheduler.js to ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
0aae31a450 Change year in copyright notice 2021-12-03 11:40:26 +05:30
RMidhunSuresh
f120ce50e6 Convert Reconnector.js to ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
90e3fde35d Convert MediaRepository.js to ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
ff53c2757d Convert HomeServerRequest.js to ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
e1a823400a Convert ExponentialRetryDelay.js to ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
3a24019d96 Convert common.js to ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
9688a561b3 Move interface to common.ts 2021-12-03 11:40:26 +05:30
RMidhunSuresh
2a3b13ecce Add request type 2021-12-03 11:40:26 +05:30
RMidhunSuresh
9bffd31ee3 Remove typeof 2021-12-03 11:36:51 +05:30
R Midhun Suresh
6dcebde69d Alias namespace as Olm
Co-authored-by: Bruno Windels <brunow@matrix.org>
2021-12-03 11:36:51 +05:30
RMidhunSuresh
e06a0e9e5a Use olm type from dependency 2021-12-03 11:36:51 +05:30
RMidhunSuresh
7362e38413 Convert interface to type 2021-12-03 11:36:51 +05:30
RMidhunSuresh
d2c09933c7 Type encrypted data 2021-12-03 11:36:51 +05:30
RMidhunSuresh
b2efcb9515 Convert SecretStorage.js to ts 2021-12-03 11:36:51 +05:30
RMidhunSuresh
814c0bed2e Convert recoveryKey.js to ts 2021-12-03 11:36:51 +05:30
RMidhunSuresh
e45f66a199 Convert passphrase.js to ts 2021-12-03 11:36:51 +05:30
RMidhunSuresh
dd4704b818 Fix imports 2021-12-03 11:36:49 +05:30
RMidhunSuresh
9b8ab9fd8d Convert index.js to index.ts 2021-12-03 11:35:12 +05:30
RMidhunSuresh
f9f59fec39 Convert common.js to ts 2021-12-03 11:34:09 +05:30
Bruno Windels
d91aaabeb3
Merge pull request #596 from vector-im/ts-conversion-matrix-sessioninfo
Convert matrix/sessioninfo to typescript
2021-12-02 09:29:11 +01:00
Bruno Windels
9042520916
Merge pull request #595 from vector-im/ts-conversion-matrix-push
Convert /matrix/push to typescript
2021-12-02 09:28:59 +01:00
Bruno Windels
d3ab961364
Merge pull request #593 from vector-im/ts-conversion-matrix-login
Convert /matrix/login to typescript
2021-12-02 09:28:47 +01:00
RMidhunSuresh
0c46460861 Add comment 2021-12-02 11:20:40 +05:30
RMidhunSuresh
9f82e7f7fc Add proper type 2021-12-02 11:17:41 +05:30
RMidhunSuresh
ef3456199c Fix formatting 2021-12-01 22:52:09 +05:30
R Midhun Suresh
928a5c27f3
Add rationale
Co-authored-by: Bruno Windels <brunow@matrix.org>
2021-12-01 22:50:59 +05:30
Bruno Windels
bc86bf2d00 some more sdk impl notes 2021-12-01 18:11:09 +01:00
Bruno Windels
fceca845a9 comment why we do this 2021-12-01 18:11:01 +01:00
Bruno Windels
09338d8aa8 bring back postcss plugins, apart from autoprefixer, which breaks vite 2021-12-01 18:10:25 +01:00
Bruno Windels
a504e74f54 extract function for script tag url 2021-12-01 18:10:02 +01:00
Bruno Windels
f83a0cec4e update postcss plugins so they all use the same version of postcss 2021-12-01 18:09:26 +01:00
Bruno Windels
69e34d03bd sort dependencies 2021-12-01 17:45:33 +01:00
Bruno Windels
261b17d36c fix lint 2021-12-01 17:27:13 +01:00
Bruno Windels
3fd2d39898 remove unused packages and move dev deps accordingly 2021-12-01 17:26:48 +01:00
Bruno Windels
bb9362ee8b only import node-html-parser when running the unit tests 2021-12-01 17:26:00 +01:00
Bruno Windels
1a618dd106 only import fake-indexeddb in tests
as it is a devDependency and can end up in the legacy bundle
under circumstances
2021-12-01 17:25:07 +01:00
Bruno Windels
7fda78ff2f disable legacy build for now 2021-12-01 14:06:15 +01:00
Bruno Windels
70c1e4e3ed move doc paragraphs around 2021-12-01 14:06:05 +01:00
Bruno Windels
b469d03677 config is at same level as paths 2021-12-01 14:05:50 +01:00
Bruno Windels
de24034b22 remove secondary theme as vite puts them in one bundle 2021-12-01 13:30:58 +01:00
Bruno Windels
75bf410320 correct path of main.js for tests 2021-12-01 13:30:17 +01:00
Bruno Windels
7e1818b285 Merge branch 'master' into bwindels/vite-mvp 2021-12-01 12:30:33 +01:00
RMidhunSuresh
73ca2dfb77 Add Record and fix typo 2021-12-01 16:05:16 +05:30
Bruno Windels
dce0ee5ace
Merge pull request #607 from vector-im/bwindels/fixpathsobserv
adjust path
2021-12-01 09:45:57 +01:00
Bruno Windels
85385a0aa7 adjust path 2021-12-01 09:43:58 +01:00
Bruno Windels
08314bd4b5
Merge pull request #606 from vector-im/bwindels/typescript-observable-2
Typescript conversion of yet more observables
2021-11-30 17:09:16 +01:00
Bruno Windels
8c3ae57497 fix Iterator vs IterableIterator confusion 2021-11-30 17:05:39 +01:00
Bruno Windels
de8995fa7e fix handlers in test missing methods, now that observable list is typed 2021-11-30 16:58:56 +01:00
Bruno Windels
581ef47c78 fix conflicting sortedIndex declaration 2021-11-30 16:53:59 +01:00
Bruno Windels
fc3eb7f57f Merge branch 'master' into bwindels/typescript-observable-2 2021-11-30 16:37:43 +01:00
Bruno Windels
49443d4f6e
Update TS-MIGRATION.md 2021-11-30 14:17:51 +00:00
Bruno Windels
2e57e99e34
clarify when to use type and interface 2021-11-30 14:15:25 +00:00
Bruno Windels
ef712b16f5
Merge pull request #584 from vector-im/ts-conversion-utils
Convert /utils to typescript
2021-11-30 14:13:09 +01:00
Bruno Windels
19827a0b5b
Merge pull request #601 from vector-im/filter-token
Ensure unwanted data do not end up in logs
2021-11-30 10:02:07 +01:00
RMidhunSuresh
66fbc37ec4 Remove comments 2021-11-30 14:15:49 +05:30
RMidhunSuresh
6699b71bd5 transformer is optional 2021-11-30 13:38:25 +05:30
RMidhunSuresh
fe77b71c97 use transformer function 2021-11-30 13:28:28 +05:30
Bruno Windels
be5deea1d3
Merge pull request #605 from vector-im/bwindels/try-test-ci-failure
Report unit test failures on CI
2021-11-30 08:20:05 +01:00
Bruno Windels
3322827979 upgrade impunity to propagate exit code 2021-11-30 08:16:28 +01:00
Bruno Windels
7f115b3e4b
Merge pull request #602 from vector-im/bwindels/tests-node-16.12
update impunity to 1.0.8 to run tests on node >= 16.12
2021-11-29 10:53:47 +01:00
Bruno Windels
a134e48ebb update impunity to 1.0.8 to run tests on node >= 16.12 2021-11-29 10:49:26 +01:00
RMidhunSuresh
104590e34d Use ! in test 2021-11-29 11:48:05 +05:30
RMidhunSuresh
d981a85239 Filter token out of stack trace 2021-11-29 11:43:43 +05:30
Bruno Windels
bc8b3d71d5
Merge pull request #600 from vector-im/bwindels/update-impunity
update to version that doesn't use a bash script anymore, which doesn…
2021-11-26 12:48:21 +01:00
Bruno Windels
2802164bb4 update to version that doesn't use a bash script anymore, which doesnt work on macos 2021-11-26 12:45:40 +01:00
Bruno Windels
876fcf532f release v0.2.22 2021-11-26 09:12:08 +01:00
Bruno Windels
92bf28e104
Merge pull request #599 from vector-im/bwindels/fix-emsonelogin
Don't fail login if dehydrated devices are not supported
2021-11-25 15:46:01 +01:00
Bruno Windels
ae7d4d07df use .name so we don't need an import 2021-11-25 15:42:36 +01:00
Bruno Windels
229c584138 don't fail login if dehydrated devices are not supported 2021-11-25 15:38:13 +01:00
RMidhunSuresh
bb18af414b Convert SessionInfoStorage.js to ts 2021-11-25 15:18:03 +05:30
RMidhunSuresh
3d9fbb685a Convert Pusher.js to ts 2021-11-25 13:23:05 +05:30
RMidhunSuresh
346e95c33c Change return type 2021-11-25 12:33:12 +05:30
RMidhunSuresh
a31860dc5f Fix formatting 2021-11-24 14:55:44 +05:30
RMidhunSuresh
c54ca168ed Convert SSOLoginHelper.js to ts 2021-11-24 14:49:08 +05:30
RMidhunSuresh
a1367f8e72 Fix password login 2021-11-24 14:00:26 +05:30
RMidhunSuresh
64037cb32a Convert TokenLoginMethod to ts 2021-11-24 13:56:47 +05:30
RMidhunSuresh
e4c443c73a Convert PasswordLoginMethod to ts 2021-11-24 13:47:26 +05:30
RMidhunSuresh
91f2a96403 Make LoginMethod an interface 2021-11-24 13:40:04 +05:30
Bruno Windels
93abbe83e8
Merge pull request #592 from vector-im/bwindels/lazylist-enhancements
Lazylist enhancements
2021-11-23 14:35:18 +01:00
Bruno Windels
f444160c6a feels ok without overflow margin for now 2021-11-23 14:33:27 +01:00
Bruno Windels
e4be1702c4 add comment for future test 2021-11-23 14:32:42 +01:00
Bruno Windels
7b38df45da i think this is fine now? 2021-11-23 14:31:23 +01:00
Bruno Windels
e34a92e2ec fix copyright 2021-11-23 14:30:11 +01:00
Bruno Windels
35fb84c275 remove old js lazylist 2021-11-23 14:26:15 +01:00
Bruno Windels
9557178ffb padding needs to be on ul, not scroll container, or the list blows up 2021-11-23 14:25:35 +01:00
Bruno Windels
4be2f12a14 subscribe before calling list.length 2021-11-23 14:25:22 +01:00
Bruno Windels
c64a9c1e23 snowpack/esbuild 0.9 doesn't support override keyword 2021-11-23 14:25:00 +01:00
Bruno Windels
7897ea88cd add some spaces and comments 2021-11-23 14:24:43 +01:00
Bruno Windels
c22718811f more tests for queryMove 2021-11-23 08:56:33 +01:00
Bruno Windels
3aa3b7e160 fix end growing larger than totalLength when range shrinks in case of remove 2021-11-23 08:30:52 +01:00
Bruno Windels
cf9f43ab9e WIP2 2021-11-22 20:35:57 +01:00
Bruno Windels
4a64d0ee17 WIP 2021-11-19 22:49:46 +01:00
RMidhunSuresh
d625d57aa4 Fix lastIndex
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:32:00 +01:00
RMidhunSuresh
bbeb909bdc Use createEnum
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:32:00 +01:00
RMidhunSuresh
33ac34b04e Do not break onListChanged
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:31:57 +01:00
RMidhunSuresh
5d54285640 Move ItemRange to separate file
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:12:50 +01:00
RMidhunSuresh
aee135a6cd Jsdoc fix
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
da715c70b0 Remove forceRender
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
e10b494f0c Improve containsIndex
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
3ae52ea1ca Fix bug in onAdd and onRemove
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
1165683f69 Fix onRemove
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
83ff2dd810 Fix onAdd
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
587dd3848e Use existing render function for initial render
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
a02b6b68d3 Move common code from if-else
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
168312627d Render only diff of ranges
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
RMidhunSuresh
61402e798e WIP 2 2021-11-19 12:06:40 +01:00
RMidhunSuresh
1a28b4f887 WIP 2021-11-19 12:06:40 +01:00
RMidhunSuresh
d4e923f9de Remove code from loadList
We don't need this method so best to leave it empty.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-11-19 12:06:40 +01:00
Bruno Windels
c0f7f0a8f1
Merge pull request #590 from vector-im/bwindels/platform-tests
Search platform-specific code for tests too
2021-11-19 10:26:35 +01:00
Bruno Windels
f1a6a4924e commit yarn.lock too 2021-11-19 10:23:59 +01:00
Bruno Windels
ec71e30ecb add Platform as entry point so also platform dependant code gets searched for tests 2021-11-19 10:23:18 +01:00
Bruno Windels
f23227fc8b use latest version of impunity with support for multiple entry points 2021-11-19 10:22:59 +01:00
Bruno Windels
5a747cd829
Merge pull request #589 from vector-im/bwindels/fix-sdk-example
packages processed by post-install need to be in dependencies, or the script fails
2021-11-18 15:26:08 +01:00
Bruno Windels
6980921dab
some impl notes for SDK 2021-11-18 14:16:10 +00:00
RMidhunSuresh
8fcfd713e0 Use IAbortable 2021-11-17 20:28:44 +05:30
RMidhunSuresh
ea2842f37f Return empty string 2021-11-17 20:28:44 +05:30
RMidhunSuresh
64a9892ee2 Use generic T in LockMap 2021-11-17 20:28:44 +05:30
RMidhunSuresh
048547828d Remove type Func 2021-11-17 20:28:44 +05:30
R Midhun Suresh
a14a8c3a07 Create interface IDisposable
Co-authored-by: Bruno Windels <brunow@matrix.org>
2021-11-17 20:28:44 +05:30
RMidhunSuresh
08ef84d112 Mention return type 2021-11-17 20:28:44 +05:30
RMidhunSuresh
5a0c06473c Use undefined instead of null 2021-11-17 20:28:44 +05:30
RMidhunSuresh
1beb153f21 func --> Func 2021-11-17 20:28:44 +05:30
RMidhunSuresh
0c424cb77f Fix imports 2021-11-17 20:28:44 +05:30
RMidhunSuresh
ebd1caf6d1 Convert enum.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
0e18247184 Use constant type 2021-11-17 20:28:44 +05:30
RMidhunSuresh
a945edfe07 Convert pbkdf2.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
6c2aa1bf61 Convert hkdf.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
afecac3e3c Convert timeout.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
8a169d5ddc Convert sortedIndex.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
ea0adb4407 Convert RetainedValue.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
88ec1b575d Convert mergeMap.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
1549d8add0 Convert LockMap to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
c8eb7ea7ac Convert Lock.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
a3460d8c2a Convert formatSize to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
4ce7634201 Convert error.js to ts 2021-11-17 20:28:44 +05:30
RMidhunSuresh
ef53a12f7a Fix imports 2021-11-17 20:28:44 +05:30
RMidhunSuresh
7d12c2ba54 Add return types 2021-11-17 20:28:44 +05:30
RMidhunSuresh
7270918b65 Convert Disposables to typescript 2021-11-17 20:28:44 +05:30
RMidhunSuresh
dd74ed1957 Add types to disposeValue 2021-11-17 20:28:44 +05:30
RMidhunSuresh
7772643b0d Disposables.js --> Disposables.ts 2021-11-17 20:28:44 +05:30
Bruno Windels
0a433b90e3 packages processed by post-install need to be in dependencies, or the script fails 2021-11-17 15:09:25 +01:00
Bruno Windels
efccc1e19e
Merge pull request #583 from vector-im/ts-conversion-logging
Convert src/logging to typescript
2021-11-17 14:37:42 +01:00
Bruno Windels
692ae25e76 remove unused method 2021-11-17 14:35:26 +01:00
Bruno Windels
b5e9eb26ba reduce size of ILogItem interface further 2021-11-17 14:33:58 +01:00
Bruno Windels
4030a4918d explicitly check for undefined 2021-11-17 13:57:11 +01:00
Bruno Windels
41a10d9697 explicitly check for undefined 2021-11-17 13:56:20 +01:00
Bruno Windels
fde0163b97 remove unneeded union type and simplify code 2021-11-17 13:53:27 +01:00
Bruno Windels
42e5fb33ba remove more non-public methods from ILogItem interface 2021-11-17 13:50:56 +01:00
Bruno Windels
526a818269 only used internally 2021-11-17 13:42:49 +01:00
Bruno Windels
afc538e875 explicitly check for type, rather than truthy 2021-11-17 12:58:08 +01:00
Bruno Windels
74fb15e426 add future todo note 2021-11-17 12:54:44 +01:00
Bruno Windels
46dd78162f no need to dig into internals here 2021-11-17 12:54:32 +01:00
Bruno Windels
276d8d4a42 check for undefined, no need for ! 2021-11-17 12:39:57 +01:00
Bruno Windels
b1d20178f8 add explicit void return type 2021-11-17 12:37:50 +01:00
Bruno Windels
5f362cbdbd remove dead code 2021-11-17 11:54:29 +01:00
Bruno Windels
695996d6e2 add ILogger and ILogExport interface, to give export correct return type
also move logging related types to own file
2021-11-17 11:39:12 +01:00
RMidhunSuresh
1b13f32d94 Remove resolved todo comment 2021-11-17 15:39:21 +05:30
Bruno Windels
3ee7e73ff0 item is optional here 2021-11-17 11:08:44 +01:00
Bruno Windels
90d7b73dd4 non-persisted queued items don't have an id yet, find them by ref equality 2021-11-17 11:08:29 +01:00
Bruno Windels
f93bdd962a might as well use generic here 2021-11-17 10:50:55 +01:00
Bruno Windels
1942c31eff still finish item when not returning from sync callback 2021-11-17 10:42:54 +01:00
RMidhunSuresh
d01271fb15 _run return T or void depending on boolean 2021-11-17 13:22:19 +05:30
RMidhunSuresh
07a1130db3 children can be array of ISerializedItem 2021-11-17 12:02:12 +05:30
RMidhunSuresh
835da58b53 Remove ! 2021-11-17 11:59:50 +05:30
R Midhun Suresh
9c8f96e233
value is optional
Co-authored-by: Bruno Windels <brunow@matrix.org>
2021-11-17 11:43:59 +05:30
RMidhunSuresh
b0ab8cd77f Space before { 2021-11-17 11:40:43 +05:30
RMidhunSuresh
8fce29caf7 Explicitly check for undefined 2021-11-17 11:38:33 +05:30
RMidhunSuresh
14eaa57434 No need for type assertion here 2021-11-16 13:14:11 +05:30
RMidhunSuresh
58105824d9 Fix error in reduce 2021-11-16 13:08:13 +05:30
RMidhunSuresh
4704a70cb7 Remove todo comment 2021-11-16 13:06:47 +05:30
RMidhunSuresh
34a8463bf9 Fix jsdoc return type 2021-11-16 12:43:23 +05:30
RMidhunSuresh
e339e730f4 Remove todo comment 2021-11-16 12:42:50 +05:30
RMidhunSuresh
286747c23c Add type annotation for ctor 2021-11-16 12:41:03 +05:30
RMidhunSuresh
3ee1607298 Convert utils to typescript 2021-11-16 12:32:49 +05:30
RMidhunSuresh
4161d31642 Convert NullLogger to typescript 2021-11-16 12:23:06 +05:30
RMidhunSuresh
4c1d7a8f2d Use generics over returning unknown 2021-11-15 22:47:38 +05:30
Bruno Windels
2da450d69d log signature verification failure in logger, not console 2021-11-15 15:27:57 +01:00
RMidhunSuresh
fe69f84c85 Use undefined in LogItem.serialize 2021-11-15 19:32:16 +05:30
RMidhunSuresh
ba5f2032ba Make properties in LogItem optional, not null 2021-11-15 19:17:49 +05:30
RMidhunSuresh
7097ba07d1 Replace LogLabelOrNull type with undefined 2021-11-15 18:59:33 +05:30
RMidhunSuresh
30a384fe1e Make LogFilter optional 2021-11-15 18:44:25 +05:30
RMidhunSuresh
520e0f1b89 Use interface ILogItem 2021-11-15 17:29:08 +05:30
RMidhunSuresh
a7d059b3ed Fix imports 2021-11-14 19:42:18 +05:30
RMidhunSuresh
bba44abf52 Convert console logger to ts 2021-11-14 16:24:16 +05:30
RMidhunSuresh
39d0708cca Add comment 2021-11-14 15:58:51 +05:30
RMidhunSuresh
2d8b719ab0 Add void return types as well 2021-11-14 15:55:42 +05:30
RMidhunSuresh
5efa27c2a3 Add more type annotations 2021-11-14 15:48:59 +05:30
RMidhunSuresh
67e8fc0c43 Add return types to methods in BaseLogger 2021-11-12 23:27:35 +05:30
RMidhunSuresh
8e42e3f21f Add types to returns in LogFilter.ts 2021-11-12 23:17:21 +05:30
RMidhunSuresh
29a8260514 Add explicit types for return in methods 2021-11-12 23:12:15 +05:30
RMidhunSuresh
8c7a765e11 Convert IDBLogger to ts 2021-11-12 15:06:21 +05:30
RMidhunSuresh
f3d0f88f95 Make error public 2021-11-12 15:06:11 +05:30
RMidhunSuresh
2ddd2d16ed IDBLogger.js --> IDBLogger.ts 2021-11-11 16:50:46 +05:30
RMidhunSuresh
9fed2ca41b Use undefined instead of null 2021-11-11 16:25:14 +05:30
RMidhunSuresh
eb7c5c4437 Use undefined only instead of both undefined and null 2021-11-11 16:08:25 +05:30
RMidhunSuresh
09851600f7 Remove unwanted types 2021-11-11 15:35:51 +05:30
RMidhunSuresh
425a3c85a9 Make error prop private and expose via getter 2021-11-11 13:24:52 +05:30
RMidhunSuresh
0f7a78ee25 Make return type explicit 2021-11-11 13:05:12 +05:30
Bruno Windels
7148f6fd41 wip to not babel deps 2021-11-10 19:10:23 +01:00
Bruno Windels
e83781b26a make ie11 compatible 2021-11-10 19:10:06 +01:00
RMidhunSuresh
cd7dccd804 Move interface to top 2021-11-10 19:13:35 +05:30
RMidhunSuresh
7a68c971aa Make field readonly 2021-11-10 19:07:24 +05:30
RMidhunSuresh
cfa7708b57 Use type imports 2021-11-10 18:51:46 +05:30
Bruno Windels
c47f872f6f
Merge pull request #578 from vector-im/fix-progressbar
Fix progressbar not working on attachment uploads
2021-11-10 11:17:02 +01:00
RMidhunSuresh
ef2aad8956 Annotate LogFilter 2021-11-10 15:04:07 +05:30
RMidhunSuresh
ab126729e0 Use LogLevel as type instead of number 2021-11-10 14:49:59 +05:30
RMidhunSuresh
e3c85c585e Log callbacks can return more than Promises 2021-11-10 14:42:43 +05:30
RMidhunSuresh
0b4eca4724 Create alias for LogLevel | null 2021-11-10 14:29:23 +05:30
RMidhunSuresh
142d3ef543 Split LogItemValues into union of types 2021-11-10 13:45:37 +05:30
RMidhunSuresh
ceb52eedaf Fix imports and add type annotations 2021-11-10 12:36:56 +05:30
RMidhunSuresh
772f7a2757 Account for duration being null 2021-11-10 12:17:43 +05:30
RMidhunSuresh
db792ab5a9 Add type annotations to LogItem 2021-11-10 12:06:50 +05:30
RMidhunSuresh
97ec680af2 Remove .js files 2021-11-10 12:06:22 +05:30
RMidhunSuresh
ba4d5453a2 Move type LogCallback to LogItem 2021-11-10 12:05:29 +05:30
Bruno Windels
36a982f7e2 WIP to run rollup twice, first with vite and babel, then to inline deps 2021-11-09 18:10:12 +01:00
RMidhunSuresh
2a5d30d749 Convert to enum 2021-11-09 22:32:02 +05:30
Bruno Windels
122528f9a9 also transpile typescript files
note that these have already been converted to javascript by vite
2021-11-09 17:59:58 +01:00
RMidhunSuresh
55401a746c Move type alias to LogItem
and add more type annotations
2021-11-09 22:28:26 +05:30
RMidhunSuresh
8cbc81b8bb Annotate method arguments 2021-11-09 20:57:47 +05:30
Bruno Windels
da7f66a531 setup babel for legacy build as input transform plugin 2021-11-09 14:52:03 +01:00
RMidhunSuresh
7893a121c0 Initialize in field 2021-11-09 17:36:18 +05:30
RMidhunSuresh
4c5d028509 any --> unknown 2021-11-09 17:34:16 +05:30
RMidhunSuresh
eef116e26b annotate labelOrValues 2021-11-09 17:19:46 +05:30
RMidhunSuresh
8fba3f4ca9 Add explaining comment 2021-11-09 15:39:24 +05:30
RMidhunSuresh
839d3fb689 Throw on export() in ConsoleLogger 2021-11-09 13:53:07 +05:30
RMidhunSuresh
377cc4ca1f Make BaseLogger abstract 2021-11-09 13:52:41 +05:30
RMidhunSuresh
030c46264b type annotate fields 2021-11-09 13:00:37 +05:30
RMidhunSuresh
dad37dece3 .js --> .ts 2021-11-09 11:46:05 +05:30
RMidhunSuresh
57e2c4ea45 No need for handler to be async 2021-11-08 22:20:56 +05:30
RMidhunSuresh
c1a8ffd814 respond with only for GET requests 2021-11-08 18:03:20 +05:30
Bruno Windels
b95c918dc6
Merge pull request #580 from vector-im/bwindels/scroll-composer
add scrollbar when > 5 lines in composer
2021-11-08 12:25:39 +01:00
Bruno Windels
e9586711e0 add scrollbar when > 5 lines in composer 2021-11-08 11:19:24 +01:00
Bruno Windels
ffef4936f9 update caniuse defs again after rebase 2021-11-08 11:14:00 +01:00
Bruno Windels
fcde507183 WIP 2021-11-08 11:13:02 +01:00
Bruno Windels
3b72157e64 apparently, package-overrides should use esm
otherwise they don't get transpiled for some reason
2021-11-08 11:12:19 +01:00
Bruno Windels
7dce579ac3 make babel plugin run after commonjs one added by vite 2021-11-08 11:12:19 +01:00
Bruno Windels
16918ddb7d ie11 build wip 2021-11-08 11:12:13 +01:00
Bruno Windels
b65782e13c ignore tsc errors on vite-specific imports
or not understood by tsc in any case
2021-11-08 11:10:34 +01:00
Bruno Windels
eb60f6717a add comment for improvements 2021-11-08 11:10:34 +01:00
Bruno Windels
923a1a2057 use vite define option to inject version number everywhere 2021-11-08 11:10:34 +01:00
Bruno Windels
216afd45cc vite/rollup plugin to inject and transform manifest & service worker 2021-11-08 11:10:34 +01:00
Bruno Windels
3fe1c0cdc3 tweak build and start command to use vite 2021-11-08 11:10:34 +01:00
Bruno Windels
afadd25885 tweak build settings somewhat for now 2021-11-08 11:10:34 +01:00
Bruno Windels
e2b20f466d remove unneeded package override 2021-11-08 11:10:34 +01:00
Bruno Windels
01712c3f23 make tests run again 2021-11-08 11:10:34 +01:00
Bruno Windels
b6fda8865f make all dependencies use vite and remove post-install script / lib dir 2021-11-08 11:10:34 +01:00
Bruno Windels
db3e8a9c6b rearrange assets and main.js to make them run with vite 2021-11-08 11:10:23 +01:00
RMidhunSuresh
d1491cc203 More checks before returning 2021-11-08 15:11:41 +05:30
RMidhunSuresh
d31371b486 Return on upload in sw 2021-11-08 14:37:32 +05:30
Bruno Windels
2afcddbf49 release v0.2.21 2021-11-05 21:08:49 +01:00
Bruno Windels
25fb645c4b
Merge pull request #577 from vector-im/bwindels/update-caniuse
update caniuse
2021-11-05 21:07:39 +01:00
Bruno Windels
5c689ac5b1 yarn wants deps in different order 2021-11-05 21:02:46 +01:00
Bruno Windels
e1c8088de2 update caniuse browser definitions 2021-11-05 21:02:16 +01:00
Bruno Windels
d40037ef49
Merge pull request #576 from vector-im/bwindels/setup-new-dehydrated-device-when-claiming
Setup new dehydrated device when claiming
2021-11-05 20:56:23 +01:00
Bruno Windels
faa0246e28 setup new dehydrated device when claiming one 2021-11-05 20:53:04 +01:00
Bruno Windels
0749073120 clone key as olm clears it 2021-11-05 20:52:50 +01:00
Bruno Windels
2dccd36a6d
Merge pull request #575 from vector-im/bwindels/composer-layout-improvements
Composer layout improvements
2021-11-05 19:24:34 +01:00
Bruno Windels
23494ab630 bottom align send button, adjust paddings 2021-11-05 19:05:52 +01:00
Bruno Windels
2f15c9a4a7 show either attachment or send button, depending on composer value 2021-11-05 19:05:35 +01:00
Bruno Windels
c3203fdacd explain this value 2021-11-05 19:05:09 +01:00
Bruno Windels
222c616148
Merge pull request #574 from vector-im/bwindels/multiline-composer-fixups
Multiline composer fixups
2021-11-05 18:44:54 +01:00
Bruno Windels
44e7e25cab clear height while sending or clearing, also fix #572 in the process 2021-11-05 18:36:59 +01:00
Bruno Windels
fc1b9abe66 don't add line when hitting enter to send 2021-11-05 18:36:38 +01:00
Bruno Windels
365c8d0953
Merge pull request #566 from vector-im/composer-improvements
Support for multiline messages
2021-11-05 18:20:02 +01:00
Bruno Windels
8ffd98162c don't make the composer shrink for now, timeline loses scroll position 2021-11-05 17:48:44 +01:00
Bruno Windels
c671596c6f only schedule one resize callback per frame 2021-11-05 17:48:08 +01:00
Bruno Windels
b22437840d don't set explicit height, rely on rows=1 for default height 2021-11-05 17:47:20 +01:00
Bruno Windels
4c5fe824c2
Merge pull request #569 from vector-im/fix-400
Show redacted tile for redacted messages in encrypted rooms
2021-11-05 15:57:36 +01:00
Bruno Windels
1fee773313
Merge pull request #571 from vector-im/fix-570
Make progress bar work for image uploads in Chrome
2021-11-05 15:56:33 +01:00
Bruno Windels
6be952491a
Merge pull request #573 from vector-im/fix-517-2
Continue filling gaps that return only non-rendered events in the first backfill
2021-11-05 15:45:45 +01:00
Bruno Windels
781147bf0e add some comments and rename for clarity 2021-11-05 15:42:07 +01:00
RMidhunSuresh
a4cd40c2f8 Keep filling gap until sibling changes 2021-11-05 17:25:29 +05:30
RMidhunSuresh
96a2dd7c72 Add event handler before executing open
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-11-04 00:02:50 +05:30
RMidhunSuresh
f6b7dcbad7 Show redacted tile 2021-11-03 20:03:47 +05:30
Bruno Windels
b011c3df03 release v0.2.20 2021-11-03 02:38:52 +01:00
Bruno Windels
014acbfaf5
Merge pull request #564 from vector-im/bwindels/dehydrated-device
Device dehydration support
2021-11-03 00:52:46 +01:00
Bruno Windels
ee9c9b33ca fix lint again 2021-11-03 02:36:53 +01:00
Bruno Windels
b2d8f5f023 fix lint 2021-11-03 02:35:22 +01:00
Bruno Windels
790b9cbc13 require a flag to enable account setup during login
as it blocks login from progressing, any sdk usage should enable
to indicate they are listening for the AccountSetup status.
2021-11-03 02:28:01 +01:00
Bruno Windels
bffe34fe0a await write key 2021-11-03 02:20:27 +01:00
Bruno Windels
a8022077f6 add minimal logging 2021-11-03 02:20:11 +01:00
Bruno Windels
80a98f04c7 pickle clears the key, so slice it before calling so we can reuse for 4s 2021-11-03 02:08:27 +01:00
Bruno Windels
8a36eb4532 check mac of dehydrated key to match default 4s key mac before adopting 2021-11-03 02:07:57 +01:00
Matthew Hodgson
abef4f0f79
Merge pull request #568 from vector-im/initial-github-action-for-docker-images
Add GH Action to build the docker image
2021-11-02 00:19:45 +00:00
Ben Banfield-Zanin
05fe68823a
Add GH Action to build the docker image
Builds the docker image on pushes for: tags, PRs, master.

Publishes docker images as per:
* `latest` & `<TAG>` for tags
* `master` for `master`

Same as https://github.com/matrix-org/lb/pull/8 & https://github.com/matrix-org/matrix-content-scanner/pull/46
2021-11-01 14:55:32 +00:00
Bruno Windels
567cdd5510 WIP for enabling session backup from dehydration key 2021-10-29 19:17:31 +02:00
Bruno Windels
6d9d8797fe use same UI as in settings to pick between recovery key and passphrase 2021-10-29 16:40:35 +02:00
Bruno Windels
44a26fd340 key backup: add disable button,and enabling add dehydrated device option 2021-10-29 15:48:28 +02:00
Bruno Windels
3b3751c827 remove dehydrated device test button from settings 2021-10-29 15:47:14 +02:00
Bruno Windels
6273d723f1 fix typo 2021-10-29 15:24:24 +02:00
RMidhunSuresh
6863fef7e5 Resize composer with text
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-10-29 17:00:02 +05:30
RMidhunSuresh
3a6e74ae1c Add css style
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-10-29 15:11:34 +05:30
RMidhunSuresh
16bec0a656 Convert input to textarea
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-10-29 15:11:12 +05:30
Bruno Windels
c3dfdde626 be forgiving when dispose has already been called 2021-10-28 12:04:42 +02:00
Bruno Windels
544019f67d ensure olm Account in dehydrated device is freed on error 2021-10-28 11:52:32 +02:00
Bruno Windels
bef12c7a8f prevent double free on olm account when logging out
by ensuring we only dispose the e2ee/Account once, as well as
the Session and other classes
2021-10-28 11:48:25 +02:00
Bruno Windels
3ef37c15c7 remove import session button as it's not supported anymore 2021-10-28 11:47:54 +02:00
Bruno Windels
68a6113c26 add logout button to session load error screen 2021-10-28 11:47:31 +02:00
Bruno Windels
cbccca20d0 remove leftover logging 2021-10-27 18:09:31 +02:00
Bruno Windels
e3378d5636 use correct device_id in signatures for dehydrating device
completely replace device id for dehydrating device
so we don't have to pass it down the stack
2021-10-27 18:08:50 +02:00
Bruno Windels
c89e414bb5 WIP3 2021-10-27 15:08:53 +02:00
Bruno Windels
718b410253 WIP2 2021-10-27 10:26:36 +02:00
Bruno Windels
faf4ea6434 WIP 2021-10-26 18:47:46 +02:00
Bruno Windels
abb802b881 release v0.2.19 2021-10-26 15:38:50 +02:00
Bruno Windels
d9ecf38e42
Merge pull request #563 from vector-im/bwindels/exportlogsonsessionloadfail
add export logs button when session fails to load
2021-10-26 15:35:50 +02:00
Bruno Windels
7ef19e0ead add export logs button when session fails to load 2021-10-26 15:30:52 +02:00
Bruno Windels
c621ccf679 release v0.2.18 2021-10-26 15:10:44 +02:00
Bruno Windels
0f0719eaa2
Merge pull request #560 from vector-im/bwindels/logout
Add Log out
2021-10-26 15:10:21 +02:00
Bruno Windels
5b889f0b32
Merge pull request #562 from vector-im/bwindels/diagnose-561
return static string when member is missing and add logging
2021-10-26 15:10:08 +02:00
Bruno Windels
82a0c1024c return static string when member is missing and add logging 2021-10-26 15:08:51 +02:00
Bruno Windels
af85fe3892 confirm before logging out 2021-10-26 14:48:37 +02:00
Bruno Windels
f998041748 add logout button in settings 2021-10-26 12:49:31 +02:00
Bruno Windels
2b884e73db remove action buttons on session picker
and now that we're adding logout, none of them are something we want to support really
2021-10-26 11:43:38 +02:00
Bruno Windels
e3c5def536 release v0.2.17 2021-10-26 11:32:42 +02:00
Bruno Windels
fae4493abc
Merge pull request #554 from vector-im/bwindels/fix-551
Only keep a limited amount of olm InboundGroupSession objects in memory to prevent OOM
2021-10-26 11:30:10 +02:00
Bruno Windels
67dd929951 put key session check in method 2021-10-26 11:14:46 +02:00
Bruno Windels
805c2657f2 remove unrelated file 2021-10-26 11:07:17 +02:00
Bruno Windels
ab2f15b5a2 prevent cache hiding better keys in storage (+ tests) 2021-10-25 19:17:13 +02:00
Bruno Windels
3c2604b384 test that sessions get free'd correctly 2021-10-25 17:33:33 +02:00
Bruno Windels
12b5bd3a4f
Merge pull request #553 from moritzdietz/moritzdietz/update-faq
Update FAQ: Add note about published builds
2021-10-25 17:21:27 +02:00
Bruno Windels
74e8bc3bda write unit tests 2021-10-25 17:19:48 +02:00
Bruno Windels
6bbce06d93 start writing tests for key loader 2021-10-22 19:01:20 +02:00
Bruno Windels
22361bdf42 don't need to dispose room keys anymore, they are owned by the loader 2021-10-22 18:08:09 +02:00
Bruno Windels
076f450ec7 this can be const 2021-10-22 18:01:26 +02:00
Bruno Windels
6d8ec69a4d fix lint 2021-10-22 18:01:17 +02:00
Bruno Windels
b7e3a54e15 remove now usused code 2021-10-22 17:51:00 +02:00
Bruno Windels
2943cb525f add comment about possible future optimization 2021-10-22 17:50:45 +02:00
Bruno Windels
1278288a42 cleanup RoomKey to changes and better naming 2021-10-22 17:50:30 +02:00
Bruno Windels
66a93ee108 adapt Session and RoomEncryption to megolm/Decryption API changes 2021-10-22 17:48:53 +02:00
Bruno Windels
ac23119838 convert SessionDecryption to TS and adapt to use KeyLoader 2021-10-22 17:48:35 +02:00
Bruno Windels
b55930f084 convert ReplayDetectionEntry to typescript 2021-10-22 17:47:29 +02:00
Bruno Windels
d6e243321b convert megolm/Decryption to typescript and adapt to KeyLoader 2021-10-22 17:46:39 +02:00
Bruno Windels
2ddb3fbf72 cleanup 2021-10-22 17:45:55 +02:00
Bruno Windels
45dc2162dc fix unit tests 2021-10-22 17:30:20 +02:00
Bruno Windels
77d10c93d6 convert groupby and megolm decryption utils to typescript 2021-10-21 14:40:51 +02:00
Bruno Windels
66a77519d7 implement key caching in KeyLoader
merging session cache into it so we can better manage and recycle
keys without exposing too low-level public methods on BaseLRUCache.

Using refCount instead of inUse flag as a key can of course be used
by multiple useKey calls at the same time.
2021-10-21 11:12:54 +02:00
Bruno Windels
3bafc89855 remove unused draft code 2021-10-20 15:25:11 +02:00
Bruno Windels
4fa285e85a convert LRUCache to ts 2021-10-20 15:24:58 +02:00
Bruno Windels
041cedbc58 fix typescript extension change 2021-10-20 15:24:39 +02:00
Bruno Windels
cbf82fcd29 cleanup code so far 2021-10-20 15:14:17 +02:00
Bruno Windels
5dc0c8c0b3 make 'better' better 2021-10-20 13:38:54 +02:00
Moritz Dietz
c83f78044e
Update FAQ to highlight about the existence of published builds 2021-10-20 12:46:37 +02:00
Bruno Windels
d7407ecf66 WIP 2021-10-20 11:39:01 +02:00
Bruno Windels
82aac93f36
Update SDK.md 2021-10-20 07:05:39 +00:00
Bruno Windels
c92d6ecbb6
Merge pull request #549 from danger89/patch-1
Add HTML title & icon
2021-10-08 10:39:08 +02:00
Melroy van den Berg
a20fe2b5a6
Add HTML title & icon
Add missing HTML title and icon
2021-10-06 20:38:39 +02:00
Danila Fedorin
3d2c74a760 Add type annotations to SortedArray 2021-10-03 22:19:46 -07:00
Danila Fedorin
7b2e452cd5 Rename SortedArray to TypeScript 2021-10-03 22:19:46 -07:00
Danila Fedorin
1363af24a7 Add type annotations to MappedList 2021-10-03 22:19:46 -07:00
Danila Fedorin
84187ce109 Make updater optional in BaseObservableList 2021-10-03 22:19:44 -07:00
Danila Fedorin
0466b49520 Rename MappedList to TypeScript 2021-10-03 22:18:12 -07:00
Danila Fedorin
3b131f2db6 Add type annotations to ConcatList 2021-10-03 22:18:12 -07:00
Danila Fedorin
588da9b719 Relax types on BaseObservableList and add helper for tests 2021-10-03 22:18:12 -07:00
Danila Fedorin
ddca467e30 Rename ConcatList to TypeScript 2021-10-03 22:18:12 -07:00
Danila Fedorin
8466a910da Add type annotations to AsyncMappedList 2021-10-03 22:18:12 -07:00
Danila Fedorin
0e6c59983f Generalize BaseMappedList to allow mappers to promises 2021-10-03 22:18:10 -07:00
Danila Fedorin
e6de873b6e Rename AsyncMappedList to TypeScript 2021-10-03 22:16:46 -07:00
Danila Fedorin
b148f3ca9e Add type annotations to ObservableArray 2021-10-03 22:16:46 -07:00
Danila Fedorin
348a9c83f5 Rename ObservableArray to TypeScript 2021-10-03 22:16:46 -07:00
Bruno Windels
6517704850
Merge pull request #545 from vector-im/bwindels/fixbuild
the build currently does not like override for some reason
2021-10-01 14:09:07 +02:00
Bruno Windels
cc58d27122 the build currently does not like override for some reason 2021-10-01 14:07:58 +02:00
Bruno Windels
39d5073f49 remove user and password 2021-10-01 13:37:37 +02:00
Bruno Windels
191cb78d8f
Merge pull request #544 from vector-im/bwindels/sdk-export
Provide very basic SDK interface
2021-10-01 13:33:13 +02:00
Bruno Windels
fbfda03138 don't let tsc check build system specific files 2021-10-01 13:32:09 +02:00
Bruno Windels
23da4ae2dd point to SDK docs from FAQ 2021-10-01 13:27:58 +02:00
Bruno Windels
675ee59e50 Add instructions and example code for basic SDK usage 2021-10-01 13:26:14 +02:00
Bruno Windels
193a0e1a4f move UI docs to doc folder 2021-10-01 13:26:03 +02:00
Bruno Windels
c1d20cb9f9 also call this -Path 2021-10-01 12:56:36 +02:00
Bruno Windels
d2eeaab5f5 get assets paths with import "asset?url" specifically for vitejs 2021-10-01 12:49:41 +02:00
Bruno Windels
aac0e74b9d also export LoadStatus 2021-10-01 12:49:30 +02:00
Bruno Windels
f89d169ef3 provide library entry point that provides convenient reexports of the public classes 2021-10-01 11:30:42 +02:00
Bruno Windels
de22a0790f
Merge pull request #543 from vector-im/bwindels/typescript-observable
Typescript conversion of base observables
2021-10-01 10:10:20 +02:00
Bruno Windels
1c06e36c1c add override keyword 2021-10-01 10:07:17 +02:00
Bruno Windels
3de3481765 prefer optional syntax over '| null' 2021-10-01 10:05:56 +02:00
Bruno Windels
6b50a63e95 missing space 2021-10-01 10:02:32 +02:00
Bruno Windels
393e2f809e make subscription handle return undefined, so we can reassign optional members in one statement 2021-10-01 10:01:52 +02:00
Bruno Windels
a0f443ccc3
Merge pull request #521 from DanilaFe/typescript-observable
Start migrating Observable code to TypeScript.
2021-10-01 09:54:29 +02:00
Bruno Windels
155207ed95
Merge pull request #392 from MidhunSureshR/documentation
Documentation for Hydrogen
2021-09-30 18:38:43 +02:00
Bruno Windels
255e479d47
Merge pull request #541 from vector-im/bwindels/fix-540
scroll room list to top when entering query first
2021-09-30 17:24:00 +02:00
Bruno Windels
e42739ec81 scroll room list to top when entering query first 2021-09-30 17:19:42 +02:00
Bruno Windels
8911588de9 release v0.2.16 2021-09-30 12:48:45 +02:00
Bruno Windels
3fba2c6513
Merge pull request #525 from vector-im/bwindels/fix-sssskey-conversion
fix sssskey not being migrated properly
2021-09-30 10:19:31 +02:00
Bruno Windels
89add8b684 fix sssskey not being migrated properly 2021-09-30 10:18:03 +02:00
Bruno Windels
31a70e1b8e release v0.2.15 2021-09-30 09:52:35 +02:00
Bruno Windels
dcb08f5266
Merge pull request #524 from vector-im/bwindels/fix-492
clear relations for room when forgetting room
2021-09-30 09:51:59 +02:00
Bruno Windels
d10d27c1d6 clear relations for room when forgetting room 2021-09-30 09:49:45 +02:00
Bruno Windels
4cebe26186
Merge pull request #523 from vector-im/bwindels/clear-cache-after-515
clear history cache to purge potential timeline corruption from #515
2021-09-30 09:41:24 +02:00
Bruno Windels
f8f4bb4eac
Merge pull request #520 from vector-im/bwindels/fix-139
Keep backup of e2ee identity in localStorage when idb gets cleared
2021-09-30 09:28:56 +02:00
Bruno Windels
42f1603d81 use correct prefix to remove local storage value 2021-09-30 09:25:35 +02:00
Bruno Windels
6ea835c2d1
Merge pull request #522 from xunzhou/master
Required pkg for aarch64 docker build
2021-09-30 08:48:47 +02:00
Bruno Windels
ae68264db4 don't use switch where single if/else works 2021-09-30 08:42:00 +02:00
Bruno Windels
ccda93cc82 remove leftover logging 2021-09-30 08:40:49 +02:00
Bruno Windels
3556878a1e clear history cache to purge potential timeline corruption from #515 2021-09-30 08:37:33 +02:00
xunzhou
17f24942da Required pkg for aarch64 docker build 2021-09-29 19:31:39 -07:00
Danila Fedorin
c80dfb10a2 Add type annotations to BaseMappedList 2021-09-29 18:41:30 -07:00
Danila Fedorin
99164eb0d8 Rename BaseMappedList to TypeScript 2021-09-29 18:17:38 -07:00
Danila Fedorin
bf53449f66 Add type annotations to common 2021-09-29 18:13:49 -07:00
Danila Fedorin
e53f3d23d5 Rename common to TypeScript 2021-09-29 18:10:09 -07:00
Danila Fedorin
64ba656043 Update ListView and TimelineListView 2021-09-29 18:08:13 -07:00
Danila Fedorin
414280ada9 Add type annotations to BaseObservableList 2021-09-29 18:05:30 -07:00
Danila Fedorin
3952c3b969 Rename BaseObservableList to TypeScript 2021-09-29 17:46:51 -07:00
Danila Fedorin
ab6ce62551 Add type annotations to ObservableValue 2021-09-29 17:43:17 -07:00
Danila Fedorin
a7360f409e Rename ObservableValue to TypeScript 2021-09-29 17:42:57 -07:00
Danila Fedorin
319027e2e3 Add type annotations to BaseObservable 2021-09-29 17:42:57 -07:00
Danila Fedorin
d73dea797a Rename BaseObservable to TypeScript 2021-09-29 17:18:22 -07:00
Bruno Windels
e0b9a3fa50 create e2ee identity also when storage got cleared without backup 2021-09-29 20:07:42 +02:00
Bruno Windels
a1c66738db migration to initialize & restore e2ee identity backup 2021-09-29 19:39:26 +02:00
Bruno Windels
3a064d6796 a IDBRequest when opening the database does not have a source 2021-09-29 19:21:42 +02:00
Bruno Windels
2ef7251079 move ssssKey to e2ee prefix as well so it gets backed up too 2021-09-29 19:21:06 +02:00
Bruno Windels
77bd0d3f3c store e2ee session values as well in localStorage 2021-09-29 11:49:58 +02:00
Bruno Windels
004aa5d3dc
Merge pull request #519 from vector-im/bwindels/rickfixes
Fix immediate errors for sdk usage
2021-09-29 11:12:17 +02:00
Bruno Windels
1b87eaf21e move packages needed for post-install script to (non-dev) deps 2021-09-29 11:02:59 +02:00
Bruno Windels
cd071e47e0 release v0.2.14 2021-09-28 14:59:40 +02:00
Bruno Windels
9a96a5b7bb
Merge pull request #516 from vector-im/bwindels/fix-515
Fix interpreting hex keys as decimal
2021-09-28 14:25:33 +02:00
Bruno Windels
e31d3abc97 fix ts errors in TimelineEventStore unit tests 2021-09-28 14:20:54 +02:00
Bruno Windels
ec2f1b9833 add unit tests for findExistingKeys 2021-09-28 14:20:21 +02:00
Bruno Windels
edc3a1d33c convert storage mock to TS and add utility for mock raw database 2021-09-28 14:19:59 +02:00
Bruno Windels
9036b21b5c don't interpret hex as decimal when decoding the key
this fixes #515 as it was causing the gap not to be closed,
because the fragment id was never equal.
2021-09-28 11:34:55 +02:00
Bruno Windels
0846fcc05d add more logging during gap filling 2021-09-27 16:34:12 +02:00
Bruno Windels
f55f450850 fix findExistingKeys too many (existing but not requested) keys 2021-09-27 16:27:52 +02:00
Bruno Windels
cc0b938a6d release v0.2.13 2021-09-24 18:45:33 +02:00
Bruno Windels
7c44fb8cd4
Merge pull request #510 from vector-im/bwindels/fix-499
move over word-break lines
2021-09-24 18:44:41 +02:00
Bruno Windels
6982f55cd7 move over word-break lines
this didn't get moved over when converting the timeline tile css to css grid
2021-09-24 18:42:47 +02:00
Bruno Windels
e2d7954846
Merge pull request #509 from vector-im/bwindels/fix-menupositioning
automatically position popups using a simpler algorithm
2021-09-24 18:32:36 +02:00
Bruno Windels
71bd797dd4 automatically position popups using a simpler algorithm 2021-09-24 18:28:06 +02:00
Bruno Windels
a7b6fe4b22
Merge pull request #508 from vector-im/bwindels/fix-393
don't (re)link fragments in fill, close gap if overlap w linked fragment
2021-09-24 15:42:49 +02:00
Bruno Windels
b75e2fe0ce decode straight to EventKey object 2021-09-24 15:40:33 +02:00
Bruno Windels
0d798178b0 log neighbor fragment id when closing gap 2021-09-23 18:50:40 +02:00
Bruno Windels
c6ed0abfd7 eventIds is missing 2021-09-23 18:49:23 +02:00
Bruno Windels
e6f7f213ec don't (re)link fragments in fill, close gap if overlap w linked fragment 2021-09-23 18:02:05 +02:00
Bruno Windels
45917eae1d
Merge pull request #494 from vector-im/DanilaFe/backfill-changes
Unit tests for GapWriter, using a new timeline mock utility
2021-09-23 10:15:37 +02:00
Bruno Windels
4b7cb6da9e make backfill limit explicit 2021-09-23 10:10:22 +02:00
Bruno Windels
dd71fdbe08 add comment 2021-09-23 10:04:58 +02:00
Bruno Windels
6c12f0f66f release v0.2.12 2021-09-22 10:45:46 +02:00
Bruno Windels
0848306cb0
Merge pull request #506 from vector-im/bwindels/fix-504
Drop events that have been synced before
2021-09-22 10:44:55 +02:00
Bruno Windels
498c00fe3c no need for try catch here as we already catch in getKeys 2021-09-22 10:38:29 +02:00
Bruno Windels
ac5a4c2bc6 pass log item everywhere to tryInsert 2021-09-22 10:33:40 +02:00
Bruno Windels
b58e10521f don't log tryInsert failures anymore as everything is logged in Store 2021-09-22 10:23:28 +02:00
Bruno Windels
1963635dd7 also log index keys for a value when write fails in Store 2021-09-22 10:22:52 +02:00
Bruno Windels
a19d93dbef don't swallow anything that isn't a request error 2021-09-22 09:36:26 +02:00
Bruno Windels
704d7b32da add tests 2021-09-21 21:04:29 +02:00
Bruno Windels
6cded5319a change timelineEventStore.insert to tryInsert 2021-09-21 21:04:10 +02:00
Bruno Windels
12add19c31 add Store.tryAdd, which prevent abort on ConstraintError 2021-09-21 21:03:29 +02:00
Bruno Windels
0d486a14f6 add the logger property to the null logger as well, forgot this before 2021-09-21 21:01:35 +02:00
Bruno Windels
2d2521cd9a add prototype to show we can prevent the txn from being aborted on error 2021-09-21 20:58:16 +02:00
Danila Fedorin
92dcc6c980 Remove duplicated lines 2021-09-21 09:39:09 -07:00
Bruno Windels
b8548afb71 export temporary vm typ def 2021-09-21 10:41:01 +02:00
Bruno Windels
07c6bf7055
Merge pull request #503 from vector-im/bwindels/fix-reactions-vm-null
use mapped value rather than reading parent vm again
2021-09-21 10:09:05 +02:00
Bruno Windels
a020febf8d use mapped value rather than reading parent vm again 2021-09-21 10:02:17 +02:00
Danila Fedorin
a3a743881d Make test adjustments requested in PR. 2021-09-20 19:37:39 -07:00
Danila Fedorin
50c8b995c3 Undo GapWriter algorithm changes 2021-09-20 18:41:01 -07:00
Bruno Windels
0d6881ad22
Merge pull request #502 from vector-im/fix-util-ts-import
Fix util import
2021-09-20 10:19:45 +02:00
Danila Fedorin
9f6c48cf0c Merge branch 'master' into DanilaFe/backfill-changes 2021-09-17 15:19:16 -07:00
Danila Fedorin
6d84cc0a81 Fix util import 2021-09-17 14:51:20 -07:00
Danila Fedorin
820b048272 Finish up the more difficult tests 2021-09-17 10:57:51 -07:00
Bruno Windels
08fe430c4b release v0.2.11 2021-09-17 18:38:30 +02:00
Bruno Windels
4759b44233
Merge pull request #500 from vector-im/bwindels/idb-write-logging
IDB write error logging
2021-09-17 18:37:55 +02:00
Bruno Windels
53a0d29244 example how to get more context for the failure of a write operation 2021-09-17 18:25:28 +02:00
Bruno Windels
f5467a653c fix stores returning the delete promise which isn't returned anymore
I checked these aren't awaited in any js file
2021-09-17 18:24:24 +02:00
Bruno Windels
ad45016b87 process write errors on complete or abort in transaction 2021-09-17 18:23:31 +02:00
Bruno Windels
533b0f40d3 pass write errors in a store to the transaction 2021-09-17 18:21:48 +02:00
Bruno Windels
aeedb948cc pass logger to Storage and make it available in Transaction 2021-09-17 18:19:26 +02:00
Danila Fedorin
82c35355b6 Start translating GapWriter tests to using MockTimeline 2021-09-16 23:54:13 -07:00
Danila Fedorin
7d27b46873 Make the response of TimelineMock look like a room sync response 2021-09-16 23:53:38 -07:00
Danila Fedorin
bcfca9ad9a Make event ID function public 2021-09-16 21:53:56 -07:00
Bruno Windels
cce7606960 release v0.2.10 2021-09-16 22:43:38 +02:00
Bruno Windels
5cf8c3c7d2
Merge pull request #498 from vector-im/bwindels/fix-replies
Fix replies after releasing scroll improvements
2021-09-16 22:43:15 +02:00
Bruno Windels
41089e2806 remove leftover logging 2021-09-16 22:30:03 +02:00
Bruno Windels
c4477a81ea don't create the avatar node when we won't need it 2021-09-16 22:29:48 +02:00
Bruno Windels
f02d52ce4c with !interactive, an array of an array is not a valid Child value
why didn't this fail before?
2021-09-16 22:29:13 +02:00
Bruno Windels
bae8dc7dd7 changes should be true when replyVM is not set and we set an entry 2021-09-16 22:28:19 +02:00
Bruno Windels
ccc5aac025 release v0.2.9 2021-09-16 18:20:25 +02:00
Bruno Windels
356af3ae7c
Merge pull request #497 from vector-im/bwindels/timeline-height-fix
Fix timeline height on mobile
2021-09-16 18:19:58 +02:00
Bruno Windels
e978202f36 add min-height: 0 2021-09-16 17:48:29 +02:00
Bruno Windels
9a9b206bf5
Merge pull request #486 from vector-im/bwindels/fix-scroll-jumps
Fix scroll jumps and loading of gaps not at top of the timeline
2021-09-16 17:14:17 +02:00
Bruno Windels
a62627f6db fix lint warning 2021-09-16 16:39:17 +02:00
Bruno Windels
381a12db20 load 20 entries initially in timeline, otherwise it flickers a bit 2021-09-16 16:34:13 +02:00
Bruno Windels
898d32c6da use quotes in css url path 2021-09-16 16:34:01 +02:00
Bruno Windels
6ec2712eec remove debug logging 2021-09-16 16:33:09 +02:00
Bruno Windels
c9f79343ef remove obsolete comment 2021-09-16 16:32:59 +02:00
Bruno Windels
b1994918fe
Merge pull request #488 from vector-im/bwindels/list-and-timeline-view-to-ts
Convert ListView and TimelineView to typescript
2021-09-16 16:08:34 +02:00
Bruno Windels
a6bcfac597 rename UIView to IView 2021-09-16 15:58:48 +02:00
Bruno Windels
b71a26b04a avoid using ! in ListView 2021-09-16 15:56:57 +02:00
Bruno Windels
81ec8dca8c improve typing in utils 2021-09-16 15:51:45 +02:00
Bruno Windels
d9ddeaf107 fix TS errors in TimelineView 2021-09-16 15:49:03 +02:00
Bruno Windels
00aa40ea7b copy Daniel's conversion of TemplateView to TypeScript from microui
with some minor type adjustments
2021-09-16 15:46:02 +02:00
Bruno Windels
ea4d833a43 reuse ViewNode in Child type 2021-09-16 15:39:25 +02:00
Bruno Windels
040efa970c make className binding always have a value (may be undefined through T) 2021-09-16 15:39:17 +02:00
Bruno Windels
68fb093c9e don't require mount args in mountView, like in UIView interface 2021-09-16 15:23:48 +02:00
Bruno Windels
060f4aa297 change extension in preparation for TS conversion 2021-09-16 14:02:36 +02:00
Bruno Windels
65f69a121b copy Daniel's conversion of BaseUpdateView to TypeScript from microui 2021-09-16 14:01:33 +02:00
Bruno Windels
69672dd99c copy Daniel's conversion of html.js to TypeScript from microui 2021-09-16 10:45:06 +02:00
Bruno Windels
cce8207870 copy Daniel's conversion of EventEmitter to TypeScript from microui 2021-09-16 10:23:03 +02:00
Danila Fedorin
bbd174cd67 Add a class to mock timeline requests 2021-09-15 16:15:18 -07:00
Bruno Windels
2396a84c99 leave out svg editor markup from icon 2021-09-15 18:39:04 +02:00
Bruno Windels
e4101ece65 add "jump down" button in timeline 2021-09-15 18:30:08 +02:00
Bruno Windels
1df12b8c89 only allow pixel gaps of < 1px for stick to bottom to prevent eleweb bug
https://github.com/matrix-org/matrix-react-sdk/pull/6751
2021-09-15 17:23:53 +02:00
Bruno Windels
2c415e37e7 where ResizeObserver is support, restore anchored node on resize 2021-09-15 17:23:28 +02:00
Bruno Windels
04edff29cf give more top padding to gaps that appear in the middle of the timeline 2021-09-15 15:57:31 +02:00
Danila Fedorin
d2b604e1dd Stop using at to fix tests. 2021-09-14 15:57:32 -07:00
Danila Fedorin
df273c5e2c Store more events from backfill 2021-09-14 15:40:15 -07:00
Danila Fedorin
b2b5690739 Add more tests 2021-09-14 13:54:14 -07:00
Danila Fedorin
f8117b6f98 Lift transaction property to QueryTarget 2021-09-14 11:18:24 -07:00
Danila Fedorin
41e568f783 Add more tests and extract common test code 2021-09-14 11:15:13 -07:00
Danila Fedorin
31577cd496 Draft first two tests 2021-09-14 10:24:18 -07:00
Bruno Windels
247d6a2148 add comments 2021-09-14 17:01:30 +02:00
Danila Fedorin
b3df37b0bc Add the beginning of a tests function for GapWriter 2021-09-13 17:01:32 -07:00
Danila Fedorin
713f675f3a Mock IDBKeyRange, too 2021-09-13 17:00:49 -07:00
Danila Fedorin
71694787cd Add an IDBFactory mock parameter 2021-09-13 16:55:55 -07:00
Bruno Windels
feb0cf7e39 fix viewport changing width when timeline starts filling the viewport
otherwise centered tiles like announcementview jump a bit vertically
2021-09-13 15:40:15 +02:00
Bruno Windels
906e5886e1 fix tiny jump when timeline starts filling viewport 2021-09-13 15:39:56 +02:00
Bruno Windels
ecccadb77e avoid requestIdleCallback as it is not supported on Safari and IE11 2021-09-13 14:53:34 +02:00
Bruno Windels
8858cffc55 fallback from scrollBy() to setting scrollTop on IE11 2021-09-13 14:53:08 +02:00
Bruno Windels
02b8b37b4c disable native scroll anchoring as it interferes with our impl 2021-09-13 13:11:25 +02:00
Bruno Windels
5c40b75eab don't override newly requested ranges when retrying loadattop 2021-09-10 15:25:19 +02:00
Bruno Windels
9411e6f065 WIP 2021-09-10 14:47:05 +02:00
Bruno Windels
98cc1e2715 don't try to load more when end of timeline reached 2021-09-10 14:17:40 +02:00
Bruno Windels
fe4bb5db40 remove comment 2021-09-09 17:15:28 +02:00
Bruno Windels
d1242c4b67 make gaps taller 2021-09-09 17:15:06 +02:00
Bruno Windels
d0f122a2be WIP-ish, batch visible range requests, allow setting empty ranges and...
don't try to notify when the tile becomes invisible again,
we don't need it currently
2021-09-09 17:14:16 +02:00
Bruno Windels
98678b991b no need to store visible state on gap tile & don't fill if edge reached 2021-09-08 16:39:46 +02:00
Bruno Windels
ab67ac00b1 restore bottom of timeline initially after attach to DOM
this will also load more items if the viewport isn't filled
2021-09-08 12:06:17 +02:00
Bruno Windels
e89f60bac0 fill gap tiles when they become visible 2021-09-08 12:05:59 +02:00
Bruno Windels
7578bfa3d9 let tiles know when they become visible & load more tiles close to top 2021-09-08 12:05:19 +02:00
Bruno Windels
f4b4638ea8 actually, pass in just the tile instances for expressing the range 2021-09-07 19:10:53 +02:00
Bruno Windels
b3cd2a0e03 express the visible range with EventKeys rather than list indices
This is less ambiguous in case the DOM and the ObservableList would be
out of sync.
2021-09-07 17:48:49 +02:00
Bruno Windels
c78a83d398 restore most bottom tile in VP on any list change
and tell view model visible range so it can load more or fill gaps, ...
2021-09-07 15:17:27 +02:00
Bruno Windels
44ea65dd3e
Merge pull request #458 from MidhunSureshR/fix-457
Opening member-details panel should not open/close grid view
2021-09-07 12:16:24 +02:00
Bruno Windels
ad4ec5f04c convert TimelineView to typescript 2021-09-06 17:51:32 +02:00
Bruno Windels
632d29795a convert ListView to typescript 2021-09-06 17:12:14 +02:00
RMidhunSuresh
df74d0c814 Remove array from logic
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-09-06 17:41:36 +05:30
Bruno Windels
c6b020a9e7 Rename TimelineList to TimelineView for consistency 2021-09-06 13:31:43 +02:00
Bruno Windels
9592d286c2
Merge pull request #460 from vector-im/bwindels/ts-eventkey-direction
Convert EventKey & Direction to typescript + some ts tidbits
2021-09-06 13:11:54 +02:00
Bruno Windels
5953cdf0cc turns out we do write these 2021-09-06 13:10:36 +02:00
Bruno Windels
35f8c40a0c fix rebase mistake 2021-09-06 13:09:16 +02:00
Bruno Windels
ae5a4f0051 start doc with additional steps for migration
once the base migration is done
2021-09-06 13:07:47 +02:00
Bruno Windels
886a60c12a set target version of TS to ES2020 2021-09-06 13:07:47 +02:00
Bruno Windels
7cb686ce8e convert EventKey to ts 2021-09-06 13:07:41 +02:00
Bruno Windels
85c8415acd convert Direction to ts 2021-09-06 13:06:20 +02:00
Bruno Windels
ed082c9869
Merge pull request #475 from vector-im/snowpack-ts-storage-4
Snowpack + Typescript conversion (Part 4)
2021-09-06 13:05:08 +02:00
Bruno Windels
36da02c14e use generics here to say return type of method is same as callback 2021-09-06 13:01:32 +02:00
Bruno Windels
360b4db17a
Merge pull request #474 from vector-im/snowpack-ts-storage-3
Snowpack + Typescript conversion (Part 3)
2021-09-06 12:55:46 +02:00
Bruno Windels
ad28f1f9a3 split out Entry type for TimelineEventStore 2021-09-06 12:51:28 +02:00
Bruno Windels
cd98cac4e4 split out Entry type for OlmSessionStore 2021-09-06 12:46:44 +02:00
Bruno Windels
5d8e66a3f3
Merge pull request #473 from vector-im/snowpack-ts-storage-2
Snowpack + Typescript conversion (Part 2)
2021-09-06 11:06:46 +02:00
RMidhunSuresh
4f4bc8f805 Add test
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-09-06 14:20:33 +05:30
RMidhunSuresh
a54413625e No need to pass in roomId
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-09-06 12:59:30 +05:30
RMidhunSuresh
f841efeef4 Make check more generic
Signed-off-by: RMidhunSuresh <hi@midhun.dev>
2021-09-06 12:41:17 +05:30
Bruno Windels
a0d66a71f7
Merge pull request #485 from vector-im/bwindels/make-ssss-key-settings-less-confusing
put switch button below input so it looks less like the big green lin…
2021-09-01 15:46:46 +02:00
Bruno Windels
6c3a64f020 put switch button below input so it looks less like the big green link is related to the first paragraph 2021-09-01 15:44:14 +02:00
Bruno Windels
27db00ab98
Merge pull request #484 from vector-im/bwindels/fix-483
Ignore redacting reactions on already redacted events
2021-09-01 14:23:13 +02:00
Bruno Windels
6cec583661 fix #483 2021-09-01 14:21:21 +02:00
Bruno Windels
795a57dde1
Merge pull request #481 from jryans/format-key-null
Check for null keys in settings view model
2021-09-01 10:37:01 +02:00
Danila Fedorin
5b9fd5de94 Import BaseLogger instead of explicitly defining 'any' type 2021-08-31 16:01:13 -07:00
Danila Fedorin
78fb8fdadf Make export types more precise 2021-08-31 15:59:50 -07:00
Danila Fedorin
2262e6be30 Use store name enum for saving stores 2021-08-31 15:44:03 -07:00
Danila Fedorin
de3807f690 Fix IDBKeyRange type 2021-08-31 15:35:01 -07:00
Danila Fedorin
4eabb7c074 Fix newly emerging type errors in schema 2021-08-31 15:32:33 -07:00
Danila Fedorin
7f8089eaff Merge branch 'snowpack-ts-storage-3' into snowpack-ts-storage-4 2021-08-31 15:26:26 -07:00
Danila Fedorin
f34ee53a12 Avoid casting to any when a more specific type is possible 2021-08-31 15:14:21 -07:00
Danila Fedorin
bef02d238f Split keys out of stored data types 2021-08-31 15:12:09 -07:00
Danila Fedorin
f5943ced97 Remove IDBValidKey references 2021-08-31 14:48:38 -07:00
Danila Fedorin
1fcc147da7 Add type to the IDBKeyRange field 2021-08-31 12:16:16 -07:00
Danila Fedorin
056c7d40eb Rename RoomEvent to TimelineEvent 2021-08-31 12:10:36 -07:00
Danila Fedorin
16d3ed579b Reduce IDBValidKey returns to the public API 2021-08-31 11:47:54 -07:00
Danila Fedorin
ce20d40ff7 Revert the return-promise change 2021-08-31 11:31:17 -07:00
Danila Fedorin
eb3f5f1ec2 Merge branch 'master' into snowpack-ts-storage-2 2021-08-31 11:13:10 -07:00
J. Ryan Stinnett
3e5cc07440 Move check to fingerprintKey 2021-08-31 17:14:14 +01:00
J. Ryan Stinnett
284ec8a74f Check for null keys in settings view model
The keys to format in the settings view model might be null. This avoid crashing
the UI by testing for this case.
2021-08-31 16:11:33 +01:00
Bruno Windels
0a13c12b1f
Update FAQ.md
mention olm issues when integrating for now
2021-08-31 12:16:59 +00:00
Bruno Windels
cd900ab842
Merge pull request #456 from vector-im/snowpack-ts-storage-1
Snowpack  + Typescript conversion (Part 1)
2021-08-31 08:48:14 +02:00
Bruno Windels
995ed23b3e tell TS we're certain to have a txn 2021-08-31 08:43:39 +02:00
Bruno Windels
3d66d71c97 fix conflict 2021-08-31 08:39:12 +02:00
Bruno Windels
f466266a5f bring back extra caution 2021-08-31 08:16:37 +02:00
Bruno Windels
3ded5b20d3 dedupe some code here 2021-08-31 08:16:27 +02:00
Bruno Windels
8d414970c6 release v0.2.8 2021-08-30 15:23:09 +02:00
Bruno Windels
a8f89c16bd
Merge pull request #472 from vector-im/bwindels/fix-new-join-detect-one-write-phase
Fix missing new joins preventing key shares
2021-08-30 15:17:19 +02:00
Bruno Windels
7fb5412176 keep comment where it was 2021-08-30 15:12:25 +02:00
Bruno Windels
0d6ae19d99 use same code to add room to identity in migration as in device tracker 2021-08-30 15:05:57 +02:00
Bruno Windels
a437332482 whitespace 2021-08-30 14:52:20 +02:00
Bruno Windels
0ca46bf2ac don't log here as we log at a lower level, and don't fail on abort 2021-08-30 14:52:04 +02:00
Bruno Windels
ddb6753f8d fix refactor error 2021-08-27 20:05:53 +02:00
Bruno Windels
6a6762f036 ensure memberwriter works with undefined for state/timeline events array 2021-08-27 20:05:34 +02:00
Bruno Windels
8e6bd6a7a1 add missing room ids to identities for tracked rooms & clear outbound session 2021-08-27 19:39:24 +02:00
Bruno Windels
fa555bedf0 log storage migration 2021-08-27 19:35:11 +02:00
Bruno Windels
0c05e97465 abort upgrade txn on error 2021-08-27 19:07:27 +02:00
Danila Fedorin
9252f3bede Migrate StorageFactory to TypeScript 2021-08-27 09:30:16 -07:00
Danila Fedorin
5db9d1493a Migrate export to TypeScript 2021-08-27 09:30:16 -07:00
Danila Fedorin
04e39ef9e2 Migrate quirks to TypeScript 2021-08-27 09:30:16 -07:00
Danila Fedorin
34b173a057 Migrate schema to TypeScript 2021-08-27 09:30:16 -07:00
Danila Fedorin
eae820f91b Migrate Storage to TypeScript 2021-08-27 09:30:15 -07:00
Danila Fedorin
5f3c9cda97 Migrate Transaction to TypeScript 2021-08-27 09:30:15 -07:00
Danila Fedorin
100aee9dcc Migrate InboundGroupSessionStore to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
914abda7c9 Migrate OlmSessionStore to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
3cd0d1f423 Migrate DeviceIdentityStore to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
8d44df83c4 Migrate UserIdentityStore to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
b46ae152d6 Migrate PendingEventStore.js to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
fad6d63c14 Migrate TimelineFragmentStore.js to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
742ab28099 Migrate AccountDataStore.js to TypeScript. 2021-08-27 09:30:10 -07:00
Danila Fedorin
77f75fd968 Migrate OperationStore to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
8c966627bc Migrate GroupSessionDecryptionStore to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
33d94b9497 Migrate OutboundGroupSessionStore to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
5d4454734a Migrate RoomStateStore.js to TypeScript 2021-08-27 09:30:10 -07:00
Danila Fedorin
af70269169 Merge branch 'snowpack-ts-storage-1' into snowpack-ts-storage-2 2021-08-27 09:29:53 -07:00
Danila Fedorin
4c4687a05f Avoid unsafe (and error-prone) cast 2021-08-27 09:29:02 -07:00
Bruno Windels
a61f052fe8 fix lint 2021-08-27 17:23:32 +02:00
Bruno Windels
826de7e9cb Write all members of a sync in one go
so state member events written but not causing a memberChange.hasJoined
don't prevent timeline member events for the same user from doing so
2021-08-27 17:11:29 +02:00
Danila Fedorin
b081aa171c Merge branch 'snowpack-ts-storage-1' into snowpack-ts-storage-2 2021-08-26 17:08:00 -07:00
Danila Fedorin
904a2cbe74 Merge branch 'master' into snowpack-ts-storage-1 2021-08-26 17:07:25 -07:00
Danila Fedorin
a95704528f Merge branch 'snowpack-ts-storage-1' into snowpack-ts-storage-2 2021-08-26 17:01:23 -07:00
Danila Fedorin
b7d232d56d Remove unnecessary cast and restrict constructor parameter type 2021-08-26 16:56:03 -07:00
Bruno Windels
d0c1ddb51b add failing test 2021-08-26 15:18:31 +02:00
Danila Fedorin
6d6821319e Merge branch 'snowpack-ts-storage-1' into snowpack-ts-storage-2 2021-08-24 11:19:36 -07:00
Danila Fedorin
1707df71df Try to reduce repitition in CursorIterator 2021-08-24 11:11:30 -07:00
Bruno Windels
25d0cbe9a2
Merge pull request #468 from vector-im/bwindels/fixlint
Cleanup: fix lint and remove dead code
2021-08-24 15:35:40 +02:00
Bruno Windels
cb9606a87b remove dead code for incomplete memory store 2021-08-24 15:33:41 +02:00
Bruno Windels
e105bc4237 fix lint warnings 2021-08-24 15:31:18 +02:00
Bruno Windels
3d4f69d048 release v0.2.7 2021-08-23 20:25:47 +02:00
Bruno Windels
b4a3bd1822
Merge pull request #466 from vector-im/bwindels/fix-updating
rename service-worker.js back to sw.js to unbreak updating
2021-08-23 20:25:25 +02:00
Bruno Windels
54ac5a96ca rename service-worker.js back to sw.js to unbreak updating 2021-08-23 20:23:38 +02:00
Bruno Windels
3d6a990c58 release v0.2.6 2021-08-23 20:14:20 +02:00
Bruno Windels
7946a3e4d7
Merge pull request #464 from vector-im/bwindels/well-known
Add .well-known support
2021-08-23 20:13:30 +02:00
Bruno Windels
65bd892d8f better naming 2021-08-23 20:09:45 +02:00
Bruno Windels
d31e442c16 better text 2021-08-23 20:06:52 +02:00
Bruno Windels
d1412e1f42 don't retry with www.{host}, as it's a minor security issue
if www.host gets hacked (e.g. a bad wordpress plugin), it could spread
to the matrix server running on a different host.
2021-08-23 20:05:42 +02:00
Bruno Windels
160ae0b767 adjust to AbortableOperation api change 2021-08-23 19:53:52 +02:00
Bruno Windels
59605a2a57 don't fail login on missing cors on well-known 2021-08-23 19:53:33 +02:00
Bruno Windels
c0d3c950b0 show the homeserver after lookup 2021-08-23 19:29:17 +02:00
Bruno Windels
3dbffdb417 don't requery the same homeserver we just did 2021-08-23 19:28:43 +02:00
Bruno Windels
e0d53e57ba clear everything when typing in homeserver field
as it's not relevant anymore
2021-08-23 19:28:21 +02:00
Bruno Windels
e944dc5cac decrease typing timeout to 1s 2021-08-23 19:27:24 +02:00
Bruno Windels
c7b47bb8d6 fix homeServer misspelling to homeserver across the project 2021-08-23 19:26:39 +02:00
Bruno Windels
3a5b7c1d0e support well-known lookup 2021-08-23 18:47:36 +02:00
Bruno Windels
b4c3a2ea43
Merge pull request #463 from vector-im/bwindels/hs-input-timer
Query homeserver login options 2s after stopping to type, in addition to change event
2021-08-23 14:07:46 +00:00
Bruno Windels
9760a4540e remove debug log 2021-08-23 16:04:00 +02:00
Bruno Windels
993bc36096 dont query 2nd time after losing focus 2021-08-23 16:01:39 +02:00
Bruno Windels
d1301fa642 input is not needed in the name here 2021-08-23 15:57:16 +02:00
Bruno Windels
8eab9ab28b add 2s timeout on input of homeserver to also query the homeserver, in addition to change event 2021-08-23 15:54:40 +02:00
Bruno Windels
577c3168e6 make queryLogin abortable 2021-08-23 15:54:06 +02:00
Bruno Windels
3b693c5b02
Merge pull request #453 from MidhunSureshR/sso-login
[SSO] - [PR 4] - SSO/Token login functionality
2021-08-23 11:38:48 +00:00
RMidhunSuresh
ef4db4abab Make navigation simpler
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 17:00:41 +05:30
RMidhunSuresh
c9319c7c38 Catch any error from queryLogin
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 16:58:54 +05:30
RMidhunSuresh
fc169af10f Rename toggle to set
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 16:09:40 +05:30
RMidhunSuresh
b0db7e0344 More simpler navigation
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 15:55:07 +05:30
RMidhunSuresh
e80667c935 Remove duplicate style
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 15:50:22 +05:30
RMidhunSuresh
69478b81b2 Fix toggleBusy
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 15:48:29 +05:30
RMidhunSuresh
ecfdc314d5 Do not set error message to empty string
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 15:28:44 +05:30
RMidhunSuresh
c9fbafb909 Also check LoadStatus.Error
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 15:12:40 +05:30
RMidhunSuresh
84fd286140 Split logintoken into two words
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-23 15:06:16 +05:30
Danila Fedorin
e610087a79 Merge branch 'snowpack-ts-storage-1' into snowpack-ts-storage-2 2021-08-20 12:51:23 -07:00
Danila Fedorin
a2ff02e6c0 Try using an enum for store names. 2021-08-20 12:33:06 -07:00
Danila Fedorin
69953e5277 Migrate TimelineRelationStore.js to TypeScript 2021-08-20 11:23:27 -07:00
Danila Fedorin
e3b1d034f0 Migrate TimelineEventStore.js to TypeScript 2021-08-20 11:23:21 -07:00
Danila Fedorin
7de704ef86 Migrate InviteStore.js to TypeScript. 2021-08-20 11:23:14 -07:00
Danila Fedorin
7c56ac7746 Migrate RoomMemberStore.js to TypeScript 2021-08-20 11:23:08 -07:00
Danila Fedorin
38a38e8287 Migrate common.ts 2021-08-20 11:23:01 -07:00
Danila Fedorin
e284224cc8 Migrate RoomSummaryStore.js to TypeScript. 2021-08-20 11:22:53 -07:00
Danila Fedorin
144e391c82 Migrate SessionStore.js to TypeScript. 2021-08-20 11:22:45 -07:00
Danila Fedorin
be4ad66b12 Merge branch 'snowpack-ts-storage-1' into snowpack-ts-storage-2-fix 2021-08-20 11:21:03 -07:00
RMidhunSuresh
04806a1425 Convert link to button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 23:16:03 +05:30
Danila Fedorin
0b8acb51a4 Switch errors to using nulls 2021-08-20 10:41:15 -07:00
Danila Fedorin
50b7a8a3fd Add a comment explaining CursorIterator 2021-08-20 10:34:06 -07:00
RMidhunSuresh
d47e126370 add missing emit
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 23:03:51 +05:30
RMidhunSuresh
0e6139d5e3 Use homeserver from login method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 23:02:06 +05:30
RMidhunSuresh
94ba93acb5 Add explaining comment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 22:57:13 +05:30
RMidhunSuresh
0630452571 No need to observe
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 22:53:15 +05:30
Danila Fedorin
94ff76711c Use 'in' to be more idiomatic 2021-08-20 10:04:22 -07:00
RMidhunSuresh
a5985cba2a Add spinner
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 22:18:24 +05:30
RMidhunSuresh
82067ca6f5 No need to pass showError
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 21:43:42 +05:30
RMidhunSuresh
55da58893b Red + bold error
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 21:41:42 +05:30
RMidhunSuresh
a2677a6400 Separate errors for each vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 21:22:12 +05:30
RMidhunSuresh
355468b637 Internationalize + add back old message
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 20:36:33 +05:30
RMidhunSuresh
ff8417dfe2 Set busy state from login vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 20:31:22 +05:30
RMidhunSuresh
784b06d500 Fix emit for isBusy
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 20:12:25 +05:30
RMidhunSuresh
0e7a9e224c Remove unwanted if
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 20:10:31 +05:30
RMidhunSuresh
5ca732341a Rename defaultHomeserver to homeserver
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 20:09:12 +05:30
RMidhunSuresh
c650b35831 resetStatus from within startLogin
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 20:05:36 +05:30
RMidhunSuresh
9482998b15 Internationalize and remove dash
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 19:08:47 +05:30
RMidhunSuresh
2468bc3e9f Remove homeserver prop
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 15:47:30 +05:30
RMidhunSuresh
dadeb7f3e5 Do not override childOptions
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 15:42:43 +05:30
RMidhunSuresh
bdc860eb79 Refactor to pull loadvm into login vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-20 15:19:42 +05:30
Danila Fedorin
19bababa68 Use method syntax in QueryTarget. 2021-08-19 17:40:10 -07:00
Danila Fedorin
704a8d99c7 Add missing return types to QueryTarget 2021-08-19 17:39:53 -07:00
RMidhunSuresh
80ea48e8a1 Move input into t.if
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 21:43:44 +05:30
RMidhunSuresh
5ab405fc30 Move back-button to login view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 21:37:26 +05:30
RMidhunSuresh
3af2ae3bdd make method private
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 20:50:00 +05:30
RMidhunSuresh
ed278e3e5a Remove unwanted check
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 20:50:00 +05:30
RMidhunSuresh
2d842c7174 rename ssoEndpointLink to createSSORedirectURL
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 20:50:00 +05:30
RMidhunSuresh
daf7af17b1 Move logic to vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 20:50:00 +05:30
RMidhunSuresh
068fba3616 Inline method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 20:50:00 +05:30
RMidhunSuresh
738603e890 Rename start to createViewModels
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 20:50:00 +05:30
RMidhunSuresh
17f1d6b16a Remove defaultHomeServer prop
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-19 20:49:54 +05:30
Bruno Windels
a56496ce2b
Merge pull request #461 from vector-im/bwindels/disable-ts-lint
Disable TS lint again on CI, as it will be too harsh for an initial migration
2021-08-18 16:54:27 +00:00
Bruno Windels
c99c5e62ba disable ts lint on CI 2021-08-18 18:52:19 +02:00
Bruno Windels
073743927d only enable promise related rules ... I think? 2021-08-18 18:52:06 +02:00
RMidhunSuresh
10a6aca477 Move homeserver input into LoginView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:05:25 +05:30
RMidhunSuresh
13cb8979ac Check correct login method early
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:05:25 +05:30
RMidhunSuresh
83f4095d88 rename queryLogin to getLoginFlows
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:05:15 +05:30
RMidhunSuresh
d2c94b0d3e Give argument better name
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
db3fd3d1ae Fix test
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
7b9ec5516a Move normalizeHomeserver into session container
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
c4e7dc3b5a Split SSOLoginViewModel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
4b72b64a2e Implement SSOLoginHelper
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
f8b0ef052f Give sso homeserver storage key a better name
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
98f8f04c74 Implement SSO
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
474a4bb19a Remove Login vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
683d2c21eb Use generic language in session load status
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
3fa955e594 Parse token/sso login in loginOptions
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
66f28b90fc Implement token login
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
daeeaa2869 Set and get strings
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
e424293293 Save homeserver before redirecting
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
6c6c4c7dfd Style CompleteSSOLoginView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
93720f6025 Style sso button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
b8f0361157 Split login view into password and sso components
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
cabffd5e3f Move view to correct directory
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
c82af5a0a3 Replace link with button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
ce5fdd465c Remove unnecessary braces
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
0af27fc8dd Move normalizeHomeserver to LoginViewModel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
2c953e361d Remove queryLoginFromInput()
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
2103adfc03 Add view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
4b87887a4f Show completion view on sso segment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
75d71717d8 Show link for SSO login
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
b2613740b8 Add functionality to remove loginToken from URL
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
bed0185186 Support loginToken query in History
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
74f5e30487 Ignore sso segment in url
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
987a83b4cf Add method to redirect to a specific URL
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
3efadcb72c Add method that returns callback url
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
e2d2291d8d Add test
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:33 +05:30
RMidhunSuresh
19664e54be Parse loginToken from query parameter
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:31 +05:30
RMidhunSuresh
18e1c305f5 Allow sso to be a root segment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
f3946fcdf3 Pass log as argument
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
9651817c5b Formatting fix
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
730a6b2d0a Move files to separate directory
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
46b7d9a373 Add explaining comment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
72fb7f679b Add license headers
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
acfe3f30db Make lint happy
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
a53e29767f Rewrite password login to use PasswordLoginMethod
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
20765d9688 Create LoginMethod for password login
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:04 +05:30
RMidhunSuresh
e0fcec9102 Add method to query login types
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 22:04:02 +05:30
Bruno Windels
eb06629385
Merge pull request #459 from vector-im/bwindels/ts-eslint
add lint command for typescript
2021-08-18 16:17:14 +00:00
Bruno Windels
4b5f5ddffa fix ts lint error by making methods abstract 2021-08-18 18:15:03 +02:00
Bruno Windels
e1481f2947 run typescript checks in ci 2021-08-18 17:46:37 +02:00
Bruno Windels
e2cdadc714 don't use any non-ts eslint rules, they don't understand abstract method 2021-08-18 17:42:12 +02:00
Bruno Windels
8fa6f4f9fd add lint command for typescript
and detect missing await
2021-08-18 17:20:31 +02:00
Bruno Windels
02e422f3ac
Merge pull request #448 from vector-im/snowpack-mvp
Snowpack MVP
2021-08-18 13:09:02 +00:00
RMidhunSuresh
2ceaebbd1f Don't find roomId in getter
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 18:17:37 +05:30
RMidhunSuresh
7e177f6953 Modify open-room action to fix bug
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-18 17:43:40 +05:30
Danila Fedorin
db66570d7a Migrate Store.js to TypeScript 2021-08-17 16:33:07 -07:00
Danila Fedorin
c4e8ed8851 Migrate QueryTarget.js to TypeScript 2021-08-17 16:30:05 -07:00
Danila Fedorin
28ee87cd2f Migrate error.js to TypeScript 2021-08-17 16:30:05 -07:00
Danila Fedorin
aa9839ee40 Seemingly fix a bug in utils.ts 2021-08-17 16:30:05 -07:00
Danila Fedorin
cd9fe360a4 Start migrating utils.js to TypeScript 2021-08-17 16:30:05 -07:00
Danila Fedorin
5579c018d1 Migrate common.js to TypeScript
Add initial stab at annotating common

Add missing return types and semicolons
2021-08-17 16:30:05 -07:00
Danila Fedorin
8ec8de67b8 Merge branch 'snowpack-mvp' into snowpack-ts-storage-1 2021-08-17 16:29:47 -07:00
Danila Fedorin
5d764a9f0e Add comment for srcPath 2021-08-17 14:24:10 -07:00
Danila Fedorin
e8dff6d16b Move web-specific files to platform/web 2021-08-17 14:13:27 -07:00
Bruno Windels
b6d0d54241 explicitly name snowpack build ouput dir, and remove it after build 2021-08-17 17:20:48 +02:00
Bruno Windels
522f8d085a exclude the polyfill files as well from being transpiled
as they are not used in the development build
2021-08-17 12:53:36 +02:00
Bruno Windels
ebd177263a disable HMR as we won't be using it any time soon I imagine.
this prevents the HMR code from loading at runtime
2021-08-17 12:53:00 +02:00
Bruno Windels
ce273d8761 make tests run again with typescript support 2021-08-17 12:52:50 +02:00
Danila Fedorin
b8e32a03f8 Invoke snowpack from build script 2021-08-09 13:30:25 -07:00
Bruno Windels
1e05d41294 release v0.2.5 2021-08-07 09:01:14 +02:00
Bruno Windels
dbb9e5363c guard against part types that have not been impl in the view 2021-08-07 09:00:41 +02:00
Bruno Windels
d9d76839c5
Merge pull request #450 from vector-im/hr-fix
Add rendering for horizontal rules
2021-08-07 06:50:23 +00:00
Danila Fedorin
f239199b1b Add rendering for horizontal rules 2021-08-06 15:50:03 -07:00
Bruno Windels
e68df1b324 release v0.2.4 2021-08-06 23:44:52 +02:00
Bruno Windels
2c8e259339 focus composer when replying 2021-08-06 23:43:10 +02:00
Bruno Windels
c3177b06bf
Merge pull request #418 from vector-im/replies
Sending replies
2021-08-06 21:28:42 +00:00
Bruno Windels
9f0c3b9cea await sending a message before clearing composer (this was missing all along) 2021-08-06 23:06:58 +02:00
Danila Fedorin
38bdeae4d4 Mount more source folders 2021-08-06 13:19:37 -07:00
Danila Fedorin
d2f32ca3a5 Run snowpack on 'yarn start' 2021-08-06 13:05:40 -07:00
Danila Fedorin
86242daf3b Add basic typescript configuration 2021-08-06 12:59:18 -07:00
Danila Fedorin
3c5b186e31 Switch BaseEntry to TypeScript 2021-08-06 12:54:06 -07:00
Danila Fedorin
b8c8325292 Copy olm and remove dompurify symlink 2021-08-06 12:46:41 -07:00
Danila Fedorin
31c6aa647b Switch to using snowpack-provided dompurify 2021-08-06 12:39:00 -07:00
Danila Fedorin
f6b2d2cca8 Move index.html to a different place 2021-08-06 12:34:20 -07:00
Danila Fedorin
177dfd339a Add a basic snowpack configuration 2021-08-06 12:17:03 -07:00
Danila Fedorin
973b33c032 Add snowpack and typescript dependencies 2021-08-06 12:15:23 -07:00
Danila Fedorin
053c94b60e Stop passing room to tiles in tests 2021-08-06 11:02:41 -07:00
Danila Fedorin
f8dae8e20c Merge branch 'replies' of github.com:vector-im/hydrogen-web into replies 2021-08-06 10:54:33 -07:00
Danila Fedorin
a8fcf63cf9 Make the close button have a pointer cursor 2021-08-06 10:46:38 -07:00
Danila Fedorin
065b1789be Remove interactive condition on continuation 2021-08-06 10:44:35 -07:00
Danila Fedorin
bf1f288a92 Make RoomViewModel's room public and stop feeding it to tileCreator 2021-08-06 10:40:25 -07:00
Danila Fedorin
4c1aeb342a Add two new tests for replies 2021-08-06 10:35:45 -07:00
Danila Fedorin
8dc80e68a7 Remove out-of-date comment 2021-08-06 10:31:01 -07:00
Danila Fedorin
ac044cb5c2 Rename pending event fields 2021-08-06 10:27:17 -07:00
Danila Fedorin
1207203b00 Prefer relations from encrypted content 2021-08-06 10:22:43 -07:00
Danila Fedorin
5a0bc55e54 Rename reply function in reply.js 2021-08-06 10:16:20 -07:00
Bruno Windels
2a92363317 use disposeTracked 2021-08-06 17:56:02 +02:00
Bruno Windels
3feaf38252 use internalId to compare so we don't have to cache the entry id separately 2021-08-06 17:53:58 +02:00
Bruno Windels
5426908444 add copyright header 2021-08-06 17:49:39 +02:00
Bruno Windels
5675895301
Merge pull request #447 from MidhunSureshR/avatar-memberlist-link
Link to member-panel from avatar
2021-08-06 15:44:57 +00:00
RMidhunSuresh
1ce111c317 Css fix
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 21:07:30 +05:30
RMidhunSuresh
b97b9da14a Link to member-panel from avatar
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 20:51:39 +05:30
Bruno Windels
7d9ccb9a3a
Merge pull request #432 from MidhunSureshR/sync-powerlevels
Member Panel - PR 3 - Update powerlevels from state event
2021-08-06 15:02:21 +00:00
RMidhunSuresh
be57352f71 Move PowerLevels.js into /room
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 19:49:48 +05:30
RMidhunSuresh
1a062f69fd Check state_key is zero-length string
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 19:02:50 +05:30
RMidhunSuresh
d85cf982cc Check if state_key is present
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 17:34:21 +05:30
RMidhunSuresh
187cf5cd28 Use optional chaining
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 17:23:01 +05:30
RMidhunSuresh
2cae7c41d3 Check timeline before state
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 17:15:40 +05:30
RMidhunSuresh
4ddc7c0683 Update powerlevels in afterSync
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 17:15:40 +05:30
RMidhunSuresh
40128c7ff6 Set pl from room response
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 17:15:40 +05:30
RMidhunSuresh
b75760c437 Add event type string
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 17:15:40 +05:30
Bruno Windels
1862e31396
Merge pull request #417 from MidhunSureshR/member-details
Member Panel - PR 2 - UI
2021-08-06 11:18:52 +00:00
RMidhunSuresh
7e8d76a95e Refactor map into switch-case
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 16:21:31 +05:30
RMidhunSuresh
bc8e6237d6 Merge suggestion from Github 2021-08-06 16:05:08 +05:30
R Midhun Suresh
3ff55204f3
No need for lambda here
Co-authored-by: Bruno Windels <bruno@windels.cloud>
2021-08-06 16:04:51 +05:30
RMidhunSuresh
dbd205701c Remove unused prop
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 16:03:19 +05:30
RMidhunSuresh
86ddfc1e9e Only need to emit
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-06 13:06:38 +05:30
R Midhun Suresh
974d9bf199
Don't cache powerlevel value
Co-authored-by: Bruno Windels <bruno@windels.cloud>
2021-08-06 13:02:58 +05:30
Danila Fedorin
21b067eaff Create new tiles instead of keeping old ones 2021-08-05 10:05:50 -07:00
Danila Fedorin
434882069e Lift tiles creator to RoomViewModel 2021-08-05 09:39:59 -07:00
Bruno Windels
73884cff0d
Merge pull request #412 from MidhunSureshR/member
Member panel - PR 1 - Add method to fetch member
2021-08-05 11:13:52 +00:00
RMidhunSuresh
49be4e64ca Remove object
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
fea4143af9 Use texttransform
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
d3ea26a38a Use i8n in role
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
862e8569a9 Describe what changed in emitChange()
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
a16c3aacab Create getter
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
28a0a0830f Copy over all segments after right-panel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
0ff0850979 Remove dm user option
- I don't know why but I thought .../open-room/<user_id> would open an
  invite view to a room with the specified user.
- Does not work like that, so removed.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
d056a9f961 Keep memberlist until rightpanel is closed
- This should make switching between memberlist and
  member-details panel smoother for large rooms

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
16887c3c33 Better formatting
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
448bf3cb3f Style tile
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
4784bcc590 Make the tiles link to details
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
65b1a1958a Add link to open member details
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
7dacbdb700 Show back button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
7530b28130 Improve segment adding logic
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
ef17808fbd Fallback to memberlist if member not available
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
f9b1068bae Add links
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
fe6551ff3c Use powerlevel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
c9224c13a7 Improve code and css
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
c2b9c3f726 Add isEncrypted to vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
7ef7c419e3 Add basic UI and styling
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
acdba8fc02 Add license headers
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
e4bb20575b Create navigation and add to right panel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
fdd4caa0b7 Create vm and view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:30 +05:30
RMidhunSuresh
7e481080b5 Return null
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-05 16:32:06 +05:30
Danila Fedorin
a9731f5a1d Clean up code for inserting emotes 2021-08-04 16:02:37 -07:00
Danila Fedorin
b4a0c31e1c Update test code with new function signature 2021-08-04 15:54:11 -07:00
Danila Fedorin
508214a46b Insert emote text after quotes 2021-08-04 15:50:42 -07:00
Danila Fedorin
b5f16468ce Add a flag to strip replies 2021-08-04 15:31:25 -07:00
Danila Fedorin
06961ff693 Add isReply flag to entries 2021-08-04 15:30:35 -07:00
Danila Fedorin
960e3ec469 Fix unsubscribing from observed events containing null 2021-08-04 11:08:35 -07:00
Danila Fedorin
2375bf061c Strip relates_to from encrypted events' original contents. 2021-08-04 10:26:03 -07:00
Danila Fedorin
fa985f8f16 Blurb isn't really the right word. 2021-08-04 09:30:02 -07:00
Danila Fedorin
611c6e9717 Move replying code into reply.js and add license 2021-08-04 09:26:26 -07:00
Bruno Windels
790724bc2d
Merge pull request #438 from vector-im/bwindels/fix-401
Fix error when back-paginating reaction on event in unloaded fragment
2021-08-04 14:49:04 +00:00
Bruno Windels
0dc5041f47 WIP 2021-08-04 15:23:03 +02:00
Danila Fedorin
1a0e305212 Extract ComposerViewModel to its own file 2021-08-03 14:02:11 -07:00
Danila Fedorin
f0b6384ad7 Rename 'disabled' to 'interactive' in BaseMessageView 2021-08-03 13:27:33 -07:00
Danila Fedorin
8956f6ecf4 Fuse methods and properties related to replies 2021-08-03 13:10:36 -07:00
RMidhunSuresh
c9a4c393ed Return null
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-02 23:51:19 +05:30
RMidhunSuresh
e363d32921 Return null on error
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-02 23:42:55 +05:30
RMidhunSuresh
bb68b4d367 Fetch member data from from state event if needed
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-02 23:01:44 +05:30
Bruno Windels
a9308f69b8
Merge pull request #420 from MidhunSureshR/add-license
Add missing license headers
2021-08-02 15:10:18 +00:00
Bruno Windels
89d9cd3782
Merge pull request #421 from MidhunSureshR/fix-avatar-lint
Fix lint warning
2021-08-02 15:09:31 +00:00
RMidhunSuresh
6cfb4cfbe0 Support state events
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-08-02 17:50:09 +05:30
Danila Fedorin
9bd7d1397c Preserve the m.relates_to field for message. 2021-07-30 14:37:34 -07:00
Danila Fedorin
d4ed146cd7 Add implementation thoughts 2021-07-29 13:40:02 -07:00
Danila Fedorin
99a630fb84 Add a note on TilesCollection and diposing of tiles 2021-07-29 10:46:17 -07:00
Danila
d5f7280249
Merge pull request #424 from moritzdietz/master
docs: Update FAQ to add missing `yarn install` command
2021-07-29 09:36:46 -07:00
Moritz Dietz
9a7cb2fd31
Apply review suggestion 2021-07-29 11:54:04 +02:00
Danila Fedorin
28248722a3 Fix incorrect conditions for showing avatar 2021-07-28 16:17:25 -07:00
Danila Fedorin
e2ad589aa3 Go through and clean up affected files. 2021-07-27 16:51:34 -07:00
Danila Fedorin
fb54ab68a3 Tweak reply style 2021-07-27 15:12:03 -07:00
Moritz Dietz
699e704a49
docs: Update FAQ to add missing yarn install command
Add yarn install to the FAQ docs

As someone who has never worked with either npm or yarn I was not aware that I need to run yarn install as well.
After having a chat in the Hydrogen room I was walked through to help me build hydrogen.

Either you initially meant to say yarn install instead of yarn init or it was assumed that one would run yarn install anyway.
2021-07-27 21:00:47 +02:00
Danila Fedorin
3d911f2a22 Add escaping to replies 2021-07-26 14:49:06 -07:00
Danila Fedorin
753bb8392b Add mx-reply to dompurify's list so we can ignore it ourselves. 2021-07-26 12:21:20 -07:00
Danila Fedorin
305fab467e Send a formatted body from quotes 2021-07-23 16:45:22 -07:00
Danila Fedorin
0db6870edb Flesh out the fallback formatting a bit. 2021-07-23 16:37:23 -07:00
Danila Fedorin
c0d39a5983 Add very rudimentary fallback reply formatting code 2021-07-23 15:34:04 -07:00
Danila Fedorin
73ca5d21ff Add ideas of pending replies 2021-07-23 15:32:37 -07:00
Danila Fedorin
242a9c209b Handle replies in EventEntry 2021-07-23 14:34:11 -07:00
RMidhunSuresh
34f1ecc0dc Remove unused import
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-23 18:10:17 +05:30
RMidhunSuresh
281dc8bdb4 Add missing license headers
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-23 18:05:48 +05:30
Danila Fedorin
711732200e Make the reply box more distinct 2021-07-22 14:44:57 -07:00
Danila Fedorin
b0c5b2f2ce Use the proper tile view to display reply preview 2021-07-22 14:15:18 -07:00
Danila Fedorin
650389538d Add some basic styling 2021-07-22 14:07:13 -07:00
Danila Fedorin
83f7391af3 Adjust CSS to match new class structure 2021-07-22 13:39:40 -07:00
Danila Fedorin
1dcfdfc1d8 Split composer into preview and input 2021-07-22 13:37:35 -07:00
Danila Fedorin
013f187dc2 Avoid inserting li tags outside a list 2021-07-22 12:51:24 -07:00
Danila Fedorin
66f686210f Add a very basic, unstyled view of the message to the composer. 2021-07-22 12:28:24 -07:00
Danila Fedorin
94ae5faa3c Add a disabled flag to message view. 2021-07-22 11:16:34 -07:00
Danila Fedorin
7adb0e5ddc Get rid of intermediate view model 2021-07-22 10:56:30 -07:00
RMidhunSuresh
3aeb0c4d98 Indicate if no member found
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-21 18:34:55 +05:30
Danila Fedorin
d33d55376a Put reply into its own view model.
Otherwise, we re-render the reply message on
every keystroke.
2021-07-20 16:51:36 -07:00
Danila Fedorin
f486bc0e04 Reset reply state after successfully sending a reply 2021-07-20 13:01:04 -07:00
Danila Fedorin
46215b3c51 Add the ability to reply 2021-07-20 12:53:31 -07:00
Danila Fedorin
800b4785d1 Accomodate in_reply_to relation shape 2021-07-20 12:17:44 -07:00
Danila Fedorin
fdcafaf6d3 Add _replyTo field to ComposerViewModel that can be set from a message 2021-07-19 16:10:35 -07:00
RMidhunSuresh
05f05bb577 Add method to get member from storage
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-19 19:36:09 +05:30
Bruno Windels
13932bb480 release v0.2.3 2021-07-18 13:16:51 +02:00
Bruno Windels
934e7e6f71
Merge pull request #410 from vector-im/invite-avatar-fix
Fix error on invites in room list panel.
2021-07-18 11:14:54 +00:00
Danila Fedorin
c9e937c41c Add redundancy to prevent stored nulls from causing errors. 2021-07-17 13:29:20 -07:00
Danila Fedorin
ad453555b9 Avoid storing null for invite avatars. 2021-07-17 13:23:13 -07:00
Bruno Windels
44b25e4157 release v0.2.2 2021-07-16 23:54:34 +02:00
Bruno Windels
b036e48b66 too many .. 2021-07-16 23:54:19 +02:00
Bruno Windels
c062584578 release v0.2.1 2021-07-16 23:48:55 +02:00
Bruno Windels
f2e5e34a7f
Merge pull request #408 from vector-im/formatted-messages
Formatted messages
2021-07-16 21:47:03 +00:00
Bruno Windels
9044f4eebe also for inline code 2021-07-16 23:32:38 +02:00
Bruno Windels
bc69e1cdae make font on code snippets slightly smaller 2021-07-16 23:29:41 +02:00
Danila Fedorin
0c0633fdd4 Fix body.string in BaseMessageTile 2021-07-16 14:28:18 -07:00
Danila Fedorin
fb29913ef0 Add lint fixes 2021-07-16 14:21:39 -07:00
Danila Fedorin
c835dc324e Merge branch 'master' of github.com:vector-im/hydrogen-web into formatted-messages 2021-07-16 14:05:43 -07:00
Danila Fedorin
3e4314e7f3 Update comment based on 'small object' version of code. 2021-07-16 13:53:14 -07:00
Danila Fedorin
c620e9c930 Move away from Object.assign for image rendering. 2021-07-16 13:51:34 -07:00
Danila Fedorin
da48ddec83 Fix == typo 2021-07-16 13:49:33 -07:00
Danila Fedorin
7cfdd4f663 Rename TextTileFormat to BodyFormat 2021-07-16 13:46:25 -07:00
Danila Fedorin
031ce42831 Properly cache message format. 2021-07-16 13:43:40 -07:00
Danila Fedorin
9e1f57a2b1 Switch tiles to using enums and checking format. 2021-07-16 13:36:26 -07:00
Danila Fedorin
1e2945ca54 Add license headers to new files. 2021-07-16 13:12:26 -07:00
Danila Fedorin
996d0cfea8 Remove unneded attribute objects 2021-07-16 13:08:21 -07:00
Danila Fedorin
900ebfe289 Properly pass children to list block 2021-07-16 13:05:47 -07:00
Bruno Windels
da7dff18c6
Merge pull request #395 from MidhunSureshR/memberlist
Add memberlist right-panel to Hydrogen
2021-07-16 20:03:13 +00:00
Danila Fedorin
cdf85edad9 Move pill parsing into deserialize and avoid intermediate objects. 2021-07-16 13:01:33 -07:00
Danila Fedorin
eacc033929 Make code blocks accept non-code tags. 2021-07-16 11:38:41 -07:00
Danila Fedorin
2197d68279 Add fix to prevent pre overflow. 2021-07-16 11:00:54 -07:00
Bruno Windels
9aedc1d526 don't put body parts in span 2021-07-16 19:30:02 +02:00
Bruno Windels
629d58b041 limit code block height 2021-07-16 19:16:15 +02:00
Bruno Windels
2ce6cea4ff not used anymore 2021-07-16 19:16:06 +02:00
Bruno Windels
b5b19abb24 only allow links for the schemas mentioned in the spec 2021-07-16 18:32:40 +02:00
Bruno Windels
fe3bdda05a all of these should be scoped to messageBody 2021-07-16 17:27:24 +02:00
Bruno Windels
1f82aef4ad some header styling in messages 2021-07-16 16:53:33 +02:00
Bruno Windels
47c1737371 make <code> styling like element 2021-07-16 16:28:03 +02:00
Bruno Windels
2159b67ba4 make link styling like element, blue an no underline 2021-07-16 16:27:43 +02:00
Bruno Windels
f012c64fc5 fix pill styling a bit, and open in new tab/noopener 2021-07-16 16:27:22 +02:00
RMidhunSuresh
1a721fe7e8 Import from AvatarView.js
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:13:18 +05:30
RMidhunSuresh
7c9755d1a6 Fix width for smaller screens
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
86bb56ab6f Fix layout of details panel in mobile
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
436e87578e Initialize prop in constructor
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ec4a783759 Fix disambiguator
- Do not disambiguate name on room rejoin

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
66d5f4d1a3 Make code clearer
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
9fdfebf00d Replace get with iterator and remove lambda
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
f366479c16 Fix move bug
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
0bd1d2b291 Improve code
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
829830c0fa Fix lazylist
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
0e0976c7f5 Inline method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
611524cdda Log instead of throwing error
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ec8b6f9dd2 Inline method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
c410aed672 Use flex instead of setting height
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
4946683b23 Use ifView instead of mapView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
694b627309 Inline method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
1f67aa34d3 Move LoadingView.js
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
da733f9f8a Move files to members directory
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
0ac3d3727b Remove TemplateView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
9a3d7e416a Remove top padding of room details view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
4bac98d39a More css fixes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
c4c0e0206f Clear margin on ul
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
fe18b61688 Css fixes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
5c0c59044f Move spinner before the text
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
60793798c7 Subscribe to powerLevels
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
960f2c283e Remove comment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
e406aa6e45 Add jsdoc for powerlevels
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
5873ab63ba Release memberlist after panel is closed
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
5a54be2e2e Style loading view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
be46d870aa Center names
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
c7e12c98b5 Make addPanelIfNeeded more generic
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
b126ba1370 Fix lazy list css
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
f98a8847e3 Implement UX
- Add chevron to member count in room details
- Make some rows in panel buttons
- Add user chrome to right panel
- Style UI

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
21f47f21aa Add chevron to button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
8e55967db3 Create UI to open memberlist from details panel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
72f79e8bef Reduce padding
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
8ee9cb10c2 Move css to layout.css
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
fe4f6d2308 Remove listHasChangedSize
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
c073d4c0d0 Unmount child views correctly
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
2502c4024a Fix broken tests
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
8e39aed4b6 Ensure that power levels are loaded only once
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
14c00f50fc Make loadPowerLevels private
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
22fab3761a Remove timeline reader and only use roomState
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
8a976ef24b Make powerLevels observable
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
d1f465e6cc Replace slice with iterator
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
c539c38699 Account for padding in itemHeight
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
f506cf615b div --> li
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ea0851eb94 Keep memberlist panel open on room/grid change
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
d3a8e95962 Find height of container from DOM
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
5338457ea0 Use normalized index in recreateItem
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
85924aba92 Fix update method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
4cb9adc9ea Remove misleading comment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
9a00143623 Improve comment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
96e2bb0b1a Add explaining comment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
f05574f579 Fix updates
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
452eee6767 Incorporate lazyrender code from element
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ee072343f5 Switch to lazy list
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
0b9f4a5e1d Add LazyListView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
1d5b16395f Export function
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
35f6043d06 Support slice
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
db515d48c1 Inline flatten method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
53fc6a7af9 Check prev name is string
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
11d411c9a9 Add failing test
For empty names not un-disambiguating

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
89e256e563 Return array to prevent fetching again
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ea06d4f88e Eliminate double lookup
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
da1e981948 name changes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ad6122a5b6 Add explaining comment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
41806b5e82 Remove duplication
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ab0a48ab31 rename rightpanel to right-panel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
3bb82e55bf Disambiguator is not async
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
e935423814 Remove duplication in RightPanelViewModel 2021-07-16 17:08:18 +05:30
RMidhunSuresh
bcfd1bd135 Support loading view for all panels
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
d64d07aee5 Move loading view into separate file
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
3fb89a81bc Add some initial styling
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
7139711314 Add avatar to tile
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
86c1550850 Switch to collator for perf reasons
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
3d4ba20150 Put up a temporary loading view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ef40027502 Call disambiguate on update/map
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
bcb46fc54a Make tile vm support disambiguation
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
b8542c6754 Implement name disambiguation
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
dea0cad252 Fix bug in FilteredMap
Caused multiple updates to be triggered.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
404129f60e Settle on MappedMap
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
f41c835e91 Support updater in MappedMap
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
0819dcb29e Use MappedList instead of MappedMap
- MappedList supports an optional updater method

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
b62473ae79 Add update method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
4fcaac3865 Add binding in view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
11eb9c7783 Use set instead of add in memberlist
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
2e8c4563aa Implement set method to support update from value
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
a1e3ff3ed8 Use name instead of displayName
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
a9ff6ab026 Sort memberlist using powerlevel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
3e23392a0e Consider powerlevels in comparator and add tests
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
cb5e598e95 Getter for powerlevel from room
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
f7a6fbd901 Make getUserLevel() public
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
ddb7a16fb8 Make member private
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
7e72d57be7 Make list scrollable
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
abd2c19bf2 Remove unused param
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
a765d7fc7d Create memberlist view from rightpanel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
f3c7ab337d Remove code from session view/vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
46a6cf6adf Add memberlist to navigation
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
1418645e15 Modify CSS to reflect changes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
dfe7385611 Accommodate "rightpanel" navigation segment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
8b01ca502e Create RightPanel from SessionViewModel
- Also remove old methods used in RoomDetails

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
7500bbeaad Modify navigation to work with rightpanel segment
- Ensure that rightpanel does not show in the URL.
- Create an action of details to insert the rightpanel segment.
- Make sure rightpanel can be a child of room.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
b74e4452dd Move rightpanel to separate view and vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
7e6e4ecfab Duplicate navigation from room details
- TODO: Remove duplication

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
RMidhunSuresh
69237fce85 Basic barebones of memberlist view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-16 17:08:18 +05:30
Danila Fedorin
c619eba8cf Add some styling to the tables. 2021-07-15 13:19:54 -07:00
Danila Fedorin
d69b78469c Add table viewing code. 2021-07-15 13:19:54 -07:00
Danila Fedorin
f6f29adacc Add a comment about avatar.js 2021-07-15 13:16:42 -07:00
Danila Fedorin
c13daedcb0 Ignore non-absolute links. 2021-07-15 13:15:56 -07:00
Bruno Windels
7177fc1a03
Merge pull request #409 from vector-im/bwindels/confirm-leave
confirm the leave room
2021-07-15 12:32:12 +00:00
Bruno Windels
fb3c090de2 confirm the leave room 2021-07-15 14:30:16 +02:00
Danila Fedorin
ba7e86ac8d Add blocquote style. 2021-07-14 18:11:09 -07:00
Danila Fedorin
025ab16668 Fix /me body rendering. 2021-07-14 18:10:59 -07:00
Danila Fedorin
038b101ed7 Render matrix.to user links as pills 2021-07-14 17:57:53 -07:00
Danila Fedorin
78d7d556e4 Add blockquote and del elements. 2021-07-14 12:38:55 -07:00
Danila Fedorin
763e1cd5de Add tentative version of linkification. 2021-07-13 16:56:46 -07:00
Danila Fedorin
0c05ff459c Add another test. 2021-07-13 15:14:03 -07:00
Danila Fedorin
5e39eb8f6c Do some additional validation, blocking block nodes inside inline nodes. 2021-07-13 14:47:10 -07:00
Danila Fedorin
4b92903ddd Add a test for auto-closing tags. 2021-07-12 15:16:04 -07:00
Danila Fedorin
4022c6121b Switch to DOMPurify in platform layer for sanitization. 2021-07-12 15:00:16 -07:00
Danila Fedorin
012ef2b215 Validate w/h and avoid use of properties object. 2021-07-12 13:42:39 -07:00
Danila Fedorin
1e9cdbafd4 Move test-specific code into the testing function. 2021-07-12 13:31:33 -07:00
Danila Fedorin
fd17ffecfb Pass parameters to parseHTMLBody directly instead of via object. 2021-07-12 12:55:33 -07:00
Danila Fedorin
c261b9fb23 Avoid intermediate objects while parsing TextTiles' bodies. 2021-07-12 12:47:22 -07:00
Danila Fedorin
9304ca330e Rewrite deserialize as a class to avoid passing options around. 2021-07-12 12:19:51 -07:00
Danila Fedorin
abd62373e5 Remove copy of MediaRepository mxcUrl parsing. 2021-07-09 13:09:19 -07:00
Danila Fedorin
1435c2c9a2 Add HTML sanitization to domain layer. 2021-07-09 13:06:24 -07:00
Danila Fedorin
695bdec9fe Match the thumbnail width and height in in-message images. 2021-07-08 14:41:05 -07:00
Danila Fedorin
b3dabc2054 Limit image size 2021-07-08 00:20:35 -07:00
Danila Fedorin
d38c52d311 Display images in messages 2021-07-08 00:16:58 -07:00
Danila Fedorin
c582b723bd Start working on images. 2021-07-07 23:40:16 -07:00
Danila Fedorin
1a14719483 Wrap DOM iterables in array. 2021-07-07 14:12:24 -07:00
Danila Fedorin
92e82a76c7 Add basic styling to code block 2021-07-07 13:05:24 -07:00
Danila Fedorin
e02163b0ff Properly display ul in message bodies. 2021-07-07 13:05:10 -07:00
Danila Fedorin
eca5308742 Move deserialization into domain and fix tests. 2021-07-07 12:21:10 -07:00
Danila Fedorin
41d82c4cdd Add some deserialization tests. 2021-07-06 19:11:05 -07:00
Danila Fedorin
e08aa7df88 Add HTML parser library for tests. 2021-07-06 19:10:36 -07:00
Danila Fedorin
49df21e465 Display lists. 2021-07-06 16:48:31 -07:00
Danila Fedorin
1da35be148 Better handle list start attribute. 2021-07-06 16:14:35 -07:00
Danila Fedorin
e56b15237e Parse formatted message bodies in addition to plain ones. 2021-07-06 16:14:04 -07:00
Bruno Windels
f515407491
Merge pull request #404 from MidhunSureshR/fix-402
Handle avatar image error
2021-07-05 07:48:48 +00:00
Danila Fedorin
cfa686867c Make parse a function that returns a MessageBody 2021-07-02 18:20:07 -07:00
Danila Fedorin
6ba2deffef Make small tweaks to formatted representation. 2021-07-02 18:19:40 -07:00
Danila Fedorin
005580f385 Move HTML parsing into platform. 2021-07-02 15:05:50 -07:00
Danila Fedorin
8f44cc21db Correct two uses of ==. 2021-07-02 00:41:28 -07:00
Danila Fedorin
824e66a62f Add some comments. 2021-07-02 00:23:59 -07:00
Danila Fedorin
94f6c99ea6 Begin a parser implementation from HTML into an internal representation. 2021-07-02 00:18:37 -07:00
Danila Fedorin
db202b23ae Add some prototype rendering implementations. 2021-07-01 13:55:01 -07:00
Danila Fedorin
cc506756a1 Add code tag to HTML 2021-07-01 13:55:01 -07:00
Danila Fedorin
4ee15005f5 Inline code blocks don't contain other elements 2021-07-01 13:55:01 -07:00
Danila Fedorin
fd12baae3b Add some other message parts as demo. 2021-07-01 13:55:01 -07:00
Danila Fedorin
ad868818c7 Add some initial thoughts on the implementation. 2021-07-01 13:55:01 -07:00
RMidhunSuresh
191613adbe Make changes
- use textContent where possible
- make sure we have an image before adding title

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-01 19:21:54 +05:30
RMidhunSuresh
b8c01272f4 remove listener on dispose
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-01 15:42:07 +05:30
RMidhunSuresh
93e77a3fcd Only add attribute if we have avatar
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-01 15:41:40 +05:30
RMidhunSuresh
03a913629f Pass color as data attribute
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-01 15:25:28 +05:30
Bruno Windels
71d0124146
Merge pull request #405 from DanilaFe/dm-color-fix
Ensure room color in DM, timeline, and left panel is the same.
2021-07-01 08:03:26 +00:00
Danila Fedorin
f7d6569154 Add a small comment to Invite.avatarColorId, too. 2021-06-30 15:21:11 -07:00
Danila Fedorin
b40f946b85 Add JSDoc to new Hero method 2021-06-30 15:15:08 -07:00
Danila Fedorin
ee1f1500e9 Roll back to using heroes for computing DM color 2021-06-30 15:07:40 -07:00
RMidhunSuresh
9ed6cd57f3 use textContent
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-07-01 00:01:38 +05:30
RMidhunSuresh
168b1d6247 Move AvatarView to separate file
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-30 23:51:14 +05:30
RMidhunSuresh
b469c4299f implement new approach
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-30 23:30:44 +05:30
RMidhunSuresh
bcaf84e545 Revert commits
This reverts commit 5f1346568d.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-30 23:28:13 +05:30
Danila Fedorin
d0f70cbdf9 Move avatar color ID computation into SummaryData 2021-06-29 18:01:08 -07:00
Danila Fedorin
dec0683145 Correctly color archived and invited rooms 2021-06-29 16:50:42 -07:00
RMidhunSuresh
b42f7e1a36 remove both handlers
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-29 19:50:37 +05:30
RMidhunSuresh
8b6ff533e8 Add and remove opposing event listeners
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-29 15:51:11 +05:30
Danila Fedorin
6527a0c677 Make the room color match up on the left panel, too. 2021-06-28 11:54:49 -07:00
Danila Fedorin
97854423c4 Ensure DM rooms have the same color in timeline and left panel. 2021-06-28 11:44:27 -07:00
RMidhunSuresh
5f1346568d Handle avatar error
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-28 23:18:07 +05:30
Bruno Windels
ce647e78ce release v0.2.0 2021-06-24 17:17:01 +02:00
Bruno Windels
80fff87950
Merge pull request #365 from MidhunSureshR/room-info
Add right panel with Room information
2021-06-24 15:08:34 +00:00
Bruno Windels
09aba78803
Merge branch 'master' into room-info 2021-06-24 15:06:37 +00:00
Bruno Windels
dbaef5117a
Merge pull request #384 from vector-im/bwindels/reactions
Reactions
2021-06-24 15:01:44 +00:00
Bruno Windels
eee1be1ceb safari doesn't like empty string key paths 🙄 2021-06-24 16:16:15 +02:00
Bruno Windels
bb6417dab9 fix lint 2021-06-24 15:24:53 +02:00
Bruno Windels
5984e8dd6d don't show reactions for redacted messages 2021-06-24 14:49:43 +02:00
Bruno Windels
787308375c prevent toggling in vm while already busy
otherwise the check in SendQueue to prevent duplicates might fail
2021-06-24 14:33:16 +02:00
Bruno Windels
575f3fa966 fix tests 2021-06-24 14:28:10 +02:00
Bruno Windels
299294daff prevent re(d)action in left/kicked room 2021-06-24 14:24:22 +02:00
Bruno Windels
3fa0f234bb not used 2021-06-24 14:12:55 +02:00
Bruno Windels
20ae21ead5 add some more emoji fonts that might be install by default 2021-06-24 14:12:41 +02:00
Bruno Windels
366d3761b8 remove waiting for update event (it might not come in case of dupe)
also remove duplicate logging impl for re(d)action at cost of
double haveAnnotation call
2021-06-24 13:35:59 +02:00
Bruno Windels
b148368d5b test different keys do work still 2021-06-24 13:29:13 +02:00
Bruno Windels
7557e2f437 not used 2021-06-24 13:26:14 +02:00
Bruno Windels
668c0aff36 drop duplicate reactions in send queue, as last measure of defence 2021-06-24 13:25:58 +02:00
Bruno Windels
38b465cb9d rename vm.toggleReaction to vm.toggle 2021-06-24 13:15:20 +02:00
Bruno Windels
c46c330efb prevent duplicate redactions from distorting reaction local echo 2021-06-24 13:14:54 +02:00
Bruno Windels
061f44f475 extract methods here 2021-06-24 12:56:23 +02:00
Bruno Windels
a4a7c23148 use pending re(d)action timestamp to have stable reaction sorting order
also move more logic into the matrix layer, from Reaction(s)ViewModel
to PendingAnnotation
2021-06-24 12:26:38 +02:00
RMidhunSuresh
eb35f46214 Make sure room does exist before creating vm
- This will stop the code from throwing when opening /details on
  UnknownRoomView.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-24 14:58:02 +05:30
RMidhunSuresh
a273b25bac Remove css assumption
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-24 14:52:48 +05:30
RMidhunSuresh
e2fd90bdc2 Remove Segment import
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-24 13:48:53 +05:30
Bruno Windels
197c247044
Merge pull request #398 from MidhunSureshR/fix-397
Support removing display names in RoomMemberTile
2021-06-24 07:27:22 +00:00
Bruno Windels
52957beb82 don't encrypt reactions 2021-06-23 17:49:27 +02:00
Bruno Windels
3c7ccc90b2 fix css for reaction view and do some renaming
also add some user-select:none
2021-06-23 17:48:21 +02:00
Bruno Windels
c585d76ce5 also clear pending reaction promise when an error is thrown 2021-06-23 17:47:47 +02:00
Bruno Windels
1a5a64864a don't double log redactReaction 2021-06-23 17:47:18 +02:00
Bruno Windels
e125599a47 prevent decryption result getting lost after reaction updates entry 2021-06-23 17:38:52 +02:00
Bruno Windels
48588687a5 share logic whether have reacted already between basemsgtile & reactvm 2021-06-23 15:38:12 +02:00
Bruno Windels
a1d24894eb this will block if we have a pending redaction & reaction
so the reaction won't be aborted
2021-06-23 11:45:24 +02:00
Bruno Windels
442d4cce03 make the react/redactReaction promise only return after update happened 2021-06-23 11:44:53 +02:00
Bruno Windels
18562d30d8 integration tests for local echo of toggling reactions 2021-06-23 11:43:14 +02:00
Bruno Windels
4d19f8d21d this should return any promise returned, otherwise breaks tests 2021-06-23 11:42:32 +02:00
Bruno Windels
8991632105 add redaction mock utility fn 2021-06-23 11:42:16 +02:00
Bruno Windels
b153613200 determine toggle state correctly with both pending redaction & reaction 2021-06-23 11:41:28 +02:00
Bruno Windels
616d701ebb add test that redaction for non-sending event aborts it 2021-06-21 19:02:42 +02:00
Bruno Windels
0e750db9ae write unit tests for (re)aggregating annotations in RelationWriter 2021-06-21 18:16:21 +02:00
Bruno Windels
d1345d0f83 write test for redaction in RelationWriter 2021-06-21 17:52:02 +02:00
Bruno Windels
81f06f565e write tests for AsyncMappedList 2021-06-21 17:26:08 +02:00
RMidhunSuresh
12305be06a Fix issue #397
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-19 16:01:02 +05:30
Bruno Windels
1fc1d2c79b fix lint 2021-06-18 15:09:34 +02:00
Bruno Windels
11fba12083 add tests for remote reaction target being added after pending event 2021-06-18 15:09:14 +02:00
Bruno Windels
0703cf8915 cleanup 2021-06-18 15:06:49 +02:00
Bruno Windels
5bea8130f2 more timeline annotation tests 2021-06-18 14:39:54 +02:00
Bruno Windels
9f99cf4b1e fix lint in tests 2021-06-18 11:52:09 +02:00
Bruno Windels
8092713faa add tests for local echo of adding and removing reaction 2021-06-18 11:51:02 +02:00
Bruno Windels
f000e98619 no point in reacting to redacted messages 2021-06-17 16:48:58 +02:00
Bruno Windels
64f1abdfed show quick reactions in message menu 2021-06-17 16:46:06 +02:00
Bruno Windels
4312610e7d support menu options with custom DOM 2021-06-17 16:45:53 +02:00
Bruno Windels
70d64f38eb spelling 2021-06-17 16:07:32 +02:00
Bruno Windels
cad884aa41 fix local redaction echo while already sending target 2021-06-17 16:07:09 +02:00
Bruno Windels
a77ef02677 cleanup 2021-06-17 10:12:45 +02:00
Bruno Windels
cbee498d41 a bit more brief 2021-06-17 10:03:32 +02:00
Bruno Windels
bf84b59e39 more accurate test name and also test >= 2021-06-17 09:59:24 +02:00
Bruno Windels
099f99a96b check power levels to see if we can react 2021-06-17 09:41:25 +02:00
Bruno Windels
fd54539e1c clarify comment 2021-06-17 09:41:10 +02:00
Bruno Windels
150f58a6b3 don't aggregate relations on redacted events 2021-06-16 18:00:50 +02:00
Bruno Windels
ce5409dc26 aggregate relations when seeing event target during back-pagination 2021-06-16 17:40:29 +02:00
Bruno Windels
9099a76f45 fix spelling in comment 2021-06-16 17:30:48 +02:00
Bruno Windels
bbcf0d2572 more local echo fixes for redacting a reaction + cleanup 2021-06-16 12:46:44 +02:00
Bruno Windels
94635a18e0 actually, 0 or -1 mean you have a local redaction 2021-06-16 12:41:42 +02:00
Bruno Windels
4f10174e48 clarify comment 2021-06-16 10:28:17 +02:00
Bruno Windels
3b629622d9 need to keep pending count around if 0 or less for redaction local echo
also need to be able to tell the difference between no pending reactions
and redactions and the sum being 0 (having both a redaction and
reaction) so we keep isPending to true
2021-06-16 10:23:22 +02:00
Bruno Windels
e5c1094153 WIP 2021-06-15 19:06:41 +02:00
RMidhunSuresh
e161f61319 Remove selector list
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-15 15:11:32 +05:30
RMidhunSuresh
5e4db2f5dc Change font size
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-15 14:52:07 +05:30
RMidhunSuresh
1772fc04f9 Remove text(..)
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-15 14:45:46 +05:30
RMidhunSuresh
4f05d9a5b7 Make navigation changes in one go
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-15 14:34:26 +05:30
RMidhunSuresh
97e484b8e6 Rename toggle --> open
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-15 14:30:27 +05:30
RMidhunSuresh
e50b503897 Undo lint config change
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-14 22:43:42 +05:30
RMidhunSuresh
88a1e34987 Unsubscribe on dispose
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-14 21:04:44 +05:30
RMidhunSuresh
7b811aa927 Remove space after brace in two places
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-14 20:33:32 +05:30
RMidhunSuresh
e10b455b27 Rename method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-14 20:32:05 +05:30
Bruno Windels
75ee509361 fix lint 2021-06-11 11:30:11 +02:00
Bruno Windels
6bdbbee83e undo forced offline mode 2021-06-11 11:05:34 +02:00
Bruno Windels
81a721f880 make equality stable in comparator for reaction 2021-06-11 11:04:48 +02:00
Bruno Windels
1d9709d4e3 also compare by key if the timestamps are the same 2021-06-11 11:02:31 +02:00
Bruno Windels
757e08c62c WIP 4 2021-06-10 18:29:10 +02:00
Bruno Windels
cb051ad161 WIP3 2021-06-09 16:52:30 +02:00
RMidhunSuresh
e2443a8b09 Undo spillover from earlier formatting havoc
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
b2f2d51594 Make formatting consistent with the codebase
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
0c5d118bfd Add rule to warn on wrong formatting
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
dd9a19b7f0 Make formatting consistent
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
4005d70bb9 Increase height to make element more centered
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
fa67c5e248 Refactor function
- Move related parameters closer together.
- Remove unused parameter.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
95512b5144 Remove margin from top
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
6086d157e2 Make value multiple of 4
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
2c0176f3f2 Remove unwanted width
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
f3f1436bb0 Rename room info to room details everywhere
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
f917730a21 Stick to naming convention for css/html classes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
efd37d811f Update on changes to vm
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
008f3601ca Remove link support from Menu
- Not needed anymore since every link item has been rewritten as a
  button.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
332fbdda84 Move variable to scope of conditional
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
7fbcf89539 Remove unncessary empty line
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
eb870cfc23 Use url instead of link
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
fcc2afba08 Do not compute link in getter
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
04065847dc Remove unused getter
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
c6f3b1fbbe Do not use private props from room summary
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
4700009c68 Add new test for parseUrlPath
Make sure that the details panel stays open during room change.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
4c962943b2 Modify setters in MenuOption to support chaining
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
0d63ce9a3a Change SideBarActive --> right-shown
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
5b74038912 Check value of details everywhere
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
b2448e1207 Calculate path when button is clicked
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
37e052c061 details do not need to be child of rooms
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
a4bcb21a85 Move details handling to separate function
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
7f922afe79 Reduce duplication in code
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
04933acbfb Add missing checks
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
a3587a80c6 Use internationalization for user facing strings
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
986f04aac0 Move Room details menu entry to top
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
9074caf443 Change Room Details --> Room details
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
65bd74442f Make menu-items uniform
- Same font ,font-size and height.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
98ef6f4321 Use optionWithButton
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
a3271fb916 Fix Menu: Use t instead of tag to get events
- Also move constructor up

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
8d254c91e3 Undo formatting/stylistic changes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
3273682155 Do not show room id instead of canonical alias
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
d3d65d89ba Put sidebar rows into container
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
f1fe17fe65 Make font-size equal to that of room-header
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
040c744b6a Style links and buttons similarly in menu
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
00dbd3db06 Add menu entry to launch RoomInfoView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
1e96b58f85 Add link support to menu
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
426d0779ee Keep RoomInfoView open across room/grid changes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
02d79b52a4 Prefer canonical alias over room_id if available
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
2dcec6343d Info icon should open/close view alternatively
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
d782c9e0bb Justify content to flex-end instead
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
eab6ca3baf Make side-bar responsive
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
9ac415fa4f Show panel when first visit contains /details
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
b5480b018b Style open button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
4edc58ebcf Add button in Room header to open details
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
ecd5505af9 Bring in info icon
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
37367cde65 Make close button close the view
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
20a250dfc0 Style close button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
c65e26ec4c Position the close button to right end
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
91f083a245 Add close button to RoomInfoView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
b0535b5d7d Add styling for RoomInfoView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
c96abc8892 Add css to place items appropriately
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
f42553f8cb Add avatar size for design
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
ee29d7f799 Bring in icon assets
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
653fcbbb1f Add method to create avatar section
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
0a4f8aff79 Create method to add sidebar element
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
a23e2c361a Better styling
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
d502a7f911 Make display name bold
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
aece82dff6 Make room avatar larger in RoomInfoView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
6f1b77b6fa Add room avatar to RoomInfoView
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
0ea2843454 Add background for sidebar
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
191eb09a76 Fallback to canonical alias if no names found
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
0d11f85ab3 Add CSS to display sidebar to the right
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
9a605cc6c6 Remove unnecessary check
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
c3333f5fe8 Extract method
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
ddaa9b46c0 Dispose vm preemptively
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
7e38c3ea88 Remove right panel on grid update if needed
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
98d8d44695 Allow details to be child of rooms
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
439910f6ca Handle case when path.get() may be null
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
96959a3c4c Put name of property in emitChange
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
e39572b98b Create RoomInformationView on toggle
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
986271d02a Add code to toggle RoomInformation panel
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
c7fd0fac07 Allow details to be child of room
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
c7ba472042 Add view and vm for RoomInformation
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-09 16:51:18 +05:30
RMidhunSuresh
9e9099f5d0 Restructure and add syntax
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-08 23:05:33 +05:30
RMidhunSuresh
4d79279f42 Add some content
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-06-08 21:13:52 +05:30
Bruno Windels
e4a1c99615 release v0.1.57 2021-06-08 17:35:17 +02:00
Bruno Windels
670d9a5e66
Merge pull request #391 from vector-im/bwindels/update-olm
update olm to 3.2.3
2021-06-08 15:32:05 +00:00
Bruno Windels
0685fa2622 update olm to 3.2.3 2021-06-08 17:23:39 +02:00
Bruno Windels
206d18f498 WIP2 2021-06-08 16:56:17 +02:00
Bruno Windels
2ebadb36c3 WIP 2021-06-08 13:20:55 +02:00
Bruno Windels
c3848ff56b
Update FAQ.md 2021-06-08 07:12:10 +00:00
Bruno Windels
280de98858 fix lint 2021-06-04 16:41:37 +02:00
Bruno Windels
3e47877645 Merge branch 'master' into bwindels/reactions 2021-06-04 16:33:10 +02:00
Bruno Windels
47e74bd598 add glow animation for pending reactions 2021-06-04 16:32:35 +02:00
Bruno Windels
33655ee37e forgot to export class 2021-06-04 16:32:03 +02:00
Bruno Windels
65eb1f7b7d
Merge pull request #388 from vector-im/bwindels/dontassumelocalentriesexists
Prevent 2 more small races between room sync and openTimeline
2021-06-04 14:31:01 +00:00
Bruno Windels
7691b28503 prevent another race between sync and openTimeline 2021-06-04 16:28:08 +02:00
Bruno Windels
bb6905bdcd don't assume localEntries exists, as load races with sync.afterSync 2021-06-04 16:05:28 +02:00
Bruno Windels
23321b4c76
Merge pull request #387 from vector-im/bwindels/dontassumecontaineratload
Don't assume container node exists when loading bundle
2021-06-04 13:37:43 +00:00
Bruno Windels
919542f8fc Don't assume container node exists when loading bundle
Only look for the container node when needed
2021-06-04 15:36:01 +02:00
Bruno Windels
b7402ce43c support local echo for adding a reaction 2021-06-04 15:34:44 +02:00
Bruno Windels
8bf160dfc0 handle sending relations to events that haven't been sent yet 2021-06-04 10:48:59 +02:00
Bruno Windels
2878208e94 keep the DOM small, avoid a node for reactions on every message 2021-06-04 10:08:07 +02:00
Bruno Windels
4ef5afc011 this is actually not used, so remove it 2021-06-04 10:07:52 +02:00
Bruno Windels
dde26da5a6 add mountView utility to handle error handling on mount
and use it where errorToDOM is used currently for catching mount errors
2021-06-04 10:07:12 +02:00
Bruno Windels
ff370d03db catch errors thrown by childview mount method on add in ListView 2021-06-04 09:37:36 +02:00
Bruno Windels
05cc1f6757 make reactions look like element 2021-06-03 21:01:59 +02:00
Bruno Windels
d91282a767 render reactions in div instead of ul 2021-06-03 21:01:47 +02:00
Bruno Windels
3e2b7ba5fa obsolete, already provided in parent class 2021-06-03 21:01:26 +02:00
Bruno Windels
1385a22e60 don't recreate the reactions after clearing it with the last one removed 2021-06-03 21:00:57 +02:00
Bruno Windels
cc444fa207 we actually don't need any of the view model infrastructure
all the updates go over the observable list
2021-06-03 21:00:25 +02:00
Bruno Windels
eab3c2d6dd update relation notes 2021-06-03 21:00:09 +02:00
Bruno Windels
8d4d9c6e8d WIP 2021-06-03 19:57:48 +02:00
Bruno Windels
2eb2e4e9b3 more stable sorting order for reactions 2021-06-03 19:57:29 +02:00
Bruno Windels
bb8acbefa3 support undoing a reaction 2021-06-03 19:57:16 +02:00
Bruno Windels
20abb01ee8 very basic way of sending a reaction 2021-06-03 19:16:53 +02:00
Bruno Windels
b722691e85 show reactions as ListView of buttons if present 2021-06-03 19:16:19 +02:00
Bruno Windels
2152d5e833 expose reactions on base message tile as vm with observable list 2021-06-03 19:15:49 +02:00
Bruno Windels
b94ab42c90 delete annotations object when no more annotations left 2021-06-03 19:10:29 +02:00
Bruno Windels
b05345ee27 only show redacted messages 2021-06-03 16:50:37 +02:00
Bruno Windels
a78e9af8fc Support (de)aggregating annotation relations in relation writer
When deaggregating on redacting an annotation relation, we remove the
relation and aggregate the other relations for that key again, so we can
reliably detect the first timestamp (and count and me as well to lesser
extent).

as a consequence, more than one event can get updated when redacting a
relation (the relation is updated, as well as the relation target), so
account for that by returning an array of entries that have updated.
2021-06-03 16:50:37 +02:00
Bruno Windels
41fb30c68b add relations store 2021-06-03 16:50:37 +02:00
Bruno Windels
36a35d92f0 pass ownUserId to RelationWriter
We'll need to to aggregate whether we have reacted to a message

Create writers at room level and pass subwriter is dependency, rather
than creating them in sync and gap writer.
2021-06-03 16:50:37 +02:00
Bruno Windels
46bfab3eb7 fix some lint and comment 2021-06-03 16:50:11 +02:00
Bruno Windels
934839574e
Update FAQ.md 2021-06-03 10:16:12 +00:00
Bruno Windels
09cfd2a10a
Update FAQ.md 2021-06-03 10:15:35 +00:00
Bruno Windels
ecde6ed919
Update FAQ.md 2021-06-03 10:14:58 +00:00
Bruno Windels
eec7ceb765
remove not being able to leave rooms 2021-06-03 10:13:37 +00:00
Bruno Windels
a39d26a3e0
clarify browser support 2021-06-03 10:11:50 +00:00
Bruno Windels
41b4c31a7f
Merge pull request #381 from vector-im/bwindels/fix-fail-to-load-at-top-with-zero-entries
Don't assume there is at least 1 tile before loading at top
2021-06-03 07:27:49 +00:00
Bruno Windels
b83613924c don't assume there is at least 1 tile before loading at top
it can happen that all tiles are not renderable, and we should just
keep calling loadAtTop
2021-06-03 09:25:56 +02:00
Bruno Windels
711b5be07f show number of matches in logviewer and don't hide expanded sibling
fixes https://github.com/vector-im/hydrogen-web/issues/378
2021-06-02 21:25:46 +02:00
Bruno Windels
5562e7cd74 release v0.1.56 2021-06-02 19:08:56 +02:00
Bruno Windels
fef0832074
Merge pull request #377 from vector-im/bwindels/fix-sync-opentimeline-race
Fix race between sync and subscribing to timeline after opening it
2021-06-02 16:48:13 +00:00
Bruno Windels
d965d57be7 don't leak timeline when an error is thrown while opening it
or you are just stuck with "not dealing with race" errors until refresh
2021-06-02 18:44:03 +02:00
Bruno Windels
0c4c018ceb add note that powerlevels won't update when the state event is changed 2021-06-02 18:43:47 +02:00
Bruno Windels
d2f5b412ac don't try to hook up local relations for events that are not relations
and do unnecessary work
2021-06-02 18:43:16 +02:00
Bruno Windels
365bd5cad0 fix the race 2021-06-02 18:42:46 +02:00
Bruno Windels
84ecaa2ee1 don't trip over missing create events when loading power levels
as the test from previous commit fails because of that, and powerlevels
works fine without
2021-06-02 18:41:52 +02:00
Bruno Windels
4a8a6168cd add failing test for race between sync & subscribing after openTimeline 2021-06-02 18:41:03 +02:00
Bruno Windels
d8acf63e1d change mock event api a bit to be easier to read 2021-06-02 18:38:16 +02:00
Bruno Windels
563847bba9
Merge pull request #376 from vector-im/bwindels/filter-and-highlight-logviewer
add highlight and filter support to logviewer
2021-06-02 15:44:51 +00:00
Bruno Windels
88a45470ba add highlight and filter support to logviewer 2021-06-02 17:10:56 +02:00
Bruno Windels
09bf0f3b4e fix status styling for text messages being broken
as binding was put at argument level rather than className level
2021-06-02 15:25:20 +02:00
Bruno Windels
1bb61d02de
Merge pull request #375 from vector-im/bwindels/fix-open-encrypted-room-inactive-txn
Load power levels before decryption closes the txn
2021-06-02 13:08:28 +00:00
Bruno Windels
31075d601f load power levels before decryption closes the txn 2021-06-02 15:06:30 +02:00
Bruno Windels
2194c1b8bd release v0.1.55 2021-06-02 13:14:31 +02:00
Bruno Windels
928d3b5eda
Merge pull request #374 from vector-im/bwindels/fix-sideeffects-stripped
allow side-effects while bundling at module level in src/ dir
2021-06-02 11:14:06 +00:00
Bruno Windels
d5e8481e22 allow side-effects at module level in src/ dir
otherwise things like src/observable/index.js break
2021-06-02 13:12:12 +02:00
Bruno Windels
d9de28b2e7 release v0.1.54 2021-06-02 12:47:59 +02:00
Bruno Windels
aa2e1aad19
Merge pull request #373 from vector-im/bwindels/fix-send-sync-race
Fix race between /send and /sync
2021-06-02 10:41:50 +00:00
Bruno Windels
cef94fea45 fix lint 2021-06-02 12:38:41 +02:00
Bruno Windels
bf951bd322 fix logging wrong value 2021-06-02 12:34:14 +02:00
Bruno Windels
c3754fdbd4 fix the race, make test succeed 2021-06-02 12:34:02 +02:00
Bruno Windels
87ebdbb0b4 add failing test for unhandled race between sync and send
see comments in code
2021-06-02 12:33:15 +02:00
Bruno Windels
8369dcef5f utility to wait until a value becomes truthy in a test 2021-06-02 12:32:50 +02:00
Bruno Windels
ffb6cb372a utility to create events in tests 2021-06-02 12:32:34 +02:00
Bruno Windels
cf6e14ed8b utility for homeserver api mocking 2021-06-02 12:32:19 +02:00
Bruno Windels
6a5679a338 create mock storage using fake-indexeddb 2021-06-02 12:32:03 +02:00
Bruno Windels
edbac25613 prepare storage to work with alternative idb impl 2021-06-02 12:31:13 +02:00
Bruno Windels
8dfed73524
Merge pull request #361 from vector-im/bwindels/redactions
Redactions
2021-06-02 10:22:33 +00:00
Bruno Windels
7a96f84cab also show redaction reason for redaction local echo 2021-06-02 12:17:09 +02:00
Bruno Windels
15f6ab8b7e only show cancel option if not already sending 2021-06-02 11:56:15 +02:00
Bruno Windels
addddf1f26 remove need for transferLocalEchoState, just add local relations again 2021-05-31 16:33:05 +02:00
Bruno Windels
13a4a0169c remove obsolete comments 2021-05-31 16:23:59 +02:00
Bruno Windels
25ce06c9d5 clarify 2021-05-31 15:55:31 +02:00
Bruno Windels
dc2e21495b explain why this is needed 2021-05-31 15:46:57 +02:00
Bruno Windels
8196a02f9d don't even need isOwn member anymore 2021-05-31 15:25:01 +02:00
Bruno Windels
00231443d3 timeline has the own member, so can just use timeline, not ownUserId 2021-05-31 15:18:44 +02:00
Bruno Windels
2bd7c23076 fix lint 2021-05-31 15:08:49 +02:00
Bruno Windels
d68d14358f use lower return 2021-05-31 14:08:45 +02:00
Bruno Windels
492a8fe359 remove extra whiteline 2021-05-31 14:05:37 +02:00
Bruno Windels
5d35caf85f no need to emit, timeline finds out by themselves with remote echo 2021-05-31 14:03:22 +02:00
Bruno Windels
606d40c9d4 simplify canRedact logic in view by overriding in RedactedTile 2021-05-31 13:55:08 +02:00
Bruno Windels
23459aad52 check if you are allowed to redact a message 2021-05-31 13:52:03 +02:00
Bruno Windels
128f9812a6 set destructive flag here too 2021-05-31 13:08:06 +02:00
Bruno Windels
245b5458d0 put latest version in package.json as well 2021-05-31 13:06:11 +02:00
Bruno Windels
f8f1d49c56 polyfill String.matchAll for IE11
See https://github.com/babel/babel/issues/10816
Don't really understand how or why this works, but it did
2021-05-31 13:04:03 +02:00
Bruno Windels
57288f75b0 add avatar & sender as first element in message so they don't occlude 2021-05-31 13:03:41 +02:00
Bruno Windels
d4373eb309 make options button look the same in all browsers 2021-05-31 13:03:23 +02:00
Bruno Windels
d66cdc97cd fix message options button placement in IE11 2021-05-31 13:02:42 +02:00
Bruno Windels
5f5f83912d try see if newer autoprefixer fixes our issue(it didn't, but good still) 2021-05-31 13:02:01 +02:00
Bruno Windels
2e34668b91 show errors while mounting list view children 2021-05-31 12:28:42 +02:00
Bruno Windels
762ed96a3b Not needed as both evententry and pendingevententry return timestamp 2021-05-31 11:58:01 +02:00
Bruno Windels
447b98ce6c don't use subviews for showing/hiding avatar & sender on continuation 2021-05-31 11:57:17 +02:00
Bruno Windels
0596ca06b1 emit remove before linking up sibling tiles
otherwise emitting the update from updatePreviousSibling has
the wrong idx
2021-05-31 11:56:41 +02:00
Bruno Windels
63b371b6ef support findAndUpdate with same predicate semantics in SortedArray too 2021-05-31 10:47:32 +02:00
Bruno Windels
fa37e8fedb findAndUpdate uses predicate, just add callback to update method 2021-05-31 10:46:16 +02:00
Bruno Windels
ab6b87987b
Merge pull request #369 from MidhunSureshR/icon-fix
Replace ellipsis character in RoomView header with svg icon
2021-05-31 08:05:30 +00:00
RMidhunSuresh
849a02cec2 Add aria-label
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-28 20:37:36 +05:30
Bruno Windels
6a5d856093 add destructive flag to delete menu option 2021-05-28 16:25:23 +02:00
Bruno Windels
5b0675b711 fix lint 2021-05-28 16:25:06 +02:00
Bruno Windels
95a680eb83 fix whitespace 2021-05-28 16:24:47 +02:00
Bruno Windels
57d9916746 buttons in ff were not in Inter 2021-05-28 15:30:03 +02:00
Bruno Windels
7f41993648 prevent buttons with negative margin to displace message menu 2021-05-28 15:28:04 +02:00
Bruno Windels
b3749f2d92 prevent long links from creating horizontal scroll 2021-05-28 15:27:44 +02:00
Bruno Windels
5afcfc3e9b fix unsent/unverified message style 2021-05-28 15:27:25 +02:00
Bruno Windels
43c082475b unify cancel option for various tiles in menu option 2021-05-28 15:27:02 +02:00
Bruno Windels
2b0fa22c8a open menu when clicking ... button on message with delete/cancel option 2021-05-28 13:14:55 +02:00
Bruno Windels
63e948fc80 change renderMessage fn to base class
as preparation to create menu items in subclasses
2021-05-28 12:32:19 +02:00
Bruno Windels
100e056d55 style the button 2021-05-28 12:22:47 +02:00
Bruno Windels
f82e873da8 adjust message status styling to css grid changes 2021-05-28 12:17:59 +02:00
RMidhunSuresh
fc898f8c7e Set new svg icon as background-image of button
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-28 15:47:50 +05:30
RMidhunSuresh
ba11cdd83f Remove ellipsis character
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-28 15:47:06 +05:30
RMidhunSuresh
3f99b7247c Bring in vertical ellipsis icon
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-28 15:46:10 +05:30
Bruno Windels
bbf9832d6a switch timeline messages to css grid, and add menu button 2021-05-28 12:09:21 +02:00
Bruno Windels
13ac41b264 delete obsolete code 2021-05-28 12:02:35 +02:00
Bruno Windels
2da7ef4280 can only look in remote entries here as PEEs never return an event id 2021-05-27 10:28:02 +02:00
Bruno Windels
5e9ce365bf also apply local relations when loading at top 2021-05-27 10:27:44 +02:00
Bruno Windels
c6e2607f1f guard against updates emitted while populating during first subscription
This came up now because Timeline uses a MappedList to map PendingEvents
to PendingEventEntries. In the map function, we setup links between
entries to support local echo for relations. When opening a timeline
that has unsent relations, the initial populating of the MappedList
will try to emit an update for the target entry in remoteEntries.
This all happens while the ListView of the timeline is calling subscribe
and all collections in the chain are populating themselves based on
their sources.

This usually entails calling subscribe on the source,
and now you are subscribed, iterate over the source (as you're not
allowed to query an unsubscribed observable collection, as it might not
be populated yet, and even if it did, it wouldn't be guaranteed to be
up to date as events aren't flowing yet).

So in this concrete example, TilesCollection hadn't populated its tiles
yet and when the update to the target of the unsent relation reached
TilesCollection, the tiles array was still null and it crashed.

I thought what would be the best way to fix this and have a solid model
for observable collections to ensure they are always compatible with
each other. I considered splitting up the subscription process in two
steps where you'd first populate the source and then explicitly start
events flowing.

I didn't go with this way because it's really only updates that
make sense to be emitted during setup.
A missed update wouldn't usually bring the collections out of sync
like a missed add or remove would. It would just mean the UI isn't
updated (or any subsequent filtered collections are not updated),
but this should be fine to ignore during setup, as you can rely
on the subscribing collections down the chain picking up the update
while populating. If we ever want to support add or remove events
during setup, we would have to explicitly support them, but for now
they are correct to throw.

So for now, just ignore update events that happen during setup
where needed.
2021-05-27 10:02:05 +02:00
Bruno Windels
a8e43d4850 remove leftover logging 2021-05-27 09:18:22 +02:00
Bruno Windels
a93b1af047 ensure these don't fail on a gap entry 2021-05-27 09:16:25 +02:00
Bruno Windels
afc3db2f33 unrelated todo note for later 2021-05-27 09:11:57 +02:00
Bruno Windels
2b5dcff836 consistent naming 2021-05-27 09:11:13 +02:00
Bruno Windels
56495c9d13 fix gap failing to fill 2nd time + unit regression test 2021-05-27 09:10:10 +02:00
Bruno Windels
15048bd9c3 very basic redact button on all text messages 2021-05-26 13:11:20 +02:00
Bruno Windels
da02b5fe2d transfer local echo state when replacing event entry
e.g. after decryption or remote echo of other relation comes in
2021-05-26 13:10:19 +02:00
Bruno Windels
ca4d09e923 add logging and return promise from Tile.redact 2021-05-26 13:08:54 +02:00
Bruno Windels
a5d5c55835 MappedList.findAndUpdate 2021-05-26 13:08:33 +02:00
Bruno Windels
ce7147e463 put redactions in their own view, and allow aborting while still queued 2021-05-26 13:07:56 +02:00
Bruno Windels
cb622be653 rerender tile when becoming or stopped being redacted 2021-05-25 12:58:20 +02:00
Bruno Windels
af45810582 add support for redactions (and relations) local echo 2021-05-21 16:59:29 +02:00
Bruno Windels
b55efb7f11 ensure updateEntries is always set in the result of GapWriter 2021-05-21 16:58:04 +02:00
Bruno Windels
c934049523 also resolve related event ids when removing remote echo during sync
as /sync races with /send, and remote echo may happen first.
It's important for local echo that the pending redaction/relation
will also get attached to the remote echo before /send returns,
otherwise the remote echo would be "unannotated" until /send returns
2021-05-21 10:52:46 +02:00
Bruno Windels
bc09ed8bf6
Merge pull request #363 from vector-im/bwindels/cache-ci-deps
Enable yarn cache in github actions
2021-05-20 13:49:32 +00:00
Bruno Windels
54478e6940 add comment with source 2021-05-20 15:45:18 +02:00
Bruno Windels
11b30e7eda remove whitespace 2021-05-20 15:40:53 +02:00
Bruno Windels
e08f8630df remove whitespace 2021-05-20 15:35:58 +02:00
Bruno Windels
9de506d2a7 enable yarn cache, hopefully 2021-05-20 15:34:06 +02:00
Bruno Windels
c3fb35848b emit update when receiving event id for related event ahead in the queue 2021-05-20 15:02:45 +02:00
Bruno Windels
619cf9bcbb this should be filter rather than find, we iterate it 2021-05-20 15:02:24 +02:00
Bruno Windels
f271517446 log redaction during sync 2021-05-20 15:02:14 +02:00
Bruno Windels
8a8c5569dc provide redact method on tile and room
also add some logging
2021-05-20 14:53:17 +02:00
Bruno Windels
9721432a8c remove pending events that failed because of permanent error
so they don't get stuck
2021-05-20 14:52:30 +02:00
Bruno Windels
4ce66fc8a1 allow concurrent removals when iterating pending events
so we can remove failed events in the next commit
2021-05-20 14:51:04 +02:00
Bruno Windels
618a32e6c0 revert last tried pending event status to waiting when offline
so we don't fail the check if we can immediately remove when redacting
2021-05-20 14:49:54 +02:00
Bruno Windels
df9e886f32 fix lint 2021-05-20 13:22:54 +02:00
Bruno Windels
b655c34bbb also show reason for redaction 2021-05-20 13:20:12 +02:00
Bruno Windels
780ad44032 render redacted messages 2021-05-20 13:15:35 +02:00
Bruno Windels
94b0bc82ef writing relations is async 2021-05-20 12:50:16 +02:00
Bruno Windels
814e92ad92 fix missing import 2021-05-20 12:50:16 +02:00
Bruno Windels
39bed4b0fc fix lint here 2021-05-20 12:50:16 +02:00
Bruno Windels
9b923d337d write redactions during sync 2021-05-20 12:50:16 +02:00
Bruno Windels
edaac9f436 draft redaction support, no local echo yet 2021-05-20 12:50:16 +02:00
Bruno Windels
b31dc38af1
Merge pull request #362 from vector-im/bwindels/lintfixesandci
Fix/disable lint errors and enable in CI
2021-05-20 09:39:41 +00:00
Bruno Windels
ab841a84a3 rename workflow 2021-05-20 11:30:42 +02:00
Bruno Windels
3e42231e7a rename workflow 2021-05-20 11:29:20 +02:00
Bruno Windels
e6c0df5ab2 try fix action 2021-05-20 11:25:53 +02:00
Bruno Windels
3649bf1f04 try fix action 2021-05-20 11:24:22 +02:00
Bruno Windels
7ea38a3f4c try fix action 2021-05-20 11:23:45 +02:00
Bruno Windels
ce2acfc659 try fix action 2021-05-20 11:22:53 +02:00
Bruno Windels
145d7f6f47 why is this not showing up anymore? trying to rename back to old name 2021-05-20 11:19:04 +02:00
Bruno Windels
9df18b839e wrong extension 2021-05-20 11:13:52 +02:00
Bruno Windels
fa7e24fed7 tune linting so it doesn't produce errors anymore and enable in CI 2021-05-20 11:11:57 +02:00
Bruno Windels
1f17edc248 fix undefined variable, this probably breaks restoring the last url 2021-05-20 11:11:07 +02:00
Bruno Windels
eb8fdf5c27 some notes on how to implement relations 2021-05-19 12:01:58 +02:00
Bruno Windels
870cf62ab6 release v0.1.53 2021-05-18 15:31:26 +02:00
Bruno Windels
df8686099f
Merge pull request #356 from MidhunSureshR/linkify-doc
Add jsdoc comments for clickable link code + Minor Changes
2021-05-18 13:30:23 +00:00
Bruno Windels
7488e0398c
Merge pull request #360 from vector-im/bwindels/defaultsessionurl
Add url action to redirect to last session id
2021-05-18 13:29:06 +00:00
Bruno Windels
c3ef801566 offer back button when session loading fails 2021-05-18 15:27:31 +02:00
Bruno Windels
d567664d8b ensure there is a session id in last-session action 2021-05-18 14:47:45 +02:00
Bruno Windels
2c7ea44afd don't expose last url to root VM so it isn't confused by empty path
instead, check the length of the path in a dedicated method in
the router
2021-05-18 14:27:09 +02:00
Bruno Windels
dd880529ac make redirects in initial url be reflected in url bar 2021-05-18 13:52:31 +02:00
Bruno Windels
1bec1033d1 translate last-session to a real session id 2021-05-18 13:52:13 +02:00
Bruno Windels
63620ce59a
Merge pull request #358 from vector-im/bwindels/joinroom
Offer to join unknown room
2021-05-18 09:59:35 +00:00
Bruno Windels
4c5be997fe fix menu alignment with extra option
we'll need to have a closer look how these alignment options make sense
but for now this will do
2021-05-18 11:58:06 +02:00
Bruno Windels
754e7e7bb2 clarify with comments 2021-05-18 11:54:18 +02:00
Bruno Windels
f92b1df4fc update copyright comments 2021-05-18 11:54:10 +02:00
Bruno Windels
54798d3079 unreachable now 2021-05-18 11:50:02 +02:00
Bruno Windels
32e47bc660 remove unused imports 2021-05-18 11:49:25 +02:00
Bruno Windels
3724fc6765 log join network call 2021-05-18 11:49:16 +02:00
Bruno Windels
2cf100efaa refine unknown room view 2021-05-18 11:46:55 +02:00
Bruno Windels
c04a8140a3 offer to rejoin archived room from menu 2021-05-18 11:07:46 +02:00
Bruno Windels
03be63572d ask to join when room id is unknown 2021-05-18 11:07:19 +02:00
Bruno Windels
3acb3bb48c fix memory leak when switching room ids 2021-05-18 11:06:48 +02:00
Bruno Windels
9679058081 allow to join room by id or alias 2021-05-18 11:05:31 +02:00
RMidhunSuresh
72f58b6001 Change lines[i] to line
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-18 14:26:56 +05:30
RMidhunSuresh
1d63799c9b Added js-doc for formatFunction
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-17 20:56:12 +05:30
RMidhunSuresh
11d551f8a3 Add js-doc for parsePlainBody
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-17 20:39:23 +05:30
RMidhunSuresh
d671bcec31 Add js-docs for linkify function
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-17 20:32:35 +05:30
Bruno Windels
a4d3b5f013 release v0.1.52 2021-05-17 13:03:26 +02:00
Bruno Windels
c62eff71a9
Merge pull request #355 from vector-im/bwindels/lint
Enable linting
2021-05-17 11:02:42 +00:00
Bruno Windels
8ff1f91d2c bring down lint error count, delete some old, inactive unit tests 2021-05-17 12:59:04 +02:00
Bruno Windels
c3b5c8750e add eslint as dep, and have yarn lint run it 2021-05-17 12:53:58 +02:00
Bruno Windels
1c8fb0a7b5
Merge pull request #354 from vector-im/bwindels/fix-encrypted-tiles
Fix crash when rendering non-decrypted message tiles
2021-05-17 10:51:31 +00:00
Bruno Windels
645470cd03 no need for private prop here 2021-05-17 12:45:55 +02:00
Bruno Windels
ce976226f9 rename MessageTile to BaseMessageTile
since MessageTile doesn't have a shape property anymore
2021-05-17 12:15:13 +02:00
Bruno Windels
67714040e7 rename BaseTextMessageTile to BaseTextTile 2021-05-17 12:14:15 +02:00
Bruno Windels
fa64fcce2d expect MessageBody here with parts property
and do some cleanup
2021-05-17 11:29:00 +02:00
Bruno Windels
054c51b82f add caching MessageBody in BaseTextMessageTile,use in EncryptedEventTile
missing body in EncryptedEventTile was what caused the bug
2021-05-17 11:27:30 +02:00
Bruno Windels
01b8b397b6 expose sourceString on result of parsing message body
and also do some cleanup
2021-05-17 11:26:30 +02:00
Bruno Windels
4f1149c1a7 release v0.1.51 2021-05-12 23:10:02 +02:00
Bruno Windels
1ef6a91cf1
Merge pull request #351 from vector-im/bwindels/fix-await-room-load
fix DM room names appearing as "Empty room"
2021-05-12 21:09:24 +00:00
Bruno Windels
f5f25c3019 fix DM room names appearing as "Empty room"
because we weren't properly awaiting the heroes to be loaded
2021-05-12 23:07:25 +02:00
Bruno Windels
c153f4a3e2 release v0.1.50 2021-05-12 22:51:01 +02:00
Bruno Windels
41f77f33af
Merge pull request #350 from vector-im/bwindels/linkify-parenthesis
Don't accept closing parenthesis as last character of url
2021-05-12 20:50:39 +00:00
Bruno Windels
62bb891e79 don't accept closing parenthesis as last character of url 2021-05-12 22:49:34 +02:00
Bruno Windels
59a74dbd9f Merge branch 'master' of github.com:vector-im/hydrogen-web 2021-05-12 18:33:25 +02:00
Bruno Windels
78946a953a release v0.1.49 2021-05-12 18:33:05 +02:00
Bruno Windels
0e270f4cb9
Merge pull request #347 from vector-im/bwindels/roomlistsorting
Don't move rooms to lower sorting position once marked as read
2021-05-12 16:32:14 +00:00
Bruno Windels
56699baaf1 dont need this anymore 2021-05-12 18:31:13 +02:00
Bruno Windels
86d60496c0 Don't move read rooms to lower sorting position 2021-05-12 18:28:39 +02:00
Bruno Windels
402a5e5d54 fix room list sorting not keeping sorting position after clearing unread 2021-05-12 18:26:55 +02:00
Bruno Windels
93c08c16c1
Merge pull request #345 from MidhunSureshR/linkify
Render URLs as clickable links in timeline
2021-05-12 15:40:46 +00:00
Bruno Windels
2ccd0c8def
Merge pull request #346 from vector-im/bwindels/leave-room
Leave and forget room
2021-05-12 15:31:31 +00:00
Bruno Windels
10ac1dc29c make options vertical ellipsis larger 2021-05-12 17:29:12 +02:00
Bruno Windels
b30b62416c add option to forget archived room 2021-05-12 15:38:54 +02:00
Bruno Windels
5d139dff43 Support deleting data in several stores for room id 2021-05-12 15:38:11 +02:00
Bruno Windels
e233caf7ac change order of type and scope in operation index key to scan per scope 2021-05-12 15:36:48 +02:00
Bruno Windels
2d2ec25f86 add comment clarifying popup arrangement options 2021-05-12 14:02:15 +02:00
Bruno Windels
f331d84292 add options menu in room header with leave room option 2021-05-12 13:59:08 +02:00
RMidhunSuresh
851e8d34a4 Incorporate better regex from review
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-12 16:15:30 +05:30
RMidhunSuresh
00bcdbab37 Fix broken test
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-12 16:15:07 +05:30
Bruno Windels
d7e8529a6e
Merge pull request #342 from vector-im/bwindels/archive-room-on-leave
Archive room on leave
2021-05-12 10:34:38 +00:00
RMidhunSuresh
bb74656348 Grammar fix
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-12 15:59:44 +05:30
RMidhunSuresh
2e657fbbbd Add failing test for link ending with <
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-12 15:56:33 +05:30
RMidhunSuresh
55b7c55efe Pull callback args into variables
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-12 15:52:05 +05:30
Bruno Windels
afd33f4b77 use constant for member event type 2021-05-12 12:19:05 +02:00
RMidhunSuresh
9d898bdf47 Change word to improve meaning
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 22:32:05 +05:30
RMidhunSuresh
b521797f4d Explain the reason for double escapes
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 22:28:14 +05:30
RMidhunSuresh
ef15968a3d Add some helpful pointers
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 22:26:45 +05:30
RMidhunSuresh
c6d7cef491 Refactor regex to improve readability
- Split regex into components
- Add informative comments

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 22:21:37 +05:30
RMidhunSuresh
31740f4ec6 Put regex into separate file
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 22:18:52 +05:30
RMidhunSuresh
6f0362802a Move linkify.js to separate directory
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 22:17:08 +05:30
RMidhunSuresh
027f6020e7 Modified regex to allow unicode in fragment
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 22:15:36 +05:30
RMidhunSuresh
e20810b0cb Add test for unicode after fragment without path
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 20:44:39 +05:30
Bruno Windels
ca84f485ab fix tests 2021-05-11 17:04:36 +02:00
RMidhunSuresh
7be9f93909 Make regex module-level constant
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 20:32:36 +05:30
Bruno Windels
1738a0ea3c need to create archived room when leaving, otherwise it isn't stored 2021-05-11 16:58:16 +02:00
RMidhunSuresh
03208b88d5 Move link tests to linkify
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 20:19:29 +05:30
Bruno Windels
a0e3848cf6 dispose existing view model when changing status, otherwise we leak! 2021-05-11 16:39:33 +02:00
Bruno Windels
8b8214cd1b reference count archived rooms and keep track of active ones
so we don't create two instances for the same id, one for sync, and one
for displaying, and hence updates from sync being pushed on a different
instance than the one displaying, and not updating the view.
2021-05-11 16:09:58 +02:00
Bruno Windels
965700272b remove archivedRoom map, it's unused and just complicating for now 2021-05-11 16:09:01 +02:00
RMidhunSuresh
dbe76e973e Add braces to all blocks
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 17:59:03 +05:30
Bruno Windels
82a1b37363 refresh room view model when going from joined -> archived
given we have a dedicated class (ArchivedRoom) for it now
2021-05-11 13:11:38 +02:00
Bruno Windels
e3c1644d09 show leave reason instead of composer for archived room 2021-05-11 13:11:11 +02:00
Bruno Windels
9ea0138ffd don't open room view when clearing room id 2021-05-11 13:10:21 +02:00
Bruno Windels
be7934057e lint 2021-05-11 13:08:13 +02:00
Bruno Windels
51d13fd8d2 update comments for Invite updates 2021-05-11 13:07:57 +02:00
Bruno Windels
6bb9140720 have individual getters for kickDetails 2021-05-11 13:07:31 +02:00
Bruno Windels
00d4dc9518 rename kickAuthor to kickedBy 2021-05-11 13:07:11 +02:00
Bruno Windels
24731687dc log room id in invite and archivedroom afterSync 2021-05-11 13:05:02 +02:00
Bruno Windels
e775ed12b4 sync arch. rooms with Room during init. sync to create summary, timeline
when receiving archived rooms during initial sync, sync them with Room
(e.g. as a joined room) first so the members, timeline get written
and the summary gets created which is then adopted by the ArchivedRoom.
2021-05-11 13:02:43 +02:00
Bruno Windels
2087059c0b fix archived room summary key path now the summary is nested 2021-05-11 13:01:19 +02:00
RMidhunSuresh
3830a95af2 Cache result in TextTile
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 16:03:14 +05:30
RMidhunSuresh
86f4b6186a Refactor functions to accept single parameter
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 15:03:20 +05:30
RMidhunSuresh
e4a143659c Refactor unwanted getter into constant object
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 14:02:25 +05:30
RMidhunSuresh
051771397c Fix bug in which links preceeded text
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 13:59:45 +05:30
RMidhunSuresh
3beaf88a8b Rename messageFormat to body
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 13:46:38 +05:30
RMidhunSuresh
070bc96186 Fallback to empty string if content is undefined
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 13:43:54 +05:30
RMidhunSuresh
577883a1d4 Refactor loop
Don't handle last element separately

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 13:31:33 +05:30
RMidhunSuresh
03b971d898 Added tests for new linkify
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-11 12:55:39 +05:30
Bruno Windels
99d5467ad1 make archived room part of sync lifecycle (draft) 2021-05-10 18:42:30 +02:00
Bruno Windels
79d97737bc calculate leave details in archived room 2021-05-10 18:41:43 +02:00
RMidhunSuresh
17be7c4f4d Add newline to message
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-10 22:10:50 +05:30
RMidhunSuresh
fc3b391aa9 Use MessageBodyBuilder instead of linkify
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-10 21:41:57 +05:30
RMidhunSuresh
55f2d79d07 Move newline handling to MessageBodyBuilder
- Also add a insert method to reflect new design.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-10 21:37:27 +05:30
RMidhunSuresh
787d438a74 Refactored Linkifier class into single function
Modified design so that linkify interacts with MessageBodyBuilder
through callbacks.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-10 21:30:11 +05:30
RMidhunSuresh
dca649dfd4 Moved files into more appropriate locations
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-10 19:53:40 +05:30
RMidhunSuresh
06a6cae4af Rename MessageBodyBuilder to MessageBodyBuilder
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-10 19:50:26 +05:30
RMidhunSuresh
bbeb0a5764 Transform new message format into DOM elements
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-08 19:22:49 +05:30
RMidhunSuresh
2f883d049c Return new message format instead of message text
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-08 19:20:07 +05:30
RMidhunSuresh
9ca0d93a01 Add tests for linkify
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-08 19:18:45 +05:30
RMidhunSuresh
32fd1eb318 Implement linkifier
Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-08 19:17:43 +05:30
RMidhunSuresh
3c46a07a1e Implement object format to represent chat messages
Every chat text message can be split into parts such as text, newline
and links.

Signed-off-by: RMidhunSuresh <rmidhunsuresh@gmail.com>
2021-05-08 19:02:44 +05:30
Bruno Windels
1216378783 Extract BaseRoom from Room with summary and timeline, not sync or send
Which we can then reuse to create a dedicated ArchivedRoom class
which will:
 - have only relevant methods and properties (e.g. no sendEvent)
 - turns out that you can still receive a leave room in the sync
   (e.g. when banned after kick) so we'll make the sync for
   an archived room separate from room to not overcomplicate the sync
   there, much like we did for Invite already.
2021-05-07 16:13:49 +02:00
Bruno Windels
06868abdb2 with room status being a thing, we don't need the invite on the room 2021-05-07 14:42:29 +02:00
Bruno Windels
a8d4ee0dd6 different room view models don't need to initiate switching now
as this is triggered by observing the room status
2021-05-07 13:11:17 +02:00
Bruno Windels
6c58c61da9 move switching room view models to a dedicated observable
based on the observing the room status
2021-05-07 13:10:35 +02:00
Bruno Windels
6bb8e2fa43 allow loading an archived room 2021-05-07 13:10:10 +02:00
Bruno Windels
1b83ae7d8a allow observing the room status 2021-05-07 13:09:38 +02:00
Bruno Windels
243d105aad support getting the room status for a room: invited, joined or archived 2021-05-07 13:08:39 +02:00
Bruno Windels
3143f2a791 also make an observable version of a retained value 2021-05-07 13:06:20 +02:00
Bruno Windels
36f54420cf extract RetainedValue from MemberList 2021-05-07 13:06:00 +02:00
Bruno Windels
030b6837ef rename 2021-05-06 15:27:32 +02:00
Bruno Windels
7e450071b1 clear all room state when rejoining room 2021-05-06 15:27:10 +02:00
Bruno Windels
8c2ae863fd clean up rejoin storage logic somewhat 2021-05-06 15:26:48 +02:00
Bruno Windels
f16c08f13e remove room from all user identities when leaving
and delete identity as well as all device identities if no rooms left
2021-05-06 15:24:52 +02:00
Bruno Windels
a12f10dc3c make type explicit 2021-05-06 15:23:33 +02:00
Bruno Windels
00d8f81bdd clear all room state before rejoining room 2021-05-06 14:15:47 +02:00
Bruno Windels
15080edfa7 fix failing test now we don't remove invite from collection anymore here 2021-05-06 14:15:47 +02:00
Bruno Windels
45837f7377 don't set dmUserId when not a DM 2021-05-06 14:15:47 +02:00
Bruno Windels
7defd4a02b ensure the sync is limited when rejoining without overlap
otherwise gap would be lost. The server should do this already,
but we're just ensuring it is, to be more robust.
2021-05-06 14:15:47 +02:00
Bruno Windels
08ba4577f6 rejoin logic was throwing away the prev_batch token 2021-05-06 14:15:47 +02:00
Bruno Windels
9546b13821 attempt to load sync writer position when joining a room during sync
since fragments and events are not archived, just the summary,
attempt to load the room and sync writer during sync,
so we write the timeline correctly and don't cause ConstraintErrors
because unaware of fragments and events already there.
2021-05-06 14:15:47 +02:00
Bruno Windels
07535eedca when rejoining, room will be archived so consider any non-join a rejoin 2021-05-06 14:15:47 +02:00
Bruno Windels
1258aaee7c brevity 2021-05-06 14:15:47 +02:00
Bruno Windels
f6957278c3 write and remove archived summary when leaving/rejoining 2021-05-06 14:15:47 +02:00
Bruno Windels
644698aed7 remove room from room list when leaving 2021-05-06 14:15:47 +02:00
Bruno Windels
89461bf69a do all collection removal from sync rather than hand callbacks to invite 2021-05-06 14:15:47 +02:00
Bruno Windels
12da71f731 unneeded ? 2021-05-06 14:15:47 +02:00
Bruno Windels
bcfc4d1fd7 have better transaction errors 2021-05-06 14:15:47 +02:00
Bruno Windels
184480ad36 no need to capture req here 2021-05-06 14:15:47 +02:00
Bruno Windels
d4d7adc7fc add archivedRoomSummary store 2021-05-06 14:15:47 +02:00
Bruno Windels
c2716a061b pass in userId for kickDetails 2021-05-06 14:15:47 +02:00
Bruno Windels
2cfe7034e8 extract fn 2021-05-06 14:15:47 +02:00
Bruno Windels
b13bfee3d8 support setting kickDetails in room summary 2021-05-06 14:15:47 +02:00
Bruno Windels
85385295a6 don't serialize null values in room summary
they only take space in the storage otherwise as we add more fields
2021-05-06 14:15:47 +02:00
Bruno Windels
517a7516b7 release v0.1.48 2021-05-06 12:14:58 +02:00
Bruno Windels
57725e745e
Merge pull request #343 from vector-im/bwindels/fix-sync-rooms-section-missing
Dont assume there is a rooms section in the sync
2021-05-06 10:11:32 +00:00
Bruno Windels
9e78f8382a dont assume there is a rooms section in the sync 2021-05-06 12:10:10 +02:00
Bruno Windels
a28b2b74d2
Merge pull request #338 from vector-im/bwindels/ci-tests
Run tests on CI
2021-05-04 10:24:00 +00:00
Bruno Windels
08bafad206
Rename node.js.yml to unittests.js.yml 2021-05-04 10:23:17 +00:00
Bruno Windels
d804a93df3
last label adjustment 2021-05-04 10:22:37 +00:00
Bruno Windels
4525daee83
try to adjust label again 2021-05-04 10:21:46 +00:00
Bruno Windels
64cca3b874
Update name and npn install args 2021-05-04 10:20:19 +00:00
Bruno Windels
61c96acc7a
Run tests on CI 2021-05-04 10:16:59 +00:00
Bruno Windels
dd046d8287
Merge pull request #301 from Johennes/feature/librejs
Add license comments to make GNU LibreJS accept Hydrogen's JS
2021-05-03 08:33:39 +00:00
Bruno Windels
10053efc0c
Merge pull request #300 from Prathap-P/update-readme
Update readme
2021-04-30 14:37:45 +00:00
Bruno Windels
1a473f7f4a
Merge pull request #337 from vector-im/gsouquet-docs-link-fix
Fix links to Matrix documentation
2021-04-30 14:29:16 +00:00
Bruno Windels
408fc29314
Merge pull request #335 from tawfiek/add-enterkeyhint
Add enterkeyhint
2021-04-30 14:28:38 +00:00
Germain
62231b7833
Fix links to Matrix documentation 2021-04-30 11:58:29 +01:00
Prathap-P
57b6c45e34
Ensured more subtleness in the readme file. 2021-04-30 13:40:10 +05:30
Tawfiek
1104317a76 UI: add enterkeyhint into filter in LerftPanelView 2021-04-30 04:19:58 +02:00
Tawfiek
31a9da09b2 UI: add enterkeyhint end into the MessageComposer 2021-04-30 04:15:11 +02:00
Tawfiek Khalaf
5e9f728346
Merge pull request #1 from vector-im/master
Pull upstream master
2021-04-30 02:47:13 +02:00
Johannes Marbach
452a0e7bda Merge branch 'master' into feature/librejs 2021-04-28 20:22:20 +02:00
Bruno Windels
9312f71764 release v0.1.47 2021-04-28 14:16:50 +02:00
Bruno Windels
0298f31ec7
Merge pull request #331 from vector-im/bwindels/dedupe-avatar-sizes
Don't repeat avatar sizes in view models
2021-04-28 14:14:38 +02:00
Bruno Windels
ad3b7f7f00 also add avatar size for timeline 2021-04-28 14:12:35 +02:00
Bruno Windels
e1d4c75d18 pass avatar sizes from view to view model rather than repeating there
and risk a mismatch resulting in too large/small image
2021-04-28 14:11:35 +02:00
Bruno Windels
b35b6ec496 work around --avatar-size being compiled away in build 2021-04-28 13:45:43 +02:00
Bruno Windels
bfaeffcb51 fix memory leak in settings view by using nested template builder 2021-04-28 13:05:52 +02:00
Prathap-P
3bc21c75d8
Update README.md
Co-authored-by: Bruno Windels <bruno@windels.cloud>
2021-04-28 15:53:37 +05:30
Bruno Windels
04a1d89907 release v0.1.46 2021-04-28 11:42:02 +02:00
Bruno Windels
870ed3f211 add avatar size now there is no default anymore 2021-04-28 11:40:24 +02:00
Bruno Windels
aea59f8974
Merge pull request #272 from jcgruenhage/jcgruenhage/new-dockerfile
New production Dockerfile and GitLab CI definition
2021-04-28 11:13:14 +02:00
Bruno Windels
25e0211ca1
Merge pull request #279 from Johennes/feature/safari-viewport
Manually adapt UI when keyboard shows or hides on mobile Safari
2021-04-28 11:04:43 +02:00
Bruno Windels
fe9f0f7692
Merge pull request #328 from vector-im/bwindels/invites
Invites
2021-04-28 10:14:56 +02:00
Bruno Windels
b23ec5ef70 remove obsolete comment 2021-04-28 10:08:11 +02:00
Bruno Windels
e850874019 clarify order 2021-04-28 10:05:34 +02:00
Bruno Windels
15dfb6c202 clarify 2021-04-28 10:05:21 +02:00
Bruno Windels
ef6f10c5a2 test for Path.replace 2021-04-28 09:53:44 +02:00
Bruno Windels
1c5b953026 hide non-joined rooms in left panel for now
until we support archiving/forgetting the room
2021-04-27 16:16:07 +02:00
Bruno Windels
025320b83e fix filtered map 2021-04-27 16:15:20 +02:00
Bruno Windels
396ec4dfd9 also in grid remove room id from nav path when refreshing room vm fails 2021-04-27 15:33:12 +02:00
Bruno Windels
c47b27428b implement invite view + styling 2021-04-27 15:10:12 +02:00
Bruno Windels
f4f153ac4b don't seems to throw the letter off-center more than anything else
apart for some rare cases ... shrug
2021-04-27 15:09:45 +02:00
Bruno Windels
a9838fed06 don't set a default avatar size in the css, makes it easier to change it 2021-04-27 15:09:29 +02:00
Bruno Windels
fbe255f837 support extra classes in static avatar 2021-04-27 15:04:01 +02:00
Bruno Windels
441bb4d5d7 keep .room-placeholder display settings separate from .middle 2021-04-27 15:03:13 +02:00
Bruno Windels
34cca2ba0f add invite view to view gallery 2021-04-27 15:02:43 +02:00
Bruno Windels
1f36122283 remove debugging code 2021-04-27 15:02:29 +02:00
Bruno Windels
799ea50c68 remove obsolete logging 2021-04-27 15:02:00 +02:00
Bruno Windels
32d9f6e830 expose inviter user id 2021-04-27 15:01:35 +02:00
Bruno Windels
a95df54978 scale down inviter avatar 2021-04-27 15:01:20 +02:00
Bruno Windels
15fba7a07d missing semicolon 2021-04-27 14:43:26 +02:00
Bruno Windels
781235cb07 scale up room avatar 2021-04-27 14:43:13 +02:00
Bruno Windels
f902f255a4 add roomDescription property 2021-04-27 14:42:37 +02:00
Bruno Windels
b6573258fb always show inviter if present 2021-04-27 14:42:12 +02:00
Bruno Windels
87defbfb51 sort invites most recent first 2021-04-27 14:27:14 +02:00
Bruno Windels
fd454f1e20 don't expose joinRule prop, as it is protocol specific 2021-04-27 14:26:40 +02:00
Bruno Windels
86e8b27cb3 fix middle-shown to use new active vm 2021-04-27 11:01:51 +02:00
Bruno Windels
aaf4e79a73 make media repo available on invite 2021-04-27 11:01:33 +02:00
Bruno Windels
9b9e6fceda refine action buttons style 2021-04-27 10:32:06 +02:00
Bruno Windels
bb6664a9a8 fix room header height 2021-04-27 10:31:47 +02:00
Bruno Windels
ab8111f847 make all middle containers column flexboxes, not just the room view 2021-04-27 10:28:25 +02:00
Bruno Windels
55b576a299 rename .TimelinePanel to .RoomView_body 2021-04-27 10:28:09 +02:00
Bruno Windels
bd748549f7 fix updates in Invite not firing on collection 2021-04-26 10:45:27 +02:00
Bruno Windels
47b2eb0bdb don't create a room when rejecting an invite 2021-04-26 10:41:21 +02:00
Bruno Windels
f2d7f5e4da refresh vm when left room receives invite
clean up room vm switching in the process
2021-04-23 18:06:38 +02:00
Bruno Windels
827075bc37 only apply the invite when joining 2021-04-23 18:05:46 +02:00
Bruno Windels
847738a76c set invite on left room so we can detect a vm refresh is needed 2021-04-23 18:05:14 +02:00
Bruno Windels
b9f145caa4 emit events for rooms before invites
so the room is already there when removing the invite
2021-04-23 18:04:10 +02:00
Bruno Windels
b77d0f75ce can just use vm here 2021-04-22 17:23:41 +02:00
Bruno Windels
d289a44624 also sync leave now that we can handle it 2021-04-22 17:23:29 +02:00
Bruno Windels
2e9ddf9c2c prevent key collisions between rooms and invites before creating tile vm 2021-04-22 17:22:47 +02:00
Bruno Windels
ec0de15da6 handle overlap with existing timeline when rejoining room 2021-04-22 17:21:29 +02:00
Bruno Windels
ff4abbc5ba make dispose not fail 2021-04-22 17:18:07 +02:00
Bruno Windels
4e3127c4cf handle key collisions in JoinedMap 2021-04-22 13:28:14 +02:00
Bruno Windels
20f4474eb6 missing semicolon 2021-04-21 18:13:29 +02:00
Bruno Windels
35992ac2fc show very basic buttons to accept or reject invite 2021-04-21 17:50:41 +02:00
Bruno Windels
b4ad6142f4 show spinner in room list while accepting or rejecting invite 2021-04-21 17:50:22 +02:00
Bruno Windels
ac312cbdc7 fix c/p error in invite store 2021-04-21 17:49:59 +02:00
Bruno Windels
76933e51aa fix default avatar color being broken in room list 2021-04-21 17:48:40 +02:00
Bruno Windels
1cf49688d6 show invites at the top of the room list 2021-04-21 17:26:34 +02:00
Bruno Windels
16df40331b fix refactor mistake in sync 2021-04-21 17:26:10 +02:00
Bruno Windels
bfb7f58a3d add very basic invite view 2021-04-21 17:25:59 +02:00
Bruno Windels
f596b34cac add very basic join observable map, joining several maps into one
will be used to join invites and rooms into one map
2021-04-21 17:25:37 +02:00
Bruno Windels
1d3a2aca0e add 'get' method to common observable map api 2021-04-21 17:23:49 +02:00
Bruno Windels
0dc796b863 spacing 2021-04-21 15:48:41 +02:00
Bruno Windels
9961d3e4fc unused code 2021-04-21 15:48:27 +02:00
Bruno Windels
7e2870acef wire up InviteViewModel in Session/RoomGridViewModel
and:
 - switch to room once accepted
 - close invite if rejected
2021-04-21 15:47:39 +02:00
Bruno Windels
0cc95f5083 first draft of InviteViewModel 2021-04-21 15:45:51 +02:00
Bruno Windels
16f275ca34 make pass-through props smaller 2021-04-21 15:34:35 +02:00
Bruno Windels
a91a584201 not used anymore 2021-04-21 15:33:45 +02:00
Bruno Windels
9c19fa5c63 utility to remove room from path (with our without grid)
which will be used when rejecting an invite
2021-04-21 15:33:08 +02:00
Bruno Windels
ad5d7fc9f0 add note collection emit should happen first in Invite 2021-04-21 15:32:37 +02:00
Bruno Windels
a5e6294593 test accepted/rejected fields in invite tests 2021-04-21 15:32:13 +02:00
Bruno Windels
465e0c191f add logging to accepting or rejecting an invite 2021-04-21 15:31:55 +02:00
Bruno Windels
5ce138539b network calls to accept and reject invite 2021-04-20 19:02:45 +02:00
Bruno Windels
bb1c64e1ac add left panel tile view model for invites
and track in-progress state in Invite so it is shared by left panel
and open invite
2021-04-20 19:01:40 +02:00
Bruno Windels
ee98eaa640 restore invites when loading the session 2021-04-20 18:09:48 +02:00
Bruno Windels
5876e5200b don't need this for now 2021-04-20 18:03:27 +02:00
Bruno Windels
4560e0e491 split sync lifecycle steps out in different methods to keep it smaller 2021-04-20 17:57:17 +02:00
Bruno Windels
09ac503e22 add test for removing invite 2021-04-20 17:57:03 +02:00
Bruno Windels
d1dc4c9e9c remove extra layer of log items 2021-04-20 17:56:53 +02:00
Bruno Windels
a072426e07 integrate invites into the sync lifecycle and expose them on session 2021-04-20 17:39:46 +02:00
Bruno Windels
de125441d1 add write/load test for Invite 2021-04-20 17:34:45 +02:00
Bruno Windels
39c772300a fix indenting 2021-04-20 16:21:35 +02:00
Bruno Windels
81a35639ba add Invite class
calculating the room name, avatar, etc ...
with empty accept and reject methods for now
2021-04-20 16:21:27 +02:00
Bruno Windels
7c4a6fbe4b invite store 2021-04-20 13:13:56 +02:00
Bruno Windels
03d92b687e doc with design rationale 2021-04-20 13:02:33 +02:00
Bruno Windels
76e65c0bfd move rollup to devdeps 2021-04-19 10:38:08 +02:00
Bruno Windels
7ecb4f9678
Merge pull request #326 from vector-im/bwindels/fix-avatar-updates
Fix avatars not switching between an image and no image
2021-04-15 15:27:32 +02:00
Bruno Windels
33f1ba686c add warning when rendering outside of render fn for templates 2021-04-15 15:14:02 +02:00
Bruno Windels
766ce4e217 create AvatarView and renderStaticAvatar (for timeline) and use it
in RoomTileView, we make some efforts to only have one update listener
for the entire list, because by default a subview would listen on
the view model
2021-04-15 15:12:14 +02:00
Bruno Windels
c85b2ca3c9 allow manually updating subviews in templates w/ parentProvidesUpdates 2021-04-15 15:11:05 +02:00
Bruno Windels
357ce21678 extract base class from Template view to select update mechanism 2021-04-15 15:09:45 +02:00
Bruno Windels
16fda4dde0 white background for transparent avatar images 2021-04-15 14:59:01 +02:00
Bruno Windels
050d603812 release v0.1.45 2021-04-09 20:01:00 +02:00
Bruno Windels
3cf86999a6
Merge pull request #325 from vector-im/bwindels/connectionerror-initial-sync
Fix handling connection error during initial sync
2021-04-09 20:00:03 +02:00
Bruno Windels
d414fb6b94 sync can also stop without an error 2021-04-09 19:50:22 +02:00
Bruno Windels
ba38ce7976 use canonical way of check error type 2021-04-09 16:30:53 +02:00
Bruno Windels
b852feeb9c ConnectionError isn't throw from start, but sets sync.error 2021-04-09 16:28:14 +02:00
Bruno Windels
4b19e3c498 remove unused member 2021-04-09 16:27:25 +02:00
Bruno Windels
4d0ad04f7b
Merge pull request #323 from vector-im/bwindels/service-worker-aborts
Map service worker aborts as network errors, so sync does not halt
2021-04-09 15:38:07 +02:00
Bruno Windels
606e30fed2 make comment easier to read 2021-04-09 15:24:10 +02:00
Bruno Windels
c604c31032 use external mock for request in timeout tests 2021-04-09 15:16:43 +02:00
Bruno Windels
c36e812360 move RequestWrapper to own file
and add tests, improve comments, and and don't use timeout connection
error as that's not what happens if aborted request from service worker
is reported as TypeError either.
2021-04-09 15:15:28 +02:00
Bruno Windels
2b1f4866a9 map unexpected fetch AbortError to ConnectionError, so doesn't stop sync 2021-04-09 14:25:19 +02:00
Bruno Windels
2341748023 add some tests to timeout code 2021-04-09 14:09:48 +02:00
Bruno Windels
fe6f0c9b48
Merge pull request #322 from vector-im/bwindels/send-waits-for-keyshare
Sending a message waits for (ongoing) keyshare
2021-04-09 10:43:05 +02:00
Bruno Windels
e17fb24bd8 also await sharing the key in encrypt 2021-04-09 10:37:55 +02:00
Bruno Windels
13f20cdd26 wait for running key share operation in encrypt 2021-04-09 10:37:43 +02:00
Bruno Windels
6802f0720e release v0.1.44 2021-04-08 18:44:57 +02:00
Bruno Windels
dce13f67dd
Merge pull request #321 from vector-im/bwindels/fix-remote-echo-race
Fix local echo reappearing after remote echo arrived
2021-04-08 18:43:41 +02:00
Bruno Windels
bdc8451c4a better logging 2021-04-08 18:39:52 +02:00
Bruno Windels
9e697c6cbe don't add the pending event again if removed already, fixes #289 2021-04-08 18:36:26 +02:00
Bruno Windels
f976eeaf13 rename SortedArray.replace to update 2021-04-08 18:36:09 +02:00
Bruno Windels
d8d97c40f7 show start time milliseconds in log viewer 2021-04-08 18:35:30 +02:00
Bruno Windels
aee904fd8c
Merge pull request #320 from vector-im/bwindels/localecho-fallback-userid
fall back to bare userid for local echo profile
2021-04-08 16:35:58 +02:00
Bruno Windels
863f659774 fall back to bare userid for local echo profile 2021-04-08 16:30:46 +02:00
Bruno Windels
521894f401
Merge pull request #319 from vector-im/bwindels/lost-first-member
Member lookup prevents members from being written when only present in the timeline
2021-04-08 16:15:22 +02:00
Bruno Windels
813be758d7 we should prefer to not pick the event itself if it's a member event
but still fall back to that if it's a new join
2021-04-08 15:33:21 +02:00
Bruno Windels
f67ccc18f4 take most recent member rather than first in timeline for inline lookup
noticed this while inspecting the code, looks related to #269
2021-04-08 12:57:10 +02:00
Bruno Windels
6f37c232f7 Don't cache members that haven't been written yet - fixes #271 2021-04-08 12:56:24 +02:00
Bruno Windels
f5db6babf2 forgot to change the type of the hs field, so it still nags 2021-04-08 10:27:50 +02:00
Johannes Marbach
37bb078547 Add license comments to make GNU LibreJS accept Hydrogen's JS
This adds proper license comments to all _internal_ JS scripts in the /target
build. This way the code self-identifies as Apache-2.0 which prevents LibreJS
from blocking it.

Closes: #209

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2021-03-27 14:19:58 +01:00
Prathap
c74a829ae4 update readme 2021-03-26 22:52:17 +05:30
Johannes Marbach
14ed5fd1e8 Manually adapt UI when keyboard shows or hides on mobile Safari
Mobile Safari seems to be the only browser that does *not* resize the
viewport when the keyboard shows and hides. Instead the window is moved
to make room for the keyboard which moves content at the top off screen.

This uses the VisualViewport API to manually resize the `SessionView`
in response to keyboard display events. Additionally, if a DOM element
exists that has the `bottom-aligned-scroll` CSS class, its scroll
position is retained. Currently this only applies to the `Timeline`.

Note that the VisualViewport API was only introduced with iOS 13.
According to [statista.com], versions below 13 made up for 19% of
all iOS users in summer 2020, with the share continuing to fall off.
As a result, this seems like an acceptable workaround.

Fixes: #181

[statista.com]: https://www.statista.com/statistics/565270/apple-devices-ios-version-share-worldwide/

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
2021-03-26 08:29:33 +01:00
Jan Christian Grünhage
d7b3d50a13 chore: add GitLab CI definition
This adds a GitLab CI definition which runs tests, and if they succeed,
build an archive and container image.
2021-03-14 13:20:39 +01:00
Jan Christian Grünhage
bc2e7a7366 chore: optimise Dockerfile
So far, the Dockerfile built hydrogen on the server running the image,
instead of building it during the building of the image. This blew up
the image size immensely and caused node+yarn to run in the resulting
image. This new Dockerfile builds hydrogen in a separate build stage and
then moves the target directory into an nginx based container image,
which takes care of serving the target webroot.

The existing Dockerfile has been moved to Dockerfile-dev for usage as a
development environment. The docs have been adjusted accordingly.

Additionally, this switched from a fixed alpine version of the node
image to the latest alpine version, and changed the container image
references in the `FROM` statements to use the fully qualified
references including the registry domain.
2021-03-14 12:52:35 +01:00
510 changed files with 35003 additions and 11711 deletions

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
node_modules
target

View file

@ -10,6 +10,16 @@ module.exports = {
},
"rules": {
"no-console": "off",
"no-empty": "off"
"no-empty": "off",
"no-prototype-builtins": "off",
"no-unused-vars": "warn"
},
"globals": {
"DEFINE_VERSION": "readonly",
"DEFINE_GLOBAL_HASH": "readonly",
// only available in sw.js
"DEFINE_UNHASHED_PRECACHED_ASSETS": "readonly",
"DEFINE_HASHED_PRECACHED_ASSETS": "readonly",
"DEFINE_HASHED_CACHED_ON_REQUEST_ASSETS": "readonly"
}
};

47
.github/workflows/codechecks.js.yml vendored Normal file
View file

@ -0,0 +1,47 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
# yarn cache setup from https://www.karltarvas.com/2020/12/09/github-actions-cache-yarn-install.html
name: Code checks
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- name: Checkout source
uses: actions/checkout@v2
- name: Install tools
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
# See: https://github.com/actions/cache/blob/main/examples.md#node---yarn
- name: Get Yarn cache directory
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Use Yarn cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
- name: Install dependencies
run: yarn install --prefer-offline --frozen-lockfile
- name: Unit tests
run: yarn test
- name: Lint
run: yarn run lint-ci
- name: Typescript
run: yarn run tsc

44
.github/workflows/docker-publish.yml vendored Normal file
View file

@ -0,0 +1,44 @@
name: Container Image
on:
push:
branches: [ master ]
tags: [ 'v*' ]
pull_request:
branches: [ master ]
env:
IMAGE_NAME: ${{ github.repository }}
REGISTRY: ghcr.io
jobs:
push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

4
.gitignore vendored
View file

@ -1,5 +1,6 @@
*.sublime-project
*.sublime-workspace
.DS_Store
node_modules
fetchlogs
sessionexports
@ -7,3 +8,6 @@ bundle.js
target
lib
*.tar.gz
.eslintcache
.tmp
tmp/

61
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,61 @@
image: docker.io/alpine
stages:
- test
- build
.yarn-template:
image: docker.io/node
before_script:
- yarn install
cache:
paths:
- node_modules
test:
extends: .yarn-template
stage: test
script:
- yarn test
build:
extends: .yarn-template
stage: build
script:
- yarn build
artifacts:
paths:
- target
.docker-template:
image: docker.io/docker
stage: build
services:
- docker:dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
docker-release:
extends: .docker-template
rules:
- if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
script:
- docker build --pull -t "${CI_REGISTRY_IMAGE}:latest" -t "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}" .
- docker push "${CI_REGISTRY_IMAGE}:latest"
- docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
docker-tags:
extends: .docker-template
rules:
- if: '$CI_COMMIT_TAG && $CI_COMMIT_TAG !~ /^v\d+\.\d+\.\d+$/'
script:
- docker build --pull -t "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}" .
- docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
docker-branches:
extends: .docker-template
rules:
- if: $CI_COMMIT_BRANCH
script:
- docker build --pull -t "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG}" .
- docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG}"

25
.ts-eslintrc.js Normal file
View file

@ -0,0 +1,25 @@
module.exports = {
root: true,
env: {
"browser": true,
"es6": true
},
extends: [
// "plugin:@typescript-eslint/recommended",
// "plugin:@typescript-eslint/recommended-requiring-type-checking",
],
parser: '@typescript-eslint/parser',
parserOptions: {
"ecmaVersion": 2020,
"sourceType": "module",
"project": "./tsconfig.json"
},
plugins: [
'@typescript-eslint',
],
rules: {
"@typescript-eslint/no-floating-promises": 2,
"@typescript-eslint/no-misused-promises": 2,
"semi": ["error", "always"]
}
};

18
.woodpecker.yml Normal file
View file

@ -0,0 +1,18 @@
pipeline:
buildfrontend:
image: node:16
commands:
- yarn install --prefer-offline --frozen-lockfile
- yarn test
- yarn run lint-ci
- yarn run tsc
- yarn build
deploy:
image: python
when:
event: push
branch: master
commands:
- make ci-deploy
secrets: [ GITEA_WRITE_DEPLOY_KEY, LIBREPAGES_DEPLOY_SECRET ]

150
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,150 @@
Contributing code to hydrogen-web
==================================
Everyone is welcome to contribute code to hydrogen-web, provided that they are
willing to license their contributions under the same license as the project
itself. We follow a simple 'inbound=outbound' model for contributions: the act
of submitting an 'inbound' contribution means that the contributor agrees to
license the code under the same terms as the project's overall 'outbound'
license - in this case, Apache Software License v2 (see
[LICENSE](LICENSE)).
How to contribute
-----------------
The preferred and easiest way to contribute changes to the project is to fork
it on github, and then create a pull request to ask us to pull your changes
into our repo (https://help.github.com/articles/using-pull-requests/)
We use GitHub's pull request workflow to review the contribution, and either
ask you to make any refinements needed or merge it and make them ourselves.
Things that should go into your PR description:
* References to any bugs fixed by the change (in GitHub's `Fixes` notation)
* Describe the why and what is changing in the PR description so it's easy for
onlookers and reviewers to onboard and context switch.
* If your PR makes visual changes, include both **before** and **after** screenshots
to easily compare and discuss what's changing.
* Include a step-by-step testing strategy so that a reviewer can check out the
code locally and easily get to the point of testing your change.
* Add comments to the diff for the reviewer that might help them to understand
why the change is necessary or how they might better understand and review it.
We use continuous integration, and all pull requests get automatically tested:
if your change breaks the build, then the PR will show that there are failed
checks, so please check back after a few minutes.
Tests
-----
If your PR is a feature then we require that the PR also includes tests.
These need to test that your feature works as expected and ideally test edge cases too.
Tests are written as unit tests by exporting a `tests` function from the file to be tested.
The function returns an object where the key is the test label, and the value is a
function that accepts an [assert](https://nodejs.org/api/assert.html) object, and return a Promise or nothing.
Note that there is currently a limitation that files that are not indirectly included from `src/platform/web/main.js` won't be found by the runner.
You can run the tests by running `yarn test`.
This uses the [impunity](https://github.com/bwindels/impunity) runner.
We don't require tests for bug fixes.
In the future we may formalise this more.
Code style
----------
The js-sdk aims to target TypeScript/ES6. All new files should be written in
TypeScript and existing files should use ES6 principles where possible.
Please disable any automatic formatting tools you may have active.
If present, you'll be asked to undo any unrelated whitespace changes during code review.
Members should not be exported as a default export in general.
In general, avoid using `export default`.
The remaining code-style for hydrogen is [in the process of being documented](codestyle.md), but
contributors are encouraged to read the
[code style document for matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md)
and follow the principles set out there.
Please ensure your changes match the cosmetic style of the existing project,
and ***never*** mix cosmetic and functional changes in the same commit, as it
makes it horribly hard to review otherwise.
Attribution
-----------
If you change or create a file, feel free to add yourself to the copyright holders
in the license header of that file.
Sign off
--------
In order to have a concrete record that your contribution is intentional
and you agree to license it under the same terms as the project's license, we've
adopted the same lightweight approach that the Linux Kernel
(https://www.kernel.org/doc/Documentation/SubmittingPatches), Docker
(https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other
projects use: the DCO (Developer Certificate of Origin:
http://developercertificate.org/). This is a simple declaration that you wrote
the contribution or otherwise have the right to contribute it to Matrix:
```
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
```
If you agree to this for your contribution, then all that's needed is to
include the line in your commit or pull request comment:
```
Signed-off-by: Your Name <your@email.example.org>
```
We accept contributions under a legally identifiable name, such as your name on
government documentation or common-law names (names claimed by legitimate usage
or repute). Unfortunately, we cannot accept anonymous contributions at this
time.
Git allows you to add this signoff automatically when using the `-s` flag to
`git commit`, which uses the name and email set in your `user.name` and
`user.email` git configs.
If you forgot to sign off your commits before making your pull request and are
on Git 2.17+ you can mass signoff using rebase:
```
git rebase --signoff origin/develop
```

View file

@ -1,7 +1,9 @@
FROM node:alpine3.12
RUN apk add --no-cache git
COPY . /code
WORKDIR /code
RUN yarn install
EXPOSE 3000
ENTRYPOINT ["yarn", "start"]
FROM docker.io/node:alpine as builder
RUN apk add --no-cache git python3 build-base
COPY . /app
WORKDIR /app
RUN yarn install \
&& yarn build
FROM docker.io/nginx:alpine
COPY --from=builder /app/target /usr/share/nginx/html

7
Dockerfile-dev Normal file
View file

@ -0,0 +1,7 @@
FROM docker.io/node:alpine
RUN apk add --no-cache git python3 build-base
COPY . /code
WORKDIR /code
RUN yarn install
EXPOSE 3000
ENTRYPOINT ["yarn", "start"]

14
Makefile Normal file
View file

@ -0,0 +1,14 @@
ci-deploy: ## Deploy from CI/CD. Only call from within CI
@if [ "${CI}" != "woodpecker" ]; \
then echo "Only call from within CI. Will re-write your local Git configuration. To override, set export CI=woodpecker"; \
exit 1; \
fi
git config --global user.email "${CI_COMMIT_AUTHOR_EMAIL}"
git config --global user.name "${CI_COMMIT_AUTHOR}"
./scripts/ci.sh --commit-files librepages target "${CI_COMMIT_AUTHOR} <${CI_COMMIT_AUTHOR_EMAIL}>"
./scripts/ci.sh --init "$$GITEA_WRITE_DEPLOY_KEY"
./scripts/ci.sh --deploy ${LIBREPAGES_DEPLOY_SECRET} librepages
./scripts/ci.sh --clean
help: ## Prints help for targets with comments
@cat $(MAKEFILE_LIST) | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

View file

@ -1,3 +1,5 @@
[![status-badge](https://ci.batsense.net/api/badges/mystiq/hydrogen-web/status.svg)](https://ci.batsense.net/mystiq/hydrogen-web)
# Hydrogen
A minimal [Matrix](https://matrix.org/) chat client, focused on performance, offline functionality, and broad browser support. This is work in progress and not yet ready for primetime. Bug reports are welcome, but please don't file any feature requests or other missing things to be on par with Element Web.
@ -10,11 +12,34 @@ Hydrogen's goals are:
- It is a standalone webapp, but can also be easily embedded into an existing website/webapp to add chat capabilities.
- Loading (unused) parts of the application after initial page load should be supported
For embedded usage, see the [SDK instructions](doc/SDK.md).
If you find this interesting, come and discuss on [`#hydrogen:matrix.org`](https://matrix.to/#/#hydrogen:matrix.org).
# How to use
Hydrogen is deployed to [hydrogen.element.io](https://hydrogen.element.io). You can run it locally `yarn install` (only the first time) and `yarn start` in the terminal, and point your browser to `http://localhost:3000`. If you prefer, you can also [use docker](doc/docker.md).
Hydrogen is deployed to [hydrogen.element.io](https://hydrogen.element.io). You can also deploy Hydrogen on your own web server:
1. Download the [latest release package](https://github.com/vector-im/hydrogen-web/releases).
1. Extract the package to the public directory of your web server.
1. If this is your first deploy:
1. copy `config.sample.json` to `config.json` and if needed, make any modifications (unless you've set up your own [sygnal](https://github.com/matrix-org/sygnal) instance, you don't need to change anything in the `push` section).
1. Disable caching entirely on the server for:
- `index.html`
- `sw.js`
- `config.json`
- All theme manifests referenced in the `themeManifests` of `config.json`, these files are typically called `theme-{name}.json`.
These resources will still be cached client-side by the service worker. Because of this; you'll still need to refresh the app twice before config.json changes are applied.
## Set up a dev environment
You can run Hydrogen locally by the following commands in the terminal:
- `yarn install` (only the first time)
- `yarn start` in the terminal
Now point your browser to `http://localhost:3000`. If you prefer, you can also [use docker](doc/docker.md).
# FAQ

View file

@ -8,3 +8,5 @@
otherwise it becomes hard to remember what was a default/named export
- should we return promises from storage mutation calls? probably not, as we don't await them anywhere. only read calls should return promises?
- we don't anymore
- don't use these features, as they are not widely enough supported.
- [lookbehind in regular expressions](https://caniuse.com/js-regexp-lookbehind)

View file

@ -1,5 +1,15 @@
# FAQ
## What browsers are supported?
Internet Explorer 11, Chrome [1], Firefox [1] (not in a private window), Edge [1], Safari [1] and any mobile versions of these. It will probably also work on any derivatives of these.
1: Because of https://github.com/vector-im/hydrogen-web/issues/230, only [more recent versions](https://caniuse.com/mdn-javascript_operators_optional_chaining) are supported.
TorBrowser ships a crippled IndexedDB implementation and will not work. At some point we should support a memory store as a fallback, but that will still give a sub-par experience with end-to-end encryption.
It used work in pre-webkit Edge, to have it work on Windows Phone, but that support has probably bit-rotted as it isn't tested anymore.
## Is there a way to run the app as a desktop app?
You can install Hydrogen as a PWA using Chrome/Chromium on any platform or Edge on Windows. Gnome Web/Ephiphany also allows to "Install site as web application". There is no Electron build of Hydrogen, and there will likely be none in the near future, as Electron complicates the release process considerably. Once Hydrogen is more mature and feature complete, we might reconsider and use [Tauri](https://tauri.studio) if there are compelling use cases not possible with PWAs. For now though, we want to keep development and releasing fast and nimble ;)
@ -12,18 +22,14 @@ If you can't find an easy way to locate the feature you are looking for, then th
That's not yet a feature, as hydrogen just uses a single line text box for message input for now.
### Hmm does Hydrogen not support leaving rooms? I left some rooms via Element and they moved to "Historical" but nothing happened on this end.
Indeed :) [Joining](https://github.com/vector-im/hydrogen-web/issues/28) and [leaving](https://github.com/vector-im/hydrogen-web/issues/147) isn't implemented yet, just haven't gotten around to it.
## How can I verify my session from Element?
You can only verify by comparing keys manually currently. In Element, go to your own profile in the right panel, click on the Hydrogen device and select Manually Verify by Text. The session key displayed should be the same as in the Hydrogen settings. You can't yet mark your Element session as trusted from Hydrogen.
## I want to host my own Hydrogen, how do I do that?
There are no published builds at this point. You need to checkout the version you want to build, or master if you want to run bleeding edge, and run `yarn init` and then `yarn build` in a console (and install nodejs > 14 and yarn if you haven't yet). Now you should find all the files needed to host Hydrogen in the `target/` folder, just copy them all over to your server. As always, don't host your client on the same [origin](https://web.dev/same-origin-policy/#what's-considered-same-origin) as your homeserver.
Published builds can be found at https://github.com/vector-im/hydrogen-web/releases. For building your own, you need to checkout the version you want to build, or master if you want to run bleeding edge, and run `yarn install` and then `yarn build` in a console (and install nodejs >= 15 and yarn if you haven't yet). Now you should find all the files needed to host Hydrogen in the `target/` folder, just copy them all over to your server. As always, don't host your client on the same [origin](https://web.dev/same-origin-policy/#what's-considered-same-origin) as your homeserver.
## I want to embed Hydrogen in my website, how should I do that?
There are no npm modules yet published for Hydrogen. The easiest is probably to setup your website project, do yarn/npm init if you haven't yet, then add the hydrogen repo as a git http dependency, and import the files/classes you want to use from Hydrogen. Feel free to ask which classes you need as the documentation is lacking somewhat still. Note that at this early, pre 1.0 stage of the project, there is no promise of API stability yet.
Hydrogen aims to be usable as an SDK, and while it is still early days, you can find some documentation how to do that in [SDK.md](SDK.md).

11
doc/IMPORT-ISSUES.md Normal file
View file

@ -0,0 +1,11 @@
## How to import common-js dependency using ES6 syntax
---
Until [#6632](https://github.com/vitejs/vite/issues/6632) is fixed, such imports should be done as follows:
```ts
import * as pkg from "off-color";
// @ts-ignore
const offColor = pkg.offColor ?? pkg.default.offColor;
```
This way build, dev server and unit tests should all work.

116
doc/SDK.md Normal file
View file

@ -0,0 +1,116 @@
# Hydrogen View SDK
The Hydrogen view SDK allows developers to integrate parts of the Hydrogen application into the UI of their own application. Hydrogen is written with the MVVM pattern, so to construct a view, you'd first construct a view model, which you then pass into the view. For most view models, you will first need a running client.
## Example
The Hydrogen SDK requires some assets to be shipped along with your app for things like downloading attachments, and end-to-end encryption. A convenient way to make this happen is provided by the SDK (importing `hydrogen-view-sdk/paths/vite`) but depends on your build system. Currently, only [vite](https://vitejs.dev/) is supported, so that's what we'll be using in the example below.
You can create a vite project using the following commands:
```sh
# you can pick "vanilla-ts" here for project type if you're not using react or vue
yarn create vite
cd <your-project-name>
yarn
yarn add hydrogen-view-sdk
```
You should see a `index.html` in the project root directory, containing an element with `id="app"`. Add the attribute `class="hydrogen"` to this element, as the CSS we'll include from the SDK assumes for now that the app is rendered in an element with this classname.
If you go into the `src` directory, you should see a `main.ts` file. If you put this code in there, you should see a basic timeline after login and initial sync have finished (might take a while before you see anything on the screen actually).
You'll need to provide the username and password of a user that is already in the [#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) room (or change the room id).
```ts
import {
Platform,
Client,
LoadStatus,
createNavigation,
createRouter,
RoomViewModel,
TimelineView,
viewClassForTile
} from "hydrogen-view-sdk";
import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
import workerPath from 'hydrogen-view-sdk/main.js?url';
import olmWasmPath from '@matrix-org/olm/olm.wasm?url';
import olmJsPath from '@matrix-org/olm/olm.js?url';
import olmLegacyJsPath from '@matrix-org/olm/olm_legacy.js?url';
const assetPaths = {
downloadSandbox: downloadSandboxPath,
worker: workerPath,
olm: {
wasm: olmWasmPath,
legacyBundle: olmLegacyJsPath,
wasmBundle: olmJsPath
}
};
import "hydrogen-view-sdk/assets/theme-element-light.css";
// OR import "hydrogen-view-sdk/assets/theme-element-dark.css";
async function main() {
const app = document.querySelector<HTMLDivElement>('#app')!
const config = {};
const platform = new Platform({container: app, assetPaths, config, options: { development: import.meta.env.DEV }});
const navigation = createNavigation();
platform.setNavigation(navigation);
const urlRouter = createRouter({
navigation: navigation,
history: platform.history
});
urlRouter.attach();
const client = new Client(platform);
const loginOptions = await client.queryLogin("matrix.org").result;
client.startWithLogin(loginOptions.password("username", "password"));
await client.loadStatus.waitFor((status: string) => {
return status === LoadStatus.Ready ||
status === LoadStatus.Error ||
status === LoadStatus.LoginFailed;
}).promise;
if (client.loginFailure) {
alert("login failed: " + client.loginFailure);
} else if (client.loadError) {
alert("load failed: " + client.loadError.message);
} else {
const {session} = client;
// looks for room corresponding to #element-dev:matrix.org, assuming it is already joined
const room = session.rooms.get("!bEWtlqtDwCLFIAKAcv:matrix.org");
const vm = new RoomViewModel({
room,
ownUserId: session.userId,
platform,
urlCreator: urlRouter,
navigation,
});
await vm.load();
const view = new TimelineView(vm.timelineViewModel, viewClassForTile);
app.appendChild(view.mount());
}
}
main();
```
## Typescript support
Typescript support is not yet available while we're converting the Hydrogen codebase to Typescript.
In your `src` directory, you'll need to add a `.d.ts` (can be called anything, e.g. `deps.d.ts`)
containing this snippet to make Typescript not complain that `hydrogen-view-sdk` doesn't have types:
```ts
declare module "hydrogen-view-sdk";
```
## API Stability
This library follows semantic versioning; there is no API stability promised as long as the major version is still 0. Once 1.0.0 is released, breaking changes will be released with a change in major versioning.
## Third-party licenses
This package bundles the bs58 package ([license](https://github.com/cryptocoinjs/bs58/blob/master/LICENSE)), and the Inter font ([license](https://github.com/rsms/inter/blob/master/LICENSE.txt)).

204
doc/THEMING.md Normal file
View file

@ -0,0 +1,204 @@
# Theming Documentation
## Basic Architecture
A **theme collection** in Hydrogen is represented by a `manifest.json` file and a `theme.css` file.
The manifest specifies variants (eg: dark,light ...) each of which is a **theme** and maps to a single css file in the build output.
Each such theme is produced by changing the values of variables in the base `theme.css` file with those specified in the variant section of the manifest:
![](images/theming-architecture.png)
More in depth explanations can be found in later sections.
## Structure of `manifest.json`
[See theme.ts](../src/platform/types/theme.ts)
## Variables
CSS variables specific to a particular variant are specified in the `variants` section of the manifest:
```json=
"variants": {
"light": {
...
"variables": {
"background-color-primary": "#fff",
"text-color": "#2E2F32",
}
},
"dark": {
...
"variables": {
"background-color-primary": "#21262b",
"text-color": "#fff",
}
}
}
```
These variables will appear in the css file (theme.css):
```css=
body {
background-color: var(--background-color-primary);
color: var(--text-color);
}
```
During the build process, this would result in the creation of two css files (one for each variant) where the variables are substitued with the corresponding values specified in the manifest:
*element-light.css*:
```css=
body {
background-color: #fff;
color: #2E2F32;
}
```
*element-dark.css*:
```css=
body {
background-color: #21262b;
color: #fff;
}
```
## Derived Variables
In addition to simple substitution of variables in the stylesheet, it is also possible to instruct the build system to first produce a new value from the base variable value before the substitution.
Such derived variables have the form `base_css_variable--operation-arg` and can be read as:
apply `operation` to `base_css_variable` with argument `arg`.
Continuing with the previous example, it possible to specify:
```css=
.left-panel {
/* background color should be 20% more darker
than background-color-primary */
background-color: var(--background-color-primary--darker-20);
}
```
Currently supported operations are:
| Operation | Argument | Operates On |
| -------- | -------- | -------- |
| darker | percentage | color |
| lighter | percentage | color |
## Aliases
It is possible give aliases to variables in the `theme.css` file:
```css=
:root {
font-size: 10px;
/* Theme aliases */
--icon-color: var(--background-color-secondary--darker-40);
}
```
It is possible to further derive from these aliased variables:
```css=
div {
background: var(--icon-color--darker-20);
--my-alias: var(--icon-color--darker-20);
/* Derive from aliased variable */
color: var(--my-alias--lighter-15);
}
```
## Colorizing svgs
Along with a change in color-scheme, it may be necessary to change the colors in the svg icons and images.
This can be done by supplying the preferred colors with query parameters:
`my-awesome-logo.svg?primary=base-variable-1&secondary=base-variable-2`
This instructs the build system to colorize the svg with the given primary and secondary colors.
`base-variable-1` and `base-variable-2` are the css-variables specified in the `variables` section of the manifest.
For colorizing svgs, the source svg must use `#ff00ff` as the primary color and `#00ffff` as the secondary color:
| ![](images/svg-icon-example.png) | ![](images/coloring-process.png) |
| :--: |:--: |
| **original source image** | **transformation process** |
## Creating your own theme variant in Hydrogen
If you're looking to change the color-scheme of the existing Element theme, you only need to add your own variant to the existing `manifest.json`.
The steps are fairly simple:
1. Copy over an existing variant to the variants section of the manifest.
2. Change `dark`, `default` and `name` fields.
3. Give new values to each variable in the `variables` section.
4. Build hydrogen.
## Creating your own theme collection in Hydrogen
If a theme variant does not solve your needs, you can create a new theme collection with a different base `theme.css` file.
1. Create a directory for your new theme-collection under `src/platform/web/ui/css/themes/`.
2. Create `manifest.json` and `theme.css` files within the newly created directory.
3. Populate `manifest.json` with the base css variables you wish to use.
4. Write styles in your `theme.css` file using the base variables, derived variables and colorized svg icons.
5. Tell the build system where to find this theme-collection by providing the location of this directory to the `themeBuilder` plugin in `vite.config.js`:
```json=
...
themeBuilder({
themeConfig: {
themes: {
element: "./src/platform/web/ui/css/themes/element",
awesome: "path/to/theme-directory"
},
default: "element",
},
compiledVariables,
}),
...
```
6. Build Hydrogen.
## Changing the default theme
To change the default theme used in Hydrogen, modify the `defaultTheme` field in `config.json` file (which can be found in the build output):
```json=
"defaultTheme": {
"light": theme-id,
"dark": theme-id
}
```
Here *theme-id* is of the form `theme-variant` where `theme` is the key used when specifying the manifest location of the theme collection in `vite.config.js` and `variant` is the key used in variants section of the manifest.
Some examples of theme-ids are `element-dark` and `element-light`.
To find the theme-id of some theme, you can look at the built-asset section of the manifest in the build output.
This default theme will render as "Default" option in the theme-chooser dropdown. If the device preference is for dark theme, the dark default is selected and vice versa.
**You'll need to reload twice so that Hydrogen picks up the config changes!**
# Derived Theme(Collection)
This allows users to theme Hydrogen without the need for rebuilding. Derived theme collections can be thought of as extensions (derivations) of some existing build time theme.
## Creating a derived theme:
Here's how you create a new derived theme:
1. You create a new theme manifest file (eg: theme-awesome.json) and mention which build time theme you're basing your new theme on using the `extends` field. The base css file of the mentioned theme is used for your new theme.
2. You configure the theme manifest as usual by populating the `variants` field with your desired colors.
3. You add your new theme manifest to the list of themes in `config.json`.
Refresh Hydrogen twice (once to refresh cache, and once to load) and the new theme should show up in the theme chooser.
## How does it work?
For every theme collection in hydrogen, the build process emits a runtime css file which like the built theme css file contains variables in the css code. But unlike the theme css file, the runtime css file lacks the definition for these variables:
CSS for the built theme:
```css
:root {
--background-color-primary: #f2f20f;
}
body {
background-color: var(--background-color-primary);
}
```
and the corresponding runtime theme:
```css
/* Notice the lack of definiton for --background-color-primary here! */
body {
background-color: var(--background-color-primary);
}
```
When hydrogen loads a derived theme, it takes the runtime css file of the extended theme and dynamically adds the variable definition based on the values specified in the manifest. Icons are also colored dynamically and injected as variables using Data URIs.

38
doc/TS-MIGRATION.md Normal file
View file

@ -0,0 +1,38 @@
# Typescript style guide
## Use `type` rather than `interface` for named parameters and POJO return values.
`type` and `interface` can be used somewhat interchangeably, but let's use `type` to describe data and `interface` to describe (polymorphic) behaviour.
Good examples of data are option objects to have named parameters, and POJO (plain old javascript objects) without any methods, just fields.
Also see [this playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBACghgJwgO2AeTMAlge2QZygF4oBvAKCiqmTgFsIAuKfYBLZAcwG5LqATCABs4IAPzNkAVzoAjCAl4BfcuVCQoAYQAWWIfwzY8hEvCSpDuAlABkZPlQDGOITgTNW7LstWOR+QjMUYHtqKGcCNilHYDcAChxMK3xmIIsk4wBKewcoFRVyPzgArV19KAgAD2AUfkDEYNDqCM9o2IQEjIJmHT0DLvxsijCw-ClIDsSjAkzeEebjEIYAuE5oEgADABJSKeSAOloGJSgsQh29433nVwQlDbnqfKA)
## Use `type foo = { [key: string]: any }` for types that you intend to fill in later.
For instance, if you have a method such as:
```js
function load(options) {
// ...
}
```
and you intend to type options at some later point, do:
```ts
type Options = { [key: string]: any}
```
This makes it much easier to add the necessary type information at a later time.
## Use `object` or `Record<string, any>` to describe a type that accepts any javascript object.
Sometimes a function or method may genuinely need to accept any object; eg:
```js
function encodeBody(body) {
// ...
}
```
In this scenario:
- Use `object` if you know that you will not access any property
- Use `Record<string, any>` if you need to access some property
Both usages prevent the type from accepting primitives (eg: string, boolean...).
If using `Record`, ensure that you have guards to check that the properties really do exist.

3
doc/UI/index.md Normal file
View file

@ -0,0 +1,3 @@
# Index for UI code
1. [Rendering DOM elements](./render-dom-elements.md)

View file

@ -0,0 +1,47 @@
tldr; Use `tag` from `ui/general/html.js` to quickly create DOM elements.
## Syntax
---
The general syntax is as follows:
```js
tag.tag_name({attribute1: value, attribute2: value, ...}, [child_elements]);
```
**tag_name** can be any one of the following:
```
br, a, ol, ul, li, div, h1, h2, h3, h4, h5, h6,
p, strong, em, span, img, section, main, article, aside,
pre, button, time, input, textarea, label, form, progress, output, video
```
<br />
eg:
Here is an example HTML segment followed with the code to create it in Hydrogen.
```html
<section class="main-section">
<h1>Demo</h1>
<button class="btn_cool">Click me</button>
</section>
```
```js
tag.section({className: "main-section"},[
tag.h1("Demo"),
tag.button({className:"btn_cool"}, "Click me")
]);
```
<br />
**Note:** In views based on `TemplateView`, you will see `t` used instead of `tag`.
`t` is is `TemplateBuilder` object passed to the render function in `TemplateView`.
Although syntactically similar, they are not functionally equivalent.
Primarily `t` **supports** bindings and event handlers while `tag` **does not**.
```js
// The onClick here wont work!!
tag.button({className:"awesome-btn", onClick: () => this.foo()});
render(t, vm){
// The onClick works here.
t.button({className:"awesome-btn", onClick: () => this.foo()});
}
```

206
doc/UI/ui.md Normal file
View file

@ -0,0 +1,206 @@
## IView components
The [interface](https://github.com/vector-im/hydrogen-web/blob/master/src/platform/web/ui/general/types.ts) adopted by view components is agnostic of how they are rendered to the DOM. This has several benefits:
- it allows Hydrogen to not ship a [heavy view framework](https://bundlephobia.com/package/react-dom@18.2.0) that may or may not be used by its SDK users, and also keep bundle size of the app down.
- Given the interface is quite simple, is should be easy to integrate this interface into the render lifecycle of other frameworks.
- The main implementations used in Hydrogen are [`ListView`](https://github.com/vector-im/hydrogen-web/blob/master/src/platform/web/ui/general/ListView.ts) (rendering [`ObservableList`](https://github.com/vector-im/hydrogen-web/blob/master/src/observable/list/BaseObservableList.ts)s) and [`TemplateView`](https://github.com/vector-im/hydrogen-web/blob/master/src/platform/web/ui/general/TemplateView.ts) (templating and one-way databinding), each only a few 100 lines of code and tailored towards their specific use-case. They work straight with the DOM API and have no other dependencies.
- a common inteface allows us to mix and match between these different implementations (and gradually shift if need be in the future) with the code.
## Templates
### Template language
Templates use a mini-DSL language in pure javascript to express declarative templates. This is basically a very thin wrapper around `document.createElement`, `document.createTextNode`, `node.setAttribute` and `node.appendChild` to quickly create DOM trees. The general syntax is as follows:
```js
t.tag_name({attribute1: value, attribute2: value, ...}, [child_elements]);
t.tag_name(child_element);
t.tag_name([child_elements]);
```
**tag_name** can be [most HTML or SVG tags](https://github.com/vector-im/hydrogen-web/blob/master/src/platform/web/ui/general/html.ts#L102-L110).
eg:
Here is an example HTML segment followed with the code to create it in Hydrogen.
```html
<section class="main-section">
<h1>Demo</h1>
<button class="btn_cool">Click me</button>
</section>
```
```js
t.section({className: "main-section"},[
t.h1("Demo"),
t.button({className:"btn_cool"}, "Click me")
]);
```
All these functions return DOM element nodes, e.g. the result of `document.createElement`.
### TemplateView
`TemplateView` builds on top of templating by adopting the IView component model and adding event handling attributes, sub views and one-way databinding.
In views based on `TemplateView`, you will see a render method with a `t` argument.
`t` is `TemplateBuilder` object passed to the render function in `TemplateView`. It also takes a data object to render and bind to, often called `vm`, short for view model from the MVVM pattern Hydrogen uses.
You either subclass `TemplateView` and override the `render` method:
```js
class MyView extends TemplateView {
render(t, vm) {
return t.div(...);
}
}
```
Or you pass a render function to `InlineTemplateView`:
```js
new InlineTemplateView(vm, (t, vm) => {
return t.div(...);
});
```
**Note:** the render function is only called once to build the initial DOM tree and setup bindings, etc ... Any subsequent updates to the DOM of a component happens through bindings.
#### Event handlers
Any attribute starting with `on` and having a function as a value will be attached as an event listener on the given node. The event handler will be removed during unmounting.
```js
t.button({onClick: evt => {
vm.doSomething(evt.target.value);
}}, "Click me");
```
#### Subviews
`t.view(instance)` will mount the sub view (can be any IView) and return its root node so it can be attached in the DOM tree.
All subviews will be unmounted when the parent view gets unmounted.
```js
t.div({className: "Container"}, t.view(new ChildView(vm.childViewModel)));
```
#### One-way data-binding
A binding couples a part of the DOM to a value on the view model. The view model emits an update when any of its properties change, to which the view can subscribe. When an update is received by the view, it will reevaluate all the bindings, and update the DOM accordingly.
A binding can appear in many places where a static value can usually be used in the template tree.
To create a binding, you pass a function that maps the view value to a static value.
##### Text binding
```js
t.p(["I've got ", vm => vm.counter, " beans"])
```
##### Attribute binding
```js
t.button({disabled: vm => vm.isBusy}, "Submit");
```
##### Class-name binding
```js
t.div({className: {
button: true,
active: vm => vm.isActive
}})
```
##### Subview binding
So far, all the bindings can only change node values within our tree, but don't change the structure of the DOM. A sub view binding allows you to conditionally add a subview based on the result of a binding function.
All sub view bindings return a DOM (element or comment) node and can be directly added to the DOM tree by including them in your template.
###### map
`t.mapView` allows you to choose a view based on the result of the binding function:
```js
t.mapView(vm => vm.count, count => {
return count > 5 ? new LargeView(count) : new SmallView(count);
});
```
Every time the first or binding function returns a different value, the second function is run to create a new view to replace the previous view.
You can also return `null` or `undefined` from the second function to indicate a view should not be rendered. In this case a comment node will be used as a placeholder.
There is also a `t.map` which will create a new template view (with the same value) and you directly provide a render function for it:
```js
t.map(vm => vm.shape, (shape, t, vm) => {
switch (shape) {
case "rect": return t.rect();
case "circle": return t.circle();
}
})
```
###### if
`t.ifView` will render the subview if the binding returns a truthy value:
```js
t.ifView(vm => vm.isActive, vm => new View(vm.someValue));
```
You equally have `t.if`, which creates a `TemplateView` and passes you the `TemplateBuilder`:
```js
t.if(vm => vm.isActive, (t, vm) => t.div("active!"));
```
##### Side-effects
Sometimes you want to imperatively modify your DOM tree based on the value of a binding.
`mapSideEffect` makes this easy to do:
```js
let node = t.div();
t.mapSideEffect(vm => vm.color, (color, oldColor) => node.style.background = color);
return node;
```
**Note:** you shouldn't add any bindings, subviews or event handlers from the side-effect callback,
the safest is to not use the `t` argument at all.
If you do, they will be added every time the callback is run and only cleaned up when the view is unmounted.
#### `tag` vs `t`
If you don't need a view component with data-binding, sub views and event handler attributes, the template language also is available in `ui/general/html.js` without any of these bells and whistles, exported as `tag`. As opposed to static templates with `tag`, you always use
`TemplateView` as an instance of a class, as there is some extra state to keep track (bindings, event handlers and subviews).
Although syntactically similar, `TemplateBuilder` and `tag` are not functionally equivalent.
Primarily `t` **supports** bindings and event handlers while `tag` **does not**. This is because to remove event listeners, we need to keep track of them, and thus we need to keep this state somewhere which
we can't do with a simple function call but we can insite the TemplateView class.
```js
// The onClick here wont work!!
tag.button({className:"awesome-btn", onClick: () => this.foo()});
class MyView extends TemplateView {
render(t, vm){
// The onClick works here.
t.button({className:"awesome-btn", onClick: () => this.foo()});
}
}
```
## ListView
A view component that renders and updates a list of sub views for every item in a `ObservableList`.
```js
const list = new ListView({
list: someObservableList
}, listValue => return new ChildView(listValue))
```
As items are added, removed, moved (change position) and updated, the DOM will be kept in sync.
There is also a `LazyListView` that only renders items in and around the current viewport, with the restriction that all items in the list must be rendered with the same height.
### Sub view updates
Unless the `parentProvidesUpdates` option in the constructor is set to `false`, the ListView will call the `update` method on the child `IView` component when it receives an update event for one of the items in the `ObservableList`.
This way, not every sub view has to have an individual listener on it's view model (a value from the observable list), and all updates go from the observable list to the list view, who then notifies the correct sub view.

View file

@ -1,22 +1,58 @@
## Warning
Usage of docker is a third-party contribution and not actively tested, used or supported by the main developer(s).
Having said that, you can also use Docker to create a local dev environment.
Having said that, you can also use Docker to create a local dev environment or a production deployment.
## Dev environment
In this repository, create a Docker image:
docker build -t hydrogen .
```
docker build -t hydrogen-dev -f Dockerfile-dev .
```
Then start up a container from that image:
docker run \
--name hydrogen-dev \
--publish 3000:3000 \
--volume "$PWD":/code \
--interactive \
--tty \
--rm \
hydrogen
```
docker run \
--name hydrogen-dev \
--publish 3000:3000 \
--volume "$PWD":/code \
--interactive \
--tty \
--rm \
hydrogen-dev
```
Then point your browser to `http://localhost:3000`. You can see the server logs in the terminal where you started the container.
To stop the container, simply hit `ctrl+c`.
## Production deployment
### Build or pull image
In this repository, create a Docker image:
```
docker build -t hydrogen .
```
Or, pull the docker image from GitLab:
```
docker pull registry.gitlab.com/jcgruenhage/hydrogen-web
docker tag registry.gitlab.com/jcgruenhage/hydrogen-web hydrogen
```
### Start container image
Then, start up a container from that image:
```
docker run \
--name hydrogen \
--publish 80:80 \
hydrogen
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -34,8 +34,8 @@
- verify signature on key
- ??? what about inbound/outbound sessions? do they require multiple OlmSession objects?
- doesn't look like it, more like a way to start the session but once started (type=1), they are equivalent?
- for outbound, see file:///home/bwindels/Downloads/matrix-docs/End-to-End%20Encryption%20implementation%20guide%20%7C%20Matrix.org.html#starting-an-olm-session
- for inbound, see: file:///home/bwindels/Downloads/matrix-docs/End-to-End%20Encryption%20implementation%20guide%20|%20Matrix.org.html#handling-an-mroomencrypted-event
- for outbound, see https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#starting-an-olm-session
- for inbound, see: https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#handling-an-mroomencrypted-event
- so in this case, it would the session would be created as an outbound session.
- store pickled, index by curve25519 identity key?
- get from storage if exists and unpickle
@ -68,10 +68,10 @@
- mark them as published in account
- update picked session in storage
- implement encrypting olm messages
- roughly file:///home/bwindels/Downloads/matrix-docs/End-to-End%20Encryption%20implementation%20guide%20|%20Matrix.org.html#encrypting-an-event-with-olm
- roughly https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#encrypting-an-event-with-olm
- packaging as m.room.encrypted event
- implement decrypting olm messages
- roughly file:///home/bwindels/Downloads/matrix-docs/End-to-End%20Encryption%20implementation%20guide%20|%20Matrix.org.html#handling-an-mroomencrypted-event
- roughly https://matrix.org/docs/guides/end-to-end-encryption-implementation-guide#handling-an-mroomencrypted-event
- decrypt with libolm
- verify signature
- check message index, etc to detect replay attacks

View file

@ -0,0 +1,25 @@
# Replying to pending messages
The matrix spec requires clients capable of rich replies (that would be us once replies work) to include fallback (textual in `body` and structured in `formatted_body`) that can be rendered
by clients that do not natively support rich replies (that would be us at the time of writing). The schema for the fallback is as follows:
```
<mx-reply>
<blockquote>
<a href="https://matrix.to/#/!somewhere:example.org/$event:example.org">In reply to</a>
<a href="https://matrix.to/#/@alice:example.org">@alice:example.org</a>
<br />
<!-- This is where the related event's HTML would be. -->
</blockquote>
</mx-reply>
```
There's a single complication here for pending events: we have `$event:example.org` in the schema (the `In reply to` link), and it must
be present _within the content_, inside `formatted_body`. The issue is that, if we are queuing a reply to a pending event,
we don't know its remote ID. All we know is its transaction ID on our end. If we were to use that while formatting the message,
we'd be sending messages that contain our internal transaction IDs instead of proper matrix event identifiers.
To solve this, we'd need `SendQueue`, whenever it receives a remote echo, to update pending events that are replies with their
`relatedEventId`. This already happens, and the `event_id` field in `m.relates_to` is updated. But we'd need to extend this
to adjust the messages' `formatted_body` with the resolved remote ID, too.
How do we safely do this, without accidentally substituting event IDs into places in the body where they were not intended?

View file

@ -3,6 +3,19 @@ Relations and redactions
events that refer to another event will need support in the SyncWriter, Timeline and SendQueue I think.
SyncWriter will need to resolve the related remote id to a [fragmentId, eventIndex] and persist that on the event that relates to some other. Same for SendQueue? If unknown remote id, not much to do. However, once the remote id comes in, how do we handle it correctly? We might need a index on m.relates_to/event_id? I'd rather avoid that if possible, as that becomes useless once we have the target event of the relationship (we store the relations on the target event (see "One fetch" below) and have the target event id on the relation so can go both ways). I'm not sure this index will be completely useless actually. For edits, we'll want to be able to list all edits. For reactions, we'll want to fetch the authors and timestamps. For replies, we want to render the origin event and not use the fallback text? It is true though that only a minority of the events will have a related_to event id, so I wonder if it is faster to put it in a different store? Perhaps a prototype can clarify ...
`event_relations` store could be this:
{
sourceEventId:
targetEventId:
rel_type:
roomId:
}
`{"key": "!bEWtlqtDwCLFIAKAcv:matrix.org|$apmyieZOI5vm4DzjEFzjbRiZW9oeQQR21adM6A6eRwM|m.annotation|m.reaction|$jSisozR3is5XUuDZXD5cyaVMOQ5_BtFS3jKfcP89MOM"}`
or actually stored like `roomId|targetEventId|rel_type|sourceEventId`. How can we get the last edit? They are sorted by origin_server_ts IIRC? Should this be part of the key? Solved: we store the event id of a replacement on the target event
We should look into what part of the relationships will be present on the event once it is received from the server (e.g. m.replace might be evident, but not all the reaction events?). If not, we could add a object store with missing relation targets.
The timeline can take incoming events from both the SendQueue and SyncWriter, and see if their related to fragmentId/eventIndex is in view, and then update it?
@ -22,3 +35,235 @@ wrt to how to store relations in indexeddb, we could store all local ids of rela
Replies should definitely use this relation mechanism, so we can easily show the most up to date version of the replied-to event.
Redactions can de done separately
For replies (or references in general?), we do need to load the referred-to event in a second read. For reactions and edits, they will already be stored on the target event.
## Example events from the wild
### Reaction
```json
{
"content": {
"m.relates_to": {
"event_id": "$apmyieZOI5vm4DzjEFzjbRiZW9oeQQR21adM6A6eRwM",
"key": "👍️",
"rel_type": "m.annotation"
}
},
"origin_server_ts": 1621284357314,
"sender": "@charly:matrix.org",
"type": "m.reaction",
"unsigned": {
"age": 64140856
},
"event_id": "$jSisozR3is5XUuDZXD5cyaVMOQ5_BtFS3jKfcP89MOM",
"room_id": "!bEWtlqtDwCLFIAKAcv:matrix.org"
}
```
### Edit
```json
{
"content": {
"body": " * ...",
"m.new_content": {
"body": "...",
"msgtype": "m.text"
},
"m.relates_to": {
"event_id": "$OXL0yk18y-VG3DuTybVh9j9cvdjjnnzWbBKY-QPXJ-0",
"rel_type": "m.replace"
},
"msgtype": "m.text"
},
"origin_server_ts": 1621264902371,
"room_id": "!bEWtlqtDwCLFIAKAcv:matrix.org",
"sender": "@alice:matrix.org",
"type": "m.room.message",
"unsigned": {
"age": 83636544
},
"event_id": "$Z7sFSKWtLTFoMMabkPFe0PSKWpkakjWUkYQeBU8IHVc",
"user_id": "@alice:matrix.org",
"age": 83636544
}
```
### Reply
```json
{
"content": {
"body": "...",
"format": "org.matrix.custom.html",
"formatted_body": "...",
"m.relates_to": {
"m.in_reply_to": {
"event_id": "$rGD9iQ93UmopkkagJ0tW_FHATa8IrvABg9cM_tNUvu4"
}
},
"msgtype": "m.text"
},
"origin_server_ts": 1621242338597,
"room_id": "!bEWtlqtDwCLFIAKAcv:matrix.org",
"sender": "@bob:matrix.org",
"type": "m.room.message",
"unsigned": {
"age": 106408661,
"m.relations": {
"m.annotation": {
"chunk": [
{
"type": "m.reaction",
"key": "👍️",
"count": 1
}
]
}
}
},
"event_id": "$yS_n5n3cIO2aTtek0_2ZSlv-7g4YYR2zKrk2mFCW_q4",
"user_id": "@bob:matrix.org",
"age": 106408661
}
```
### Remaining spec issues
- m.in_reply_to vs rel_type
- reactions in unsigned can't be deduplicated
- how to sort edits? for now we went with origin_server_ts
- do we say anything about events of a different type replacing an event?
- do we specify that replies should be to the original event, not the edit?
## What to store denormalized on the event itself?
```json
{
"reactions": {
"👍": {"count": 3, "me": true, "firstTimestamp": 2323989},
"👋": {"count": 1, "me": false, "firstTimestamp": 2323989}
},
"replacingEvent": {
"event_id": "$abc",
"origin_server_ts": ?,
"content": {}
}
}
```
we only need the m.new_content and event id of the replacing event, even timestamp we can load the event for on hover?
store the replacing event along the original event because we need to keep the original event along somewhere, but for displaying purposes, we'd use the content of the replacingEvent. Should we just store the content of the replacing event? Or even just the `m.new_content`? Could make sense, but perhaps also store the new timestamp along. How about whem somebody than the sender edits?
# Aggregation
what do we do with the aggregated timestamps? do we store them? if so, where?
when we hover reactions, we want to show the authors, rather than the timestamp, so we'll need to call /relations for that anyway. so no need to store the timestamp?
`/relations` is in fact a bit the server-side version of our `event_relations` store
## Dealing with gappy syncs
Doesn't look like synapse currently tells us which target events have outdates relations after a gappy sync. MSC 2675 proposes `stale_events`, but inspecting network traffic, that doesn't seem to be implemented right now.
So, if we locally would need to determine if relations are outdated, we could look if any of the fragments between an event and the last synced event have pagination tokens. Although we have no way to clear this "flag" if we were to fetch the relations after this.
As an initial cut it is probably fine if reactions and edits are outdated unless you scroll up all the way to an event (and hence back-fill), as this is what we'll always do (apart from permalinks).
### Permalinks
So once we do support permalinks, how do we solve this? Element solves this by not storing `/context` and the `/messages` requests around, hence it is always fresh.
We could store the live fragment id in events when we refresh their `/relations`, and if it is not the current live fragment id, you're outdated.
To accurately display anything not in the live fragment, we either need to:
- backfill until there are no more gaps between the event fragment and the live fragment.
- -- there is no way to know how many events this would load.
- ++ that we know which gaps we've already filled
- ++ we need to do this for e2ee rooms anyway
- ++ we need to implement this anyway for non-gappy sync
- ++ we can only do this as an initial cut, especially as we don't support permalinks yet
- Refetch the `/context` and `/messages` for what is on the screen and reconcile.
- ++ we know how much we'll fetch
- -- we need to fetch everything again if we have one small gap
- we store the current live fragment when doing this, so can know:
- if we need to refetch / if there is a gap
- how many gaps we need to fill
- could we fall back to this strategy if the first one takes too long/many events?
- we could pick a heuristic to pick either strategy (like time between syncs or try for x events and if the gap is not closed, give up)?
- Refetch /aggregations for every event
- ++ we don't get the events (we dont need? edits?)
- --- need to do it for every event
- use `stale_events` if we actually implement it one day
- this can work well with the first strategy, we'd store a "relationsStale" flag on the event, and refetch /relations immediately or if scrolled into view.
# API
## Reactions
```js
const reaction = eventEntry.react("👍");
room.sendEvent("m.reaction", reaction);
```
```js
// this is an ObservableMap mapping the key to the count (or rather SortedArray?)
// probably fine to just use a SortedArray to sorts by count, then key
// actually, maybe better to do ObservableMap and store first timestamp so we can support https://github.com/vector-im/element-web/issues/9698 outside of SDK.
const reactions = eventEntry.reactions.sortValues((r1, r2) => r1.count - r2.count);
new ListView({list: reactions}, reaction => new ReactionView(reaction, room));
// reaction has:
reaction.key
reaction.hasMyReaction // how do we get this from the bundled events?
reaction.count
reaction.firstTimestamp
room.sendEvent("m.reaction", reaction.react());
// this won't work as we don't have the event id:
// room.sendRedaction(reaction.redact());
```
## Edits
```js
const replacement = eventEntry.replace({});
room.sendEvent(eventEntry.eventType, replacement);
```
## Replies
```js
const reply = eventEntry.reply({});
room.sendEvent("m.room.message", reply);
```
## Redactions
```js
const redaction = eventEntry.redact();
room.sendRedaction(redaction);
```
All off these reaction and edit entries should probably not be live, and at some point in the future if we need them to be live for some use case, we can add an additional api to make them live with an explicit release mechanism?
```js
// there is no api to get the reactions by sender though, so perhaps we need to load them all and then find our own?
const reactions = await eventEntry.getReactionEntries("👍");
const reaction = reactions.find(r => r.sender = ownUserId);
room.sendRedaction(reaction.redact());
```
```js
const edits = await eventEntry.getEdits();
room.sendRedaction(edits[1].redact());
```
```js
const lastEdit = await eventEntry.getLastEdit();
room.sendRedaction(lastEdit.redact());
```

View file

@ -0,0 +1,17 @@
If we were to render replies in a smart way (instead of relying on the fallback), we would
need to manually find entries that are pointed to be `in_reply_to`. Consulting the timeline
code, it seems appropriate to add a `_replyingTo` field to a `BaseEventEntry` (much like we
have `_pendingAnnotations` and `pendingRedactions`). We can then:
* use `TilesCollection`'s `_findTileIdx` to find the tile of the message being replied to,
and put a reference to its tile into the new tile being created (?).
* It doesn't seem appropriate to add an additional argument to TileCreator, but we may
want to re-use tiles instead of creating duplicate ones. Otherwise, of course, `tileCreator`
can create more than one tile from an entry's `_replyingTo` field.
* Resolve `_replyingTo` much like we resolve `redactingEntry` in timeline: search by `relatedTxnId`
and `relatedEventId` if our entry is a reply (we can add an `isReply` flag there).
* This works fine for local entries, which are loaded via an `AsyncMappedList`, but what
about remote entries? They are not loaded asynchronously, and the fact that they are
not a derived collection is used throughout `Timeline`.
* Entries that don't have replies that are loadeded (but that are replies) probably need
to be tracked somehow?
* Then, on timeline add, check new IDs and update corresponding entries

109
doc/impl-thoughts/SDK.md Normal file
View file

@ -0,0 +1,109 @@
SDK:
- we need to compile src/lib.ts to javascript, with a d.ts file generated as well. We need to compile to javascript once for cjs and once of es modules. The package.json looks like this:
```
"main": "./dist/index.cjs",
"exports": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"types": "dist/index.d.ts",
```
we don't need to bundle for the sdk case! we might need to do some transpilation to just plain ES6 (e.g. don't assume ?. and ??) we could use a browserslist query for this e.g. `node 14`. esbuild seems to support this as well, tldraw uses esbuild for their build.
one advantage of not bundling the files for the sdk is that you can still use import overrides in the consuming project build settings. is that an idiomatic way of doing things though?
this way we will support typescript, non-esm javascript and esm javascript using libhydrogen as an SDK
got this from https://medium.com/dazn-tech/publishing-npm-packages-as-native-es-modules-41ffbc0a9dea
how about the assets?
we also need to build the app
we need to be able to version libhydrogen independently from hydrogen the app? as any api breaking changes will need a major version increase. we probably want to end up with a monorepo where the app uses the sdk as well and we just use the local code with yarn link?
## Assets
we want to provide scss/sass files, but also css that can be included
https://github.com/webpack/webpack/issues/7353 seems to imply that we just need to include the assets in the published files and from there on it is the consumer of libhydrogen's problem.
how does all of this tie in with vite?
we want to have hydrogenapp be a consumer of libhydrogen, potentially as two packages in a monorepo ... but we want the SDK to expose views and stylesheets... without having an index.html (which would be in hydrogenapp). this seems a bit odd...?
what would be in hydrogenapp actually? just an index.html file?
I'm not sure it makes sense to have them be 2 different packages in a monorepo, they should really be two artifacts from the same directory.
the stylesheets included in libhydrogen are from the same main.css file as is used in the app
https://www.freecodecamp.org/news/build-a-css-library-with-vitejs/
basically, we import the sass file from src/lib.ts so it is included in the assets there too, and we also create a plugin that emits a file for every sass file as suggested in the link above?
we probably want two different build commands for the app and the sdk though, we could have a parent vite config that both build configs extend from?
### Dependency assets
our dependencies should not be bundled for the SDK case. So if we import aesjs, it would be up to the build system of the consuming project to make that import work.
the paths.ts thingy ... we want to make it easy for people to setup the assets for our dependencies (olm), some assets are also part of the sdk itself. it might make sense to make all of the assets there part of the sdk (e.g. bundle olm.wasm and friends?) although shipping crypto, etc ...
perhaps we should have an include file per build system that treats own assets and dep assets the same by including the package name as wel for our own deps:
```js
import _downloadSandboxPath from "@matrix-org/hydrogen-sdk/download-sandbox.html?url";
import _serviceWorkerPath from "@matrix-org/hydrogen-sdk/sw.js?url"; // not yet sure this is the way to do it
import olmWasmPath from "@matrix-org/olm/olm.wasm?url";
import olmJsPath from "@matrix-org/olm/olm.js?url";
import olmLegacyJsPath from "@matrix-org/olm/olm_legacy.js?url";
export const olmPaths = {
wasm: olmWasmPath,
legacyBundle: olmLegacyJsPath,
wasmBundle: olmJsPath,
};
export const downloadSandboxPath = _downloadSandboxPath;
```
we could put this file per build system, as ESM, in dist as well so you can include it to get the paths
## Tooling
- `vite` a more high-level build tool that takes your index.html and turns it into optimized assets that you can host for production, as well as a very fast dev server. is used to have good default settings for our tools, typescript support, and also deals with asset compiling. good dev server. Would be nice to have the same tool for dev and prod. vite has good support for using `import` for anything that is not javascript, where we had an issue with `snowpack` (to get the prod path of an asset).
- `rollup`: inlines
- `lerna` is used to handle multi-package monorepos
- `esbuild`: a js/ts build tool that we could use for building the lower level sdk where no other assets are involved, `vite` uses it for fast dev builds (`rollup` for prod). For now we won't extract a lower level sdk though.
## TODO
- finish vite app build (without IE11 for now?)
- create vite config to build src/lib.ts in cjs and esm, inheriting from a common base config with the app config
- this will create a dist folder with
- the whole source tree in es and cjs format
- an es file to import get the asset paths as they are expected by Platform, per build system
- assets from hydrogen itself:
- css files and any resource used therein
- download-sandbox.html
- a type declaration file (index.d.ts)
## Questions
- can rollup not bundle the source tree and leave modules intact?
- if we can use a function that creates a chunk per file to pass to manualChunks and disable chunk hashing we can probably do this. See https://rollupjs.org/guide/en/#outputmanualchunks
looks like we should be able to disable chunk name hashing with chunkFileNames https://rollupjs.org/guide/en/#outputoptions-object
we should test this with a vite test config
we also need to compile down to ES6, both for the app and for the sdk

View file

@ -3,4 +3,118 @@
- invite_state doesn't update over /sync
- can we reuse room summary? need to clear when joining
- rely on filter operator to split membership=join from membership=invite?
-
- invite_state comes once, and then not again
- only state (no heroes for example, but we do get the members)
- wants:
- different class to represent invited room, with accept or reject method?
- make it somewhat easy to render just joined rooms (rely on filter and still put them all in the same observable map)
- make the transition from invite to joined smooth
- reuse room summary logic?
InvitedRoom
isDM
isEncrypted
name
timestamp
accept()
reject()
JoiningRoom
to store intent of room you joined through directory, invite, or just /join roomid
also joining is retried when coming back online
forget()
Room
so, also taking into account that other types of room we might not want to expose through session.rooms will have invites,
perhaps it is best to expose invites through a different observable collection. You can always join/concat them to show in
the same list.
How do we handle a smooth UI transition when accepting an invite though?
For looking at the room itself:
- we would attach to the Invite event emitter, and we can have a property "joined" that we would update. Then you know you can go look for the room (or even allow to access the room through a property?)
- so this way the view model can know when to switch and signal the view
For the room list:
- the new Room will be added at exactly the same moment the Invite is removed,
so it should already be fairly smooth whether they are rendered in the same list or not.
How will we locate the Invite/Room during sync when we go from invite => join?
- have both adhere to sync target api (e.g. prepareSync, ...) and look in invite map
if room id is not found in room map in session.getroom.
- how do we remove the invite when join?
- we ca
Where to store?
- room summaries?
- do we have an interest in keeping the raw events?
- room versions will add another layer of indirection to the room summaries (or will it? once you've upgraded the room, we don't care too much anymore about the details of the old room? hmmm, we do care about whether it is encrypted or not... we need everything to be able to show the timeline in any case)
Invite => accept() => Room (ends up in session.rooms)
(.type) => Space (ends up in session.spaces)
Invite:
- isEncrypted
- isDM
- type
- id
- name
- avatarUrl
- timestamp
- joinRule (to say wheter you cannot join this room again if you reject)
new "memberships":
joining (when we want to join/are joining but haven't received remote echo yet)
leaving (needed?)
maybe it's slightly overkill to persist the intent of joining or leaving a room,
but I do want a way to local echo joining a room,
so that it immediately appears in the room list when clicking join in the room directory / from a url ... how would we sort these rooms though? we can always add another collection, but I'm not sure invites should be treated the same, they can already local echo on the invite object itself.
since invites don't update, we could, in sync when processing a new join just set a flag on the roomsyncstate if a room is newly created and in writeSync/afterSync check if there is a `session.invites.get(id)` and call `writeSync/afterSync` on it as well. We need to handle leave => invite as well. So don't check for invites only if it is a new room, but also if membership is leave
transitions are:
invite => join
invite => leave
invite => ban
join => left
join => ban
leave => invite
leave => join
leave => ban
ban => leave
none => invite
none => join
none => ban
kick should keep the room & timeline visible (even in room list, until you archive?)
leave should close the room. So explicit archive() step on room ?
Room => leave() => ArchivedRoom (just a Room loaded from archived_room_summaries) => .forget()
=> .forget()
Room receives leave membership
- if sender === state_key, we left, and we archive the room (remove it from the room list, but keep it in storage)
- if sender !== state_key, we got kicked, and we write the membership but don't archive so it stays in the room list until you call archive/forget on the room
when calling room.leave(), do you have to call archive() or forget() after as well? or rather param of leave and stored intent? sounds like non-atomical operation to me ...
we should be able to archive or forget before leave remote echo arrives
if two stores, this could mean we could have both an invite and a room with kicked state for a given room id?
we should avoid key collisions between `session.invites` and `session.rooms` (also `session.archivedRooms` once supported?) in any case,
because if we join them to display in one list, things get complicated.
avoiding key collisions can happen both with 1 or multiple stores for different room states and is just a matter
of carefully removing one state representation before adding another one.
so a kicked or left room would disappear from session.rooms when an invite is synced?
this would prevent you from seeing the old timeline for example, and if you reject, the old state would come back?
# Decisions
- we expose session.invites separate from session.rooms because they are of a different type.
This way, you only have methods on the object that make sense (accept on Room does not make sense, like Invite.openTimeline doesn't make sense)
- we store invites (and likely also archived rooms) in a different store, so that we don't have to clear/add properties where they both differ when transitioning. Also, this gives us the possibility to show the timeline on a room that you have previously joined, as the room summary and invite can exist at the same time. (need to resolve key collision question though for this)
- we want to keep kicked rooms in the room list until explicitly archived
- room id collisions between invites and rooms, can we implement a strategy to prefer invites in the join operator?

View file

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no">
<meta name="application-name" content="Hydrogen Chat"/>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Hydrogen Chat">
<meta name="description" content="A matrix chat application">
<link rel="apple-touch-icon" href="assets/icon-maskable.png">
<link rel="stylesheet" type="text/css" href="src/platform/web/ui/css/main.css">
<link rel="stylesheet" type="text/css" href="src/platform/web/ui/css/themes/element/theme.css" title="Element Theme">
<link rel="alternate stylesheet" type="text/css" href="src/platform/web/ui/css/themes/bubbles/theme.css" title="Bubbles Theme">
</head>
<body class="hydrogen">
<script id="version" type="disabled">
window.HYDROGEN_VERSION = "%%VERSION%%";
window.HYDROGEN_GLOBAL_HASH = "%%GLOBAL_HASH%%";
</script>
<script id="main" type="module">
import {main} from "./src/main.js";
import {Platform} from "./src/platform/web/Platform.js";
main(new Platform(document.body, {
worker: "src/worker.js",
downloadSandbox: "assets/download-sandbox.html",
defaultHomeServer: "matrix.org",
// NOTE: uncomment this if you want the service worker for local development
// serviceWorker: "sw.js",
// NOTE: provide push config if you want push notifs for local development
// see assets/config.json for what the config looks like
// push: {...},
olm: {
wasm: "lib/olm/olm.wasm",
legacyBundle: "lib/olm/olm_legacy.js",
wasmBundle: "lib/olm/olm.js",
}
}, null, {development: true}));
</script>
</body>
</html>

View file

@ -1,16 +1,24 @@
{
"name": "hydrogen-web",
"version": "0.1.43",
"version": "0.3.1",
"description": "A javascript matrix client prototype, trying to minize RAM usage by offloading as much as possible to IndexedDB",
"main": "index.js",
"directories": {
"doc": "doc"
},
"enginesStrict": {
"node": ">=15"
},
"scripts": {
"test": "node_modules/.bin/impunity --entry-point src/main.js --force-esm-dirs lib/ src/",
"start": "node scripts/serve-local.js",
"build": "node --experimental-modules scripts/build.mjs",
"postinstall": "node ./scripts/post-install.js"
"lint": "eslint --cache src/",
"lint-ts": "eslint src/ -c .ts-eslintrc.js --ext .ts",
"lint-ci": "eslint src/",
"test": "impunity --entry-point src/platform/web/main.js src/platform/web/Platform.js --force-esm-dirs lib/ src/ --root-dir src/",
"test:postcss": "impunity --entry-point scripts/postcss/tests/css-compile-variables.test.js scripts/postcss/tests/css-url-to-variables.test.js",
"test:sdk": "yarn build:sdk && cd ./scripts/sdk/test/ && yarn --no-lockfile && node test-sdk-in-esm-vite-build-env.js && node test-sdk-in-commonjs-env.js",
"start": "vite --port 3000",
"build": "vite build && ./scripts/cleanup.sh",
"build:sdk": "./scripts/sdk/build.sh",
"watch:sdk": "./scripts/sdk/build.sh && yarn run vite build -c vite.sdk-lib-config.js --watch"
},
"repository": {
"type": "git",
@ -23,37 +31,36 @@
},
"homepage": "https://github.com/vector-im/hydrogen-web/#readme",
"devDependencies": {
"@babel/core": "^7.11.1",
"@babel/preset-env": "^7.11.0",
"@rollup/plugin-babel": "^5.1.0",
"@rollup/plugin-multi-entry": "^4.0.0",
"autoprefixer": "^10.0.1",
"cheerio": "^1.0.0-rc.3",
"commander": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"acorn": "^8.6.0",
"acorn-walk": "^8.2.0",
"aes-js": "^3.1.2",
"bs58": "^4.0.1",
"core-js": "^3.6.5",
"finalhandler": "^1.1.1",
"impunity": "^1.0.0",
"es6-promise": "https://github.com/bwindels/es6-promise.git#bwindels/expose-flush",
"escodegen": "^2.0.0",
"eslint": "^7.32.0",
"fake-indexeddb": "^3.1.2",
"impunity": "^1.0.9",
"mdn-polyfills": "^5.20.0",
"postcss": "^8.1.1",
"postcss-css-variables": "^0.17.0",
"postcss-flexbugs-fixes": "^4.2.1",
"postcss-import": "^12.0.1",
"postcss-url": "^8.0.0",
"merge-options": "^3.0.4",
"node-html-parser": "^4.0.0",
"postcss-css-variables": "^0.18.0",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-value-parser": "^4.2.0",
"regenerator-runtime": "^0.13.7",
"rollup-plugin-cleanup": "^3.1.1",
"serve-static": "^1.13.2",
"svgo": "^2.8.0",
"text-encoding": "^0.7.0",
"typescript": "^4.7.0",
"vite": "^2.9.8",
"xxhashjs": "^0.2.2"
},
"dependencies": {
"rollup": "^2.26.4",
"@rollup/plugin-commonjs": "^15.0.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"aes-js": "^3.1.2",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
"another-json": "^0.2.0",
"base64-arraybuffer": "^0.2.0",
"bs58": "^4.0.1",
"es6-promise": "https://github.com/bwindels/es6-promise.git#bwindels/expose-flush",
"olm": "https://packages.matrix.org/npm/olm/olm-3.1.4.tgz",
"text-encoding": "^0.7.0"
"dompurify": "^2.3.0",
"off-color": "^2.0.0"
}
}

View file

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script type="text/javascript">
class IDBError extends Error {
constructor(errorEvent) {
const request = errorEvent.target;
const {error} = request;
super(error.message);
this.name = error.name;
this.errorEvent = errorEvent;
}
preventAbort() {
this.errorEvent.preventDefault();
}
}
class AbortError extends Error {
get name() { return "AbortError"; }
}
function reqAsPromise(req) {
return new Promise(function (resolve, reject) {
req.onsuccess = function(e) {
resolve(e.target.result);
};
req.onerror = function(e) {
reject(new IDBError(e));
};
});
}
function txnAsPromise(txn) {
return new Promise((resolve, reject) => {
txn.addEventListener("complete", () => resolve());
txn.addEventListener("abort", event => {
reject(new AbortError());
});
});
}
function Storage(databaseName) {
this._databaseName = databaseName;
this._database = null;
}
Storage.prototype = {
open: function() {
const req = window.indexedDB.open(this._databaseName);
const self = this;
req.onupgradeneeded = function(ev) {
const db = ev.target.result;
const oldVersion = ev.oldVersion;
self._createStores(db, oldVersion);
};
return reqAsPromise(req).then(function() {
self._database = req.result;
});
},
readWriteTxn: function(storeName) {
return this._database.transaction([storeName], "readwrite");
},
readTxn: function(storeName) {
return this._database.transaction([storeName], "readonly");
},
_createStores: function(db) {
db.createObjectStore("foos", {keyPath: "id"});
}
};
async function main() {
const storage = new Storage("idb-continue-on-constrainterror");
await storage.open();
const txn1 = storage.readWriteTxn("foos");
const store = txn1.objectStore("foos");
await reqAsPromise(store.clear());
console.log("first foo read back", await reqAsPromise(store.get(5)));
await reqAsPromise(store.add({id: 5, name: "Mr Foo"}));
try {
await reqAsPromise(store.add({id: 5, name: "bar"}));
} catch (err) {
console.log("we did get an error", err.name);
err.preventAbort();
}
await txnAsPromise(txn1);
const txn2 = storage.readTxn("foos");
const store2 = txn2.objectStore("foos");
console.log("got name from second txn", await reqAsPromise(store2.get(5)));
}
main().catch(err => console.error(err));
</script>
</body>
</html>

18
scripts/.eslintrc.js Normal file
View file

@ -0,0 +1,18 @@
module.exports = {
"env": {
"node": true,
"es6": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"rules": {
"no-console": "off",
"no-empty": "off",
"no-prototype-builtins": "off",
"no-unused-vars": "warn"
},
};

View file

@ -0,0 +1,51 @@
const fs = require('fs/promises');
const path = require('path');
module.exports = function injectWebManifest(manifestFile) {
let root;
let base;
let manifestHref;
return {
name: "hydrogen:injectWebManifest",
apply: "build",
configResolved: config => {
root = config.root;
base = config.base;
},
transformIndexHtml: {
transform(html) {
return [{
tag: "link",
attrs: {rel: "manifest", href: manifestHref},
injectTo: "head"
}];
},
},
generateBundle: async function() {
const absoluteManifestFile = path.resolve(root, manifestFile);
const manifestDir = path.dirname(absoluteManifestFile);
const json = await fs.readFile(absoluteManifestFile, {encoding: "utf8"});
const manifest = JSON.parse(json);
for (const icon of manifest.icons) {
const iconFileName = path.resolve(manifestDir, icon.src);
const imgData = await fs.readFile(iconFileName);
const ref = this.emitFile({
type: "asset",
name: path.basename(iconFileName),
source: imgData
});
// we take the basename as getFileName gives the filename
// relative to the output dir, but the manifest is an asset
// just like they icon, so we assume they end up in the same dir
icon.src = path.basename(this.getFileName(ref));
}
const outputName = path.basename(absoluteManifestFile);
const manifestRef = this.emitFile({
type: "asset",
name: outputName,
source: JSON.stringify(manifest)
});
manifestHref = base + this.getFileName(manifestRef);
}
};
}

View file

@ -0,0 +1,376 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const path = require('path').posix;
const {optimize} = require('svgo');
async function readCSSSource(location) {
const fs = require("fs").promises;
const resolvedLocation = path.resolve(__dirname, "../../", `${location}/theme.css`);
const data = await fs.readFile(resolvedLocation);
return data;
}
function getRootSectionWithVariables(variables) {
return `:root{\n${Object.entries(variables).reduce((acc, [key, value]) => acc + `--${key}: ${value};\n`, "")} }\n\n`;
}
function appendVariablesToCSS(variables, cssSource) {
return cssSource + getRootSectionWithVariables(variables);
}
function addThemesToConfig(bundle, manifestLocations, defaultThemes) {
for (const [fileName, info] of Object.entries(bundle)) {
if (fileName === "config.json") {
const source = new TextDecoder().decode(info.source);
const config = JSON.parse(source);
config["themeManifests"] = manifestLocations;
config["defaultTheme"] = defaultThemes;
info.source = new TextEncoder().encode(JSON.stringify(config, undefined, 2));
}
}
}
/**
* Returns an object where keys are the svg file names and the values
* are the svg code (optimized)
* @param {*} icons Object where keys are css variable names and values are locations of the svg
* @param {*} manifestLocation Location of manifest used for resolving path
*/
async function generateIconSourceMap(icons, manifestLocation) {
const sources = {};
const fileNames = [];
const promises = [];
const fs = require("fs").promises;
for (const icon of Object.values(icons)) {
const [location] = icon.split("?");
// resolve location against manifestLocation
const resolvedLocation = path.resolve(manifestLocation, location);
const iconData = fs.readFile(resolvedLocation);
promises.push(iconData);
const fileName = path.basename(resolvedLocation);
fileNames.push(fileName);
}
const results = await Promise.all(promises);
for (let i = 0; i < results.length; ++i) {
const svgString = results[i].toString();
const result = optimize(svgString, {
plugins: [
{
name: "preset-default",
params: {
overrides: { convertColors: false, },
},
},
],
});
const optimizedSvgString = result.data;
sources[fileNames[i]] = optimizedSvgString;
}
return sources;
}
/**
* Returns a mapping from location (of manifest file) to an array containing all the chunks (of css files) generated from that location.
* To understand what chunk means in this context, see https://rollupjs.org/guide/en/#generatebundle.
* @param {*} bundle Mapping from fileName to AssetInfo | ChunkInfo
*/
function getMappingFromLocationToChunkArray(bundle) {
const chunkMap = new Map();
for (const [fileName, info] of Object.entries(bundle)) {
if (!fileName.endsWith(".css") || info.type === "asset" || info.facadeModuleId?.includes("type=runtime")) {
continue;
}
const location = info.facadeModuleId?.match(/(.+)\/.+\.css/)?.[1];
if (!location) {
throw new Error("Cannot find location of css chunk!");
}
const array = chunkMap.get(location);
if (!array) {
chunkMap.set(location, [info]);
}
else {
array.push(info);
}
}
return chunkMap;
}
/**
* Returns a mapping from unhashed file name (of css files) to AssetInfo.
* To understand what AssetInfo means in this context, see https://rollupjs.org/guide/en/#generatebundle.
* @param {*} bundle Mapping from fileName to AssetInfo | ChunkInfo
*/
function getMappingFromFileNameToAssetInfo(bundle) {
const assetMap = new Map();
for (const [fileName, info] of Object.entries(bundle)) {
if (!fileName.endsWith(".css")) {
continue;
}
if (info.type === "asset") {
/**
* So this is the css assetInfo that contains the asset hashed file name.
* We'll store it in a separate map indexed via fileName (unhashed) to avoid
* searching through the bundle array later.
*/
assetMap.set(info.name, info);
}
}
return assetMap;
}
/**
* Returns a mapping from location (of manifest file) to ChunkInfo of the runtime css asset
* To understand what ChunkInfo means in this context, see https://rollupjs.org/guide/en/#generatebundle.
* @param {*} bundle Mapping from fileName to AssetInfo | ChunkInfo
*/
function getMappingFromLocationToRuntimeChunk(bundle) {
let runtimeThemeChunkMap = new Map();
for (const [fileName, info] of Object.entries(bundle)) {
if (!fileName.endsWith(".css") || info.type === "asset") {
continue;
}
const location = info.facadeModuleId?.match(/(.+)\/.+\.css/)?.[1];
if (!location) {
throw new Error("Cannot find location of css chunk!");
}
if (info.facadeModuleId?.includes("type=runtime")) {
/**
* We have a separate field in manifest.source just for the runtime theme,
* so store this separately.
*/
runtimeThemeChunkMap.set(location, info);
}
}
return runtimeThemeChunkMap;
}
module.exports = function buildThemes(options) {
let manifest, variants, defaultDark, defaultLight, defaultThemes = {};
let isDevelopment = false;
const virtualModuleId = '@theme/'
const resolvedVirtualModuleId = '\0' + virtualModuleId;
const themeToManifestLocation = new Map();
return {
name: "build-themes",
enforce: "pre",
configResolved(config) {
if (config.command === "serve") {
isDevelopment = true;
}
},
async buildStart() {
const { themeConfig } = options;
for (const location of themeConfig.themes) {
manifest = require(`${location}/manifest.json`);
const themeCollectionId = manifest.id;
themeToManifestLocation.set(themeCollectionId, location);
variants = manifest.values.variants;
for (const [variant, details] of Object.entries(variants)) {
const fileName = `theme-${themeCollectionId}-${variant}.css`;
if (themeCollectionId === themeConfig.default && details.default) {
// This is the default theme, stash the file name for later
if (details.dark) {
defaultDark = fileName;
defaultThemes["dark"] = `${themeCollectionId}-${variant}`;
}
else {
defaultLight = fileName;
defaultThemes["light"] = `${themeCollectionId}-${variant}`;
}
}
// emit the css as built theme bundle
if (!isDevelopment) {
this.emitFile({ type: "chunk", id: `${location}/theme.css?variant=${variant}${details.dark ? "&dark=true" : ""}`, fileName, });
}
}
// emit the css as runtime theme bundle
if (!isDevelopment) {
this.emitFile({ type: "chunk", id: `${location}/theme.css?type=runtime`, fileName: `theme-${themeCollectionId}-runtime.css`, });
}
}
},
resolveId(id) {
if (id.startsWith(virtualModuleId)) {
return '\0' + id;
}
},
async load(id) {
if (isDevelopment) {
/**
* To load the theme during dev, we need to take a different approach because emitFile is not supported in dev.
* We solve this by resolving virtual file "@theme/name/variant" into the necessary css import.
* This virtual file import is removed when hydrogen is built (see transform hook).
*/
if (id.startsWith(resolvedVirtualModuleId)) {
let [theme, variant, file] = id.substr(resolvedVirtualModuleId.length).split("/");
if (theme === "default") {
theme = options.themeConfig.default;
}
const location = themeToManifestLocation.get(theme);
const manifest = require(`${location}/manifest.json`);
const variants = manifest.values.variants;
if (!variant || variant === "default") {
// choose the first default variant for now
// this will need to support light/dark variants as well
variant = Object.keys(variants).find(variantName => variants[variantName].default);
}
if (!file) {
file = "index.js";
}
switch (file) {
case "index.js": {
const isDark = variants[variant].dark;
return `import "${path.resolve(`${location}/theme.css`)}${isDark? "?dark=true": ""}";` +
`import "@theme/${theme}/${variant}/variables.css"`;
}
case "variables.css": {
const variables = variants[variant].variables;
const css = getRootSectionWithVariables(variables);
return css;
}
}
}
}
else {
const result = id.match(/(.+)\/theme.css\?variant=([^&]+)/);
if (result) {
const [, location, variant] = result;
const cssSource = await readCSSSource(location);
const config = variants[variant];
return appendVariablesToCSS(config.variables, cssSource);
}
return null;
}
},
transform(code, id) {
if (isDevelopment) {
return;
}
/**
* Removes develop-only script tag; this cannot be done in transformIndexHtml hook because
* by the time that hook runs, the import is added to the bundled js file which would
* result in a runtime error.
*/
const devScriptTag =
/<script type="module"> import "@theme\/.+"; <\/script>/;
if (id.endsWith("index.html")) {
const htmlWithoutDevScript = code.replace(devScriptTag, "");
return htmlWithoutDevScript;
}
},
transformIndexHtml(_, ctx) {
if (isDevelopment) {
// Don't add default stylesheets to index.html on dev
return;
}
let darkThemeLocation, lightThemeLocation;
for (const [, bundle] of Object.entries(ctx.bundle)) {
if (bundle.name === defaultDark) {
darkThemeLocation = bundle.fileName;
}
if (bundle.name === defaultLight) {
lightThemeLocation = bundle.fileName;
}
}
return [
{
tag: "link",
attrs: {
rel: "stylesheet",
type: "text/css",
media: "(prefers-color-scheme: dark)",
href: `./${darkThemeLocation}`,
class: "theme",
}
},
{
tag: "link",
attrs: {
rel: "stylesheet",
type: "text/css",
media: "(prefers-color-scheme: light)",
href: `./${lightThemeLocation}`,
class: "theme",
}
},
];
},
async generateBundle(_, bundle) {
const assetMap = getMappingFromFileNameToAssetInfo(bundle);
const chunkMap = getMappingFromLocationToChunkArray(bundle);
const runtimeThemeChunkMap = getMappingFromLocationToRuntimeChunk(bundle);
const manifestLocations = [];
// Location of the directory containing manifest relative to the root of the build output
const manifestLocation = "assets";
for (const [location, chunkArray] of chunkMap) {
const manifest = require(`${location}/manifest.json`);
const compiledVariables = options.compiledVariables.get(location);
const derivedVariables = compiledVariables["derived-variables"];
const icon = compiledVariables["icon"];
const builtAssets = {};
let themeKey;
for (const chunk of chunkArray) {
const [, name, variant] = chunk.fileName.match(/theme-(.+)-(.+)\.css/);
themeKey = name;
const locationRelativeToBuildRoot = assetMap.get(chunk.fileName).fileName;
const locationRelativeToManifest = path.relative(manifestLocation, locationRelativeToBuildRoot);
builtAssets[`${name}-${variant}`] = locationRelativeToManifest;
}
// Emit the base svg icons as asset
const nameToAssetHashedLocation = [];
const nameToSource = await generateIconSourceMap(icon, location);
for (const [name, source] of Object.entries(nameToSource)) {
const ref = this.emitFile({ type: "asset", name, source });
const assetHashedName = this.getFileName(ref);
nameToAssetHashedLocation[name] = assetHashedName;
}
// Update icon section in output manifest with paths to the icon in build output
for (const [variable, location] of Object.entries(icon)) {
const [locationWithoutQueryParameters, queryParameters] = location.split("?");
const name = path.basename(locationWithoutQueryParameters);
const locationRelativeToBuildRoot = nameToAssetHashedLocation[name];
const locationRelativeToManifest = path.relative(manifestLocation, locationRelativeToBuildRoot);
icon[variable] = `${locationRelativeToManifest}?${queryParameters}`;
}
const runtimeThemeChunk = runtimeThemeChunkMap.get(location);
const runtimeAssetLocation = path.relative(manifestLocation, assetMap.get(runtimeThemeChunk.fileName).fileName);
manifest.source = {
"built-assets": builtAssets,
"runtime-asset": runtimeAssetLocation,
"derived-variables": derivedVariables,
"icon": icon,
};
const name = `theme-${themeKey}.json`;
manifestLocations.push(`${manifestLocation}/${name}`);
this.emitFile({
type: "asset",
name,
source: JSON.stringify(manifest),
});
}
addThemesToConfig(bundle, manifestLocations, defaultThemes);
},
}
}

View file

@ -0,0 +1,157 @@
const fs = require('fs/promises');
const path = require('path');
const xxhash = require('xxhashjs');
function contentHash(str) {
var hasher = new xxhash.h32(0);
hasher.update(str);
return hasher.digest();
}
function injectServiceWorker(swFile, findUnhashedFileNamesFromBundle, placeholdersPerChunk) {
const swName = path.basename(swFile);
let root;
let version;
let logger;
return {
name: "hydrogen:injectServiceWorker",
apply: "build",
enforce: "post",
buildStart() {
this.emitFile({
type: "chunk",
fileName: swName,
id: swFile,
});
},
configResolved: config => {
root = config.root;
version = JSON.parse(config.define.DEFINE_VERSION); // unquote
logger = config.logger;
},
generateBundle: async function(options, bundle) {
const otherUnhashedFiles = findUnhashedFileNamesFromBundle(bundle);
const unhashedFilenames = [swName].concat(otherUnhashedFiles);
const unhashedFileContentMap = unhashedFilenames.reduce((map, fileName) => {
const chunkOrAsset = bundle[fileName];
if (!chunkOrAsset) {
throw new Error("could not get content for uncached asset or chunk " + fileName);
}
map[fileName] = chunkOrAsset.source || chunkOrAsset.code;
return map;
}, {});
const assets = Object.values(bundle);
const hashedFileNames = assets.map(o => o.fileName).filter(fileName => !unhashedFileContentMap[fileName]);
const globalHash = getBuildHash(hashedFileNames, unhashedFileContentMap);
const placeholderValues = {
DEFINE_GLOBAL_HASH: `"${globalHash}"`,
...getCacheFileNamePlaceholderValues(swName, unhashedFilenames, assets, placeholdersPerChunk)
};
replacePlaceholdersInChunks(assets, placeholdersPerChunk, placeholderValues);
logger.info(`\nBuilt ${version} (${globalHash})`);
}
};
}
function getBuildHash(hashedFileNames, unhashedFileContentMap) {
const unhashedHashes = Object.entries(unhashedFileContentMap).map(([fileName, content]) => {
return `${fileName}-${contentHash(Buffer.from(content))}`;
});
const globalHashAssets = hashedFileNames.concat(unhashedHashes);
globalHashAssets.sort();
return contentHash(globalHashAssets.join(",")).toString();
}
const NON_PRECACHED_JS = [
"hydrogen-legacy",
"olm_legacy.js",
// most environments don't need the worker
"main.js"
];
function isPreCached(asset) {
const {name, fileName} = asset;
return name.endsWith(".svg") ||
name.endsWith(".png") ||
name.endsWith(".css") ||
name.endsWith(".wasm") ||
name.endsWith(".html") ||
// the index and vendor chunks don't have an extension in `name`, so check extension on `fileName`
fileName.endsWith(".js") && !NON_PRECACHED_JS.includes(path.basename(name));
}
function getCacheFileNamePlaceholderValues(swName, unhashedFilenames, assets) {
const unhashedPreCachedAssets = [];
const hashedPreCachedAssets = [];
const hashedCachedOnRequestAssets = [];
for (const asset of assets) {
const {name, fileName} = asset;
// the service worker should not be cached at all,
// it's how updates happen
if (fileName === swName) {
continue;
} else if (unhashedFilenames.includes(fileName)) {
unhashedPreCachedAssets.push(fileName);
} else if (isPreCached(asset)) {
hashedPreCachedAssets.push(fileName);
} else {
hashedCachedOnRequestAssets.push(fileName);
}
}
return {
DEFINE_UNHASHED_PRECACHED_ASSETS: JSON.stringify(unhashedPreCachedAssets),
DEFINE_HASHED_PRECACHED_ASSETS: JSON.stringify(hashedPreCachedAssets),
DEFINE_HASHED_CACHED_ON_REQUEST_ASSETS: JSON.stringify(hashedCachedOnRequestAssets)
}
}
function replacePlaceholdersInChunks(assets, placeholdersPerChunk, placeholderValues) {
for (const [name, placeholderMap] of Object.entries(placeholdersPerChunk)) {
const chunk = assets.find(a => a.type === "chunk" && a.name === name);
if (!chunk) {
throw new Error(`could not find chunk ${name} to replace placeholders`);
}
for (const [placeholderName, placeholderLiteral] of Object.entries(placeholderMap)) {
const replacedValue = placeholderValues[placeholderName];
const oldCode = chunk.code;
chunk.code = chunk.code.replaceAll(placeholderLiteral, replacedValue);
if (chunk.code === oldCode) {
throw new Error(`Could not replace ${placeholderName} in ${name}, looking for literal ${placeholderLiteral}:\n${chunk.code}`);
}
}
}
}
/** creates a value to be include in the `define` build settings,
* but can be replace at the end of the build in certain chunks.
* We need this for injecting the global build hash and the final
* filenames in the service worker and index chunk.
* These values are only known in the generateBundle step, so we
* replace them by unique strings wrapped in a prompt call so no
* transformation will touch them (minifying, ...) and we can do a
* string replacement still at the end of the build. */
function definePlaceholderValue(mode, name, devValue) {
if (mode === "production") {
// note that `prompt(...)` will never be in the final output, it's replaced by the final value
// once we know at the end of the build what it is and just used as a temporary value during the build
// as something that will not be transformed.
// I first considered Symbol but it's not inconceivable that babel would transform this.
return `prompt(${JSON.stringify(name)})`;
} else {
return JSON.stringify(devValue);
}
}
function createPlaceholderValues(mode) {
return {
DEFINE_GLOBAL_HASH: definePlaceholderValue(mode, "DEFINE_GLOBAL_HASH", null),
DEFINE_UNHASHED_PRECACHED_ASSETS: definePlaceholderValue(mode, "UNHASHED_PRECACHED_ASSETS", []),
DEFINE_HASHED_PRECACHED_ASSETS: definePlaceholderValue(mode, "HASHED_PRECACHED_ASSETS", []),
DEFINE_HASHED_CACHED_ON_REQUEST_ASSETS: definePlaceholderValue(mode, "HASHED_CACHED_ON_REQUEST_ASSETS", []),
};
}
module.exports = {injectServiceWorker, createPlaceholderValues};

View file

@ -1,541 +0,0 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import cheerio from "cheerio";
import fsRoot from "fs";
const fs = fsRoot.promises;
import path from "path";
import xxhash from 'xxhashjs';
import { rollup } from 'rollup';
import postcss from "postcss";
import postcssImport from "postcss-import";
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import commander from "commander";
// needed for legacy bundle
import babel from '@rollup/plugin-babel';
// needed to find the polyfill modules in the main-legacy.js bundle
import { nodeResolve } from '@rollup/plugin-node-resolve';
// needed because some of the polyfills are written as commonjs modules
import commonjs from '@rollup/plugin-commonjs';
// multi-entry plugin so we can add polyfill file to main
import multi from '@rollup/plugin-multi-entry';
import removeJsComments from 'rollup-plugin-cleanup';
// replace urls of asset names with content hashed version
import postcssUrl from "postcss-url";
import cssvariables from "postcss-css-variables";
import autoprefixer from "autoprefixer";
import flexbugsFixes from "postcss-flexbugs-fixes";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const projectDir = path.join(__dirname, "../");
const cssSrcDir = path.join(projectDir, "src/platform/web/ui/css/");
const parameters = new commander.Command();
parameters
.option("--modern-only", "don't make a legacy build")
.option("--override-imports <json file>", "pass in a file to override import paths, see doc/SKINNING.md")
.option("--override-css <main css file>", "pass in an alternative main css file")
parameters.parse(process.argv);
async function build({modernOnly, overrideImports, overrideCss}) {
// get version number
const version = JSON.parse(await fs.readFile(path.join(projectDir, "package.json"), "utf8")).version;
let importOverridesMap;
if (overrideImports) {
importOverridesMap = await readImportOverrides(overrideImports);
}
const devHtml = await fs.readFile(path.join(projectDir, "index.html"), "utf8");
const doc = cheerio.load(devHtml);
const themes = [];
findThemes(doc, themeName => {
themes.push(themeName);
});
// clear target dir
const targetDir = path.join(projectDir, "target/");
await removeDirIfExists(targetDir);
await createDirs(targetDir, themes);
const assets = new AssetMap(targetDir);
// copy olm assets
const olmAssets = await copyFolder(path.join(projectDir, "lib/olm/"), assets.directory);
assets.addSubMap(olmAssets);
await assets.write(`hydrogen.js`, await buildJs("src/main.js", ["src/platform/web/Platform.js"], importOverridesMap));
if (!modernOnly) {
await assets.write(`hydrogen-legacy.js`, await buildJsLegacy("src/main.js", [
'src/platform/web/legacy-polyfill.js',
'src/platform/web/LegacyPlatform.js'
], importOverridesMap));
await assets.write(`worker.js`, await buildJsLegacy("src/platform/web/worker/main.js", ['src/platform/web/worker/polyfill.js']));
}
// copy over non-theme assets
const baseConfig = JSON.parse(await fs.readFile(path.join(projectDir, "assets/config.json"), {encoding: "utf8"}));
const downloadSandbox = "download-sandbox.html";
let downloadSandboxHtml = await fs.readFile(path.join(projectDir, `assets/${downloadSandbox}`));
await assets.write(downloadSandbox, downloadSandboxHtml);
// creates the directories where the theme css bundles are placed in,
// and writes to assets, so the build bundles can translate them, so do it first
await copyThemeAssets(themes, assets);
await buildCssBundles(buildCssLegacy, themes, assets, overrideCss);
await buildManifest(assets);
// all assets have been added, create a hash from all assets name to cache unhashed files like index.html
assets.addToHashForAll("index.html", devHtml);
let swSource = await fs.readFile(path.join(projectDir, "src/platform/web/service-worker.js"), "utf8");
assets.addToHashForAll("sw.js", swSource);
const globalHash = assets.hashForAll();
await buildServiceWorker(swSource, version, globalHash, assets);
await buildHtml(doc, version, baseConfig, globalHash, modernOnly, assets);
console.log(`built hydrogen ${version} (${globalHash}) successfully with ${assets.size} files`);
}
async function findThemes(doc, callback) {
doc("link[rel~=stylesheet][title]").each((i, el) => {
const theme = doc(el);
const href = theme.attr("href");
const themesPrefix = "/themes/";
const prefixIdx = href.indexOf(themesPrefix);
if (prefixIdx !== -1) {
const themeNameStart = prefixIdx + themesPrefix.length;
const themeNameEnd = href.indexOf("/", themeNameStart);
const themeName = href.substr(themeNameStart, themeNameEnd - themeNameStart);
callback(themeName, theme);
}
});
}
async function createDirs(targetDir, themes) {
await fs.mkdir(targetDir);
const themeDir = path.join(targetDir, "themes");
await fs.mkdir(themeDir);
for (const theme of themes) {
await fs.mkdir(path.join(themeDir, theme));
}
}
async function copyThemeAssets(themes, assets) {
for (const theme of themes) {
const themeDstFolder = path.join(assets.directory, `themes/${theme}`);
const themeSrcFolder = path.join(cssSrcDir, `themes/${theme}`);
const themeAssets = await copyFolder(themeSrcFolder, themeDstFolder, file => {
return !file.endsWith(".css");
});
assets.addSubMap(themeAssets);
}
return assets;
}
async function buildHtml(doc, version, baseConfig, globalHash, modernOnly, assets) {
// transform html file
// change path to main.css to css bundle
doc("link[rel=stylesheet]:not([title])").attr("href", assets.resolve(`hydrogen.css`));
// adjust file name of icon on iOS
doc("link[rel=apple-touch-icon]").attr("href", assets.resolve(`icon-maskable.png`));
// change paths to all theme stylesheets
findThemes(doc, (themeName, theme) => {
theme.attr("href", assets.resolve(`themes/${themeName}/bundle.css`));
});
const configJSON = JSON.stringify(Object.assign({}, baseConfig, {
worker: assets.has("worker.js") ? assets.resolve(`worker.js`) : null,
downloadSandbox: assets.resolve("download-sandbox.html"),
serviceWorker: "sw.js",
olm: {
wasm: assets.resolve("olm.wasm"),
legacyBundle: assets.resolve("olm_legacy.js"),
wasmBundle: assets.resolve("olm.js"),
}
}));
const mainScripts = [
`<script type="module">import {main, Platform} from "./${assets.resolve(`hydrogen.js`)}"; main(new Platform(document.body, ${configJSON}));</script>`
];
if (!modernOnly) {
mainScripts.push(
`<script type="text/javascript" nomodule src="${assets.resolve(`hydrogen-legacy.js`)}"></script>`,
`<script type="text/javascript" nomodule>hydrogen.main(new hydrogen.Platform(document.body, ${configJSON}));</script>`
);
}
doc("script#main").replaceWith(mainScripts.join(""));
const versionScript = doc("script#version");
versionScript.attr("type", "text/javascript");
let vSource = versionScript.contents().text();
vSource = vSource.replace(`"%%VERSION%%"`, `"${version}"`);
vSource = vSource.replace(`"%%GLOBAL_HASH%%"`, `"${globalHash}"`);
versionScript.text(vSource);
doc("head").append(`<link rel="manifest" href="${assets.resolve("manifest.json")}">`);
await assets.writeUnhashed("index.html", doc.html());
}
async function buildJs(mainFile, extraFiles, importOverrides) {
// create js bundle
const plugins = [multi(), removeJsComments({comments: "none"})];
if (importOverrides) {
plugins.push(overridesAsRollupPlugin(importOverrides));
}
const bundle = await rollup({
input: extraFiles.concat(mainFile),
plugins
});
const {output} = await bundle.generate({
format: 'es',
// TODO: can remove this?
name: `hydrogen`
});
const code = output[0].code;
return code;
}
async function buildJsLegacy(mainFile, extraFiles, importOverrides) {
// compile down to whatever IE 11 needs
const babelPlugin = babel.babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "entry",
corejs: "3",
targets: "IE 11",
// we provide our own promise polyfill (es6-promise)
// with support for synchronous flushing of
// the queue for idb where needed
exclude: ["es.promise", "es.promise.all-settled", "es.promise.finally"]
}
]
]
});
const plugins = [multi(), commonjs()];
if (importOverrides) {
plugins.push(overridesAsRollupPlugin(importOverrides));
}
plugins.push(nodeResolve(), babelPlugin);
// create js bundle
const rollupConfig = {
// important the extraFiles come first,
// so polyfills are available in the global scope
// if needed for the mainfile
input: extraFiles.concat(mainFile),
plugins
};
const bundle = await rollup(rollupConfig);
const {output} = await bundle.generate({
format: 'iife',
name: `hydrogen`
});
const code = output[0].code;
return code;
}
const NON_PRECACHED_JS = [
"hydrogen-legacy.js",
"olm_legacy.js",
"worker.js"
];
function isPreCached(asset) {
return asset.endsWith(".svg") ||
asset.endsWith(".png") ||
asset.endsWith(".css") ||
asset.endsWith(".wasm") ||
asset.endsWith(".html") ||
// most environments don't need the worker
asset.endsWith(".js") && !NON_PRECACHED_JS.includes(asset);
}
async function buildManifest(assets) {
const webManifest = JSON.parse(await fs.readFile(path.join(projectDir, "assets/manifest.json"), "utf8"));
// copy manifest icons
for (const icon of webManifest.icons) {
let iconData = await fs.readFile(path.join(projectDir, icon.src));
const iconTargetPath = path.basename(icon.src);
icon.src = await assets.write(iconTargetPath, iconData);
}
await assets.write("manifest.json", JSON.stringify(webManifest));
}
async function buildServiceWorker(swSource, version, globalHash, assets) {
const unhashedPreCachedAssets = ["index.html"];
const hashedPreCachedAssets = [];
const hashedCachedOnRequestAssets = [];
for (const [unresolved, resolved] of assets) {
if (unresolved === resolved) {
unhashedPreCachedAssets.push(resolved);
} else if (isPreCached(unresolved)) {
hashedPreCachedAssets.push(resolved);
} else {
hashedCachedOnRequestAssets.push(resolved);
}
}
const replaceArrayInSource = (name, value) => {
const newSource = swSource.replace(`${name} = []`, `${name} = ${JSON.stringify(value)}`);
if (newSource === swSource) {
throw new Error(`${name} was not found in the service worker source`);
}
return newSource;
};
const replaceStringInSource = (name, value) => {
const newSource = swSource.replace(new RegExp(`${name}\\s=\\s"[^"]*"`), `${name} = ${JSON.stringify(value)}`);
if (newSource === swSource) {
throw new Error(`${name} was not found in the service worker source`);
}
return newSource;
};
// write service worker
swSource = swSource.replace(`"%%VERSION%%"`, `"${version}"`);
swSource = swSource.replace(`"%%GLOBAL_HASH%%"`, `"${globalHash}"`);
swSource = replaceArrayInSource("UNHASHED_PRECACHED_ASSETS", unhashedPreCachedAssets);
swSource = replaceArrayInSource("HASHED_PRECACHED_ASSETS", hashedPreCachedAssets);
swSource = replaceArrayInSource("HASHED_CACHED_ON_REQUEST_ASSETS", hashedCachedOnRequestAssets);
swSource = replaceStringInSource("NOTIFICATION_BADGE_ICON", assets.resolve("icon.png"));
// service worker should not have a hashed name as it is polled by the browser for updates
await assets.writeUnhashed("sw.js", swSource);
}
async function buildCssBundles(buildFn, themes, assets, mainCssFile = null) {
if (!mainCssFile) {
mainCssFile = path.join(cssSrcDir, "main.css");
}
const bundleCss = await buildFn(mainCssFile);
await assets.write(`hydrogen.css`, bundleCss);
for (const theme of themes) {
const themeRelPath = `themes/${theme}/`;
const themeRoot = path.join(cssSrcDir, themeRelPath);
const assetUrlMapper = ({absolutePath}) => {
if (!absolutePath.startsWith(themeRoot)) {
throw new Error("resource is out of theme directory: " + absolutePath);
}
const relPath = absolutePath.substr(themeRoot.length);
const hashedDstPath = assets.resolve(path.join(themeRelPath, relPath));
if (hashedDstPath) {
return hashedDstPath.substr(themeRelPath.length);
}
};
const themeCss = await buildFn(path.join(themeRoot, `theme.css`), assetUrlMapper);
await assets.write(path.join(themeRelPath, `bundle.css`), themeCss);
}
}
// async function buildCss(entryPath, urlMapper = null) {
// const preCss = await fs.readFile(entryPath, "utf8");
// const options = [postcssImport];
// if (urlMapper) {
// options.push(postcssUrl({url: urlMapper}));
// }
// const cssBundler = postcss(options);
// const result = await cssBundler.process(preCss, {from: entryPath});
// return result.css;
// }
async function buildCssLegacy(entryPath, urlMapper = null) {
const preCss = await fs.readFile(entryPath, "utf8");
const options = [
postcssImport,
cssvariables(),
autoprefixer({overrideBrowserslist: ["IE 11"], grid: "no-autoplace"}),
flexbugsFixes()
];
if (urlMapper) {
options.push(postcssUrl({url: urlMapper}));
}
const cssBundler = postcss(options);
const result = await cssBundler.process(preCss, {from: entryPath});
return result.css;
}
async function removeDirIfExists(targetDir) {
try {
await fs.rmdir(targetDir, {recursive: true});
} catch (err) {
if (err.code !== "ENOENT") {
throw err;
}
}
}
async function copyFolder(srcRoot, dstRoot, filter, assets = null) {
assets = assets || new AssetMap(dstRoot);
const dirEnts = await fs.readdir(srcRoot, {withFileTypes: true});
for (const dirEnt of dirEnts) {
const dstPath = path.join(dstRoot, dirEnt.name);
const srcPath = path.join(srcRoot, dirEnt.name);
if (dirEnt.isDirectory()) {
await fs.mkdir(dstPath);
await copyFolder(srcPath, dstPath, filter, assets);
} else if ((dirEnt.isFile() || dirEnt.isSymbolicLink()) && (!filter || filter(srcPath))) {
const content = await fs.readFile(srcPath);
await assets.write(dstPath, content);
}
}
return assets;
}
function contentHash(str) {
var hasher = new xxhash.h32(0);
hasher.update(str);
return hasher.digest();
}
class AssetMap {
constructor(targetDir) {
// remove last / if any, so substr in create works well
this._targetDir = path.resolve(targetDir);
this._assets = new Map();
// hashes for unhashed resources so changes in these resources also contribute to the hashForAll
this._unhashedHashes = [];
}
_toRelPath(resourcePath) {
let relPath = resourcePath;
if (path.isAbsolute(resourcePath)) {
if (!resourcePath.startsWith(this._targetDir)) {
throw new Error(`absolute path ${resourcePath} that is not within target dir ${this._targetDir}`);
}
relPath = resourcePath.substr(this._targetDir.length + 1); // + 1 for the /
}
return relPath;
}
_create(resourcePath, content) {
const relPath = this._toRelPath(resourcePath);
const hash = contentHash(Buffer.from(content));
const dir = path.dirname(relPath);
const extname = path.extname(relPath);
const basename = path.basename(relPath, extname);
const dstRelPath = path.join(dir, `${basename}-${hash}${extname}`);
this._assets.set(relPath, dstRelPath);
return dstRelPath;
}
async write(resourcePath, content) {
const relPath = this._create(resourcePath, content);
const fullPath = path.join(this.directory, relPath);
if (typeof content === "string") {
await fs.writeFile(fullPath, content, "utf8");
} else {
await fs.writeFile(fullPath, content);
}
return relPath;
}
async writeUnhashed(resourcePath, content) {
const relPath = this._toRelPath(resourcePath);
this._assets.set(relPath, relPath);
const fullPath = path.join(this.directory, relPath);
if (typeof content === "string") {
await fs.writeFile(fullPath, content, "utf8");
} else {
await fs.writeFile(fullPath, content);
}
return relPath;
}
get directory() {
return this._targetDir;
}
resolve(resourcePath) {
const relPath = this._toRelPath(resourcePath);
const result = this._assets.get(relPath);
if (!result) {
throw new Error(`unknown path: ${relPath}, only know ${Array.from(this._assets.keys()).join(", ")}`);
}
return result;
}
addSubMap(assetMap) {
if (!assetMap.directory.startsWith(this.directory)) {
throw new Error(`map directory doesn't start with this directory: ${assetMap.directory} ${this.directory}`);
}
const relSubRoot = assetMap.directory.substr(this.directory.length + 1);
for (const [key, value] of assetMap._assets.entries()) {
this._assets.set(path.join(relSubRoot, key), path.join(relSubRoot, value));
}
}
[Symbol.iterator]() {
return this._assets.entries();
}
isUnhashed(relPath) {
const resolvedPath = this._assets.get(relPath);
if (!resolvedPath) {
throw new Error("Unknown asset: " + relPath);
}
return relPath === resolvedPath;
}
get size() {
return this._assets.size;
}
has(relPath) {
return this._assets.has(relPath);
}
hashForAll() {
const globalHashAssets = Array.from(this).map(([, resolved]) => resolved);
globalHashAssets.push(...this._unhashedHashes);
globalHashAssets.sort();
return contentHash(globalHashAssets.join(","));
}
addToHashForAll(resourcePath, content) {
this._unhashedHashes.push(`${resourcePath}-${contentHash(Buffer.from(content))}`);
}
}
async function readImportOverrides(filename) {
const json = await fs.readFile(filename, "utf8");
const mapping = new Map(Object.entries(JSON.parse(json)));
return {
basedir: path.dirname(path.resolve(filename))+path.sep,
mapping
};
}
function overridesAsRollupPlugin(importOverrides) {
const {mapping, basedir} = importOverrides;
return {
name: "rewrite-imports",
resolveId (source, importer) {
let file;
if (source.startsWith(path.sep)) {
file = source;
} else {
file = path.join(path.dirname(importer), source);
}
if (file.startsWith(basedir)) {
const searchPath = file.substr(basedir.length);
const replacingPath = mapping.get(searchPath);
if (replacingPath) {
console.info(`replacing ${searchPath} with ${replacingPath}`);
return path.join(basedir, replacingPath);
}
}
return null;
}
};
}
build(parameters).catch(err => console.error(err));

165
scripts/ci.sh Executable file
View file

@ -0,0 +1,165 @@
#!/bin/bash
# ci.sh: Helper script to automate deployment operations on CI/CD
# Copyright © 2022 Aravinth Manivannan <realaravinth@batsense.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set -xEeuo pipefail
#source $(pwd)/scripts/lib.sh
readonly SSH_ID_FILE=/tmp/ci-ssh-id
readonly SSH_REMOTE_NAME=origin-ssh
readonly PROJECT_ROOT=$(pwd)
match_arg() {
if [ $1 == $2 ] || [ $1 == $3 ]
then
return 0
else
return 1
fi
}
help() {
cat << EOF
USAGE: ci.sh [SUBCOMMAND]
Helper script to automate deployment operations on CI/CD
Subcommands
-c --clean cleanup secrets, SSH key and other runtime data
-i --init <SSH_PRIVATE_KEY> initialize environment, write SSH private to file
-d --deploy <PAGES-SECRET> <TARGET BRANCH> push branch to Gitea and call Pages server
-h --help print this help menu
EOF
}
# $1: SSH private key
write_ssh(){
truncate --size 0 $SSH_ID_FILE
echo "$1" > $SSH_ID_FILE
chmod 600 $SSH_ID_FILE
}
set_ssh_remote() {
http_remote_url=$(git remote get-url origin)
remote_hostname=$(echo $http_remote_url | cut -d '/' -f 3)
repository_owner=$(echo $http_remote_url | cut -d '/' -f 4)
repository_name=$(echo $http_remote_url | cut -d '/' -f 5)
ssh_remote="git@$remote_hostname:$repository_owner/$repository_name"
ssh_remote="git@git.batsense.net:mystiq/hydrogen-web.git"
git remote add $SSH_REMOTE_NAME $ssh_remote
}
clean() {
if [ -f $SSH_ID_FILE ]
then
shred $SSH_ID_FILE
rm $SSH_ID_FILE
fi
}
# $1: branch name
# $2: directory containing build assets
# $3: Author in <author-name author@example.com> format
commit_files() {
cd $PROJECT_ROOT
original_branch=$(git branch --show-current)
tmp_dir=$(mktemp -d)
cp -r $2/* $tmp_dir
if [[ -z $(git ls-remote --heads origin ${1}) ]]
then
echo "[*] Creating deployment branch $1"
git checkout --orphan $1
else
echo "[*] Deployment branch $1 exists, pulling changes from remote"
git fetch origin $1
git switch $1
fi
git rm -rf .
/bin/rm -rf *
cp -r $tmp_dir/* .
git add --all
if [ $(git status --porcelain | xargs | sed '/^$/d' | wc -l) -gt 0 ];
then
echo "[*] Repository has changed, committing changes"
git commit \
--author="$3" \
--message="new deploy: $(date --iso-8601=seconds)"
fi
git checkout $original_branch
}
# $1: Pages API secret
# $2: Deployment target branch
deploy() {
if (( "$#" < 2 ))
then
help
else
git -c core.sshCommand="/usr/bin/ssh -oStrictHostKeyChecking=no -i $SSH_ID_FILE"\
push --force $SSH_REMOTE_NAME $2
curl -vv --location --request \
POST "https://deploy.batsense.net/api/v1/update"\
--header 'Content-Type: application/json' \
--data-raw "{ \"secret\": \"$1\", \"branch\": \"$2\" }"
fi
}
if (( "$#" < 1 ))
then
help
exit -1
fi
if match_arg $1 '-i' '--init'
then
if (( "$#" < 2 ))
then
help
exit -1
fi
set_ssh_remote
write_ssh "$2"
elif match_arg $1 '-c' '--clean'
then
clean
elif match_arg $1 '-cf' '--commit-files'
then
if (( "$#" < 4 ))
then
help
exit -1
fi
commit_files $2 $3 $4
elif match_arg $1 '-d' '--deploy'
then
if (( "$#" < 3 ))
then
help
exit -1
fi
deploy $2 $3
elif match_arg $1 '-h' '--help'
then
help
else
help
fi

3
scripts/cleanup.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
# Remove icons created in .tmp
rm -rf .tmp

View file

@ -1,12 +0,0 @@
import fsRoot from "fs";
const fs = fsRoot.promises;
export async function removeDirIfExists(targetDir) {
try {
await fs.rmdir(targetDir, {recursive: true});
} catch (err) {
if (err.code !== "ENOENT") {
throw err;
}
}
}

View file

@ -1,6 +0,0 @@
git checkout gh-pages
cp -R target/* .
git add $(find . -maxdepth 1 -type f)
git add themes
git commit -m "update hydrogen"
git checkout master

View file

@ -172,13 +172,36 @@
color: HighlightText;
}
.timeline .item.highlighted {
background-color: fuchsia;
color: white;
}
.hidden {
display: none;
}
#highlight {
width: 300px;
}
nav form {
display: inline;
}
</style>
</head>
<body>
<nav><button id="openFile">Open log file</button></nav>
<nav>
<button id="openFile">Open log file</button>
<button id="collapseAll">Collapse all</button>
<button id="hideCollapsed">Hide collapsed root items</button>
<button id="hideHighlightedSiblings" title="Hide collapsed siblings of highlighted">Hide non-highlighted</button>
<button id="showAll">Show all</button>
<form id="highlightForm">
<input type="text" id="highlight" name="highlight" placeholder="Highlight a search term" autocomplete="on">
<output id="highlightMatches"></output>
</form>
</nav>
<main></main>
<aside></aside>
<script type="module" src="main.js"></script>

View file

@ -82,12 +82,13 @@ function showItemDetails(item, parent, itemNode) {
const parentOffset = itemStart(parent) ? `${itemStart(item) - itemStart(parent)}ms` : "none";
const expandButton = t.button("Expand recursively");
expandButton.addEventListener("click", () => expandResursively(itemNode.parentElement.parentElement));
const start = itemStart(item);
const aside = t.aside([
t.h3(itemCaption(item)),
t.p([t.strong("Log level: "), logLevels[itemLevel(item)]]),
t.p([t.strong("Error: "), itemError(item) ? `${itemError(item).name} ${itemError(item).stack}` : "none"]),
t.p([t.strong("Parent offset: "), parentOffset]),
t.p([t.strong("Start: "), new Date(itemStart(item)).toString()]),
t.p([t.strong("Start: "), new Date(start).toString(), ` (${start})`]),
t.p([t.strong("Duration: "), `${itemDuration(item)}ms`]),
t.p([t.strong("Child count: "), itemChildren(item) ? `${itemChildren(item).length}` : "none"]),
t.p([t.strong("Forced finish: "), (itemForcedFinish(item) || false) + ""]),
@ -165,6 +166,7 @@ async function loadFile() {
main.replaceChildren(fragment);
}
// TODO: make this use processRecursively
function preprocessRecursively(item, parentElement, refsMap, path) {
item.s = (parentElement?.s || 0) + item.s;
if (itemRefSource(item)) {
@ -296,3 +298,101 @@ function itemToNode(item) {
}
return li;
}
const highlightForm = document.getElementById("highlightForm");
highlightForm.addEventListener("submit", evt => {
evt.preventDefault();
const matchesOutput = document.getElementById("highlightMatches");
const query = document.getElementById("highlight").value;
if (query) {
matchesOutput.innerText = "Searching…";
let matches = 0;
processRecursively(rootItem, item => {
let domNode = document.getElementById(item.id);
if (itemMatchesFilter(item, query)) {
matches += 1;
domNode.classList.add("highlighted");
domNode = domNode.parentElement;
while (domNode.nodeName !== "SECTION") {
if (domNode.nodeName === "LI") {
domNode.classList.add("expanded");
}
domNode = domNode.parentElement;
}
} else {
domNode.classList.remove("highlighted");
}
});
matchesOutput.innerText = `${matches} matches`;
} else {
for (const node of document.querySelectorAll(".highlighted")) {
node.classList.remove("highlighted");
}
matchesOutput.innerText = "";
}
});
function itemMatchesFilter(item, query) {
if (itemError(item)) {
if (valueMatchesQuery(itemError(item), query)) {
return true;
}
}
return valueMatchesQuery(itemValues(item), query);
}
function valueMatchesQuery(value, query) {
if (typeof value === "string") {
return value.includes(query);
} else if (typeof value === "object" && value !== null) {
for (const key in value) {
if (value.hasOwnProperty(key) && valueMatchesQuery(value[key], query)) {
return true;
}
}
} else if (typeof value === "number") {
return value.toString().includes(query);
}
return false;
}
function processRecursively(item, callback, parentItem) {
if (item.id) {
callback(item, parentItem);
}
if (itemChildren(item)) {
for (let i = 0; i < itemChildren(item).length; i += 1) {
// do it in advance for a child as we don't want to do it for the rootItem
const child = itemChildren(item)[i];
processRecursively(child, callback, item);
}
}
}
document.getElementById("collapseAll").addEventListener("click", () => {
for (const node of document.querySelectorAll(".expanded")) {
node.classList.remove("expanded");
}
});
document.getElementById("hideCollapsed").addEventListener("click", () => {
for (const node of document.querySelectorAll("section > div.timeline > ol > li:not(.expanded)")) {
node.closest("section").classList.add("hidden");
}
});
document.getElementById("hideHighlightedSiblings").addEventListener("click", () => {
for (const node of document.querySelectorAll(".highlighted")) {
const list = node.closest("ol");
const siblings = Array.from(list.querySelectorAll("li > div > a:not(.highlighted)")).map(n => n.closest("li"));
for (const sibling of siblings) {
if (!sibling.classList.contains("expanded")) {
sibling.classList.add("hidden");
}
}
}
});
document.getElementById("showAll").addEventListener("click", () => {
for (const node of document.querySelectorAll(".hidden")) {
node.classList.remove("hidden");
}
});

View file

@ -1,6 +1,7 @@
module.exports = class Buffer {
static isBuffer(array) {return array instanceof Uint8Array;}
static from(arrayBuffer) {return arrayBuffer;}
static allocUnsafe(size) {return Buffer.alloc(size);}
static alloc(size) {return new Uint8Array(size);}
var Buffer = {
isBuffer: function(array) {return array instanceof Uint8Array;},
from: function(arrayBuffer) {return arrayBuffer;},
allocUnsafe: function(size) {return Buffer.alloc(size);},
alloc: function(size) {return new Uint8Array(size);}
};
export default Buffer;

View file

@ -1 +1,2 @@
module.exports.Buffer = require("buffer");
import Buffer from "buffer";
export {Buffer};

View file

@ -2,6 +2,9 @@ VERSION=$(jq -r ".version" package.json)
PACKAGE=hydrogen-web-$VERSION.tar.gz
yarn build
pushd target
# move config file so we don't override it
# when deploying a new version
mv config.json config.sample.json
tar -czvf ../$PACKAGE ./
popd
echo $PACKAGE

View file

@ -1,116 +0,0 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const fsRoot = require("fs");
const fs = fsRoot.promises;
const path = require("path");
const { rollup } = require('rollup');
const { fileURLToPath } = require('url');
const { dirname } = require('path');
// needed to translate commonjs modules to esm
const commonjs = require('@rollup/plugin-commonjs');
const { nodeResolve } = require('@rollup/plugin-node-resolve');
const projectDir = path.join(__dirname, "../");
async function removeDirIfExists(targetDir) {
try {
await fs.rmdir(targetDir, {recursive: true});
} catch (err) {
if (err.code !== "ENOENT") {
throw err;
}
}
}
/** function used to resolve common-js require calls below. */
function packageIterator(request, start, defaultIterator) {
// this is just working for bs58, would need to tune it further for other dependencies
if (request === "safe-buffer") {
return [path.join(projectDir, "/scripts/package-overrides/safe-buffer")];
} else if (request === "buffer/") {
return [path.join(projectDir, "/scripts/package-overrides/buffer")];
} else {
return defaultIterator();
}
}
async function commonjsToESM(src, dst) {
// create js bundle
const bundle = await rollup({
input: src,
plugins: [commonjs(), nodeResolve({
browser: true,
preferBuiltins: false,
customResolveOptions: {packageIterator}
})]
});
const {output} = await bundle.generate({
format: 'es'
});
const code = output[0].code;
await fs.writeFile(dst, code, "utf8");
}
async function populateLib() {
const libDir = path.join(projectDir, "lib/");
await removeDirIfExists(libDir);
await fs.mkdir(libDir);
const olmSrcDir = path.dirname(require.resolve("olm"));
const olmDstDir = path.join(libDir, "olm/");
await fs.mkdir(olmDstDir);
for (const file of ["olm.js", "olm.wasm", "olm_legacy.js"]) {
await fs.symlink(path.join(olmSrcDir, file), path.join(olmDstDir, file));
}
// transpile another-json to esm
await fs.mkdir(path.join(libDir, "another-json/"));
await commonjsToESM(
require.resolve('another-json/another-json.js'),
path.join(libDir, "another-json/index.js")
);
// transpile bs58 to esm
await fs.mkdir(path.join(libDir, "bs58/"));
await commonjsToESM(
require.resolve('bs58/index.js'),
path.join(libDir, "bs58/index.js")
);
// transpile base64-arraybuffer to esm
await fs.mkdir(path.join(libDir, "base64-arraybuffer/"));
await commonjsToESM(
require.resolve('base64-arraybuffer/lib/base64-arraybuffer.js'),
path.join(libDir, "base64-arraybuffer/index.js")
);
// this probably should no go in here, we can just import "aes-js" from legacy-extras.js
// as that file is never loaded from a browser
// transpile aesjs to esm
await fs.mkdir(path.join(libDir, "aes-js/"));
await commonjsToESM(
require.resolve('aes-js/index.js'),
path.join(libDir, "aes-js/index.js")
);
// es6-promise is already written as an es module,
// but it does need to be babelified, and current we don't babelify
// anything in node_modules in the build script, so make a bundle that
// is conveniently not placed in node_modules rather than symlinking.
await fs.mkdir(path.join(libDir, "es6-promise/"));
await commonjsToESM(
require.resolve('es6-promise/lib/es6-promise/promise.js'),
path.join(libDir, "es6-promise/index.js")
);
}
populateLib();

View file

@ -0,0 +1,180 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const valueParser = require("postcss-value-parser");
/**
* This plugin derives new css variables from a given set of base variables.
* A derived css variable has the form --base--operation-argument; meaning that the derived
* variable has a value that is generated from the base variable "base" by applying "operation"
* with given "argument".
*
* eg: given the base variable --foo-color: #40E0D0, --foo-color--darker-20 is a css variable
* derived from foo-color by making it 20% more darker.
*
* All derived variables are added to the :root section.
*
* The actual derivation is done outside the plugin in a callback.
*/
function getValueFromAlias(alias, {aliasMap, baseVariables, resolvedMap}) {
const derivedVariable = aliasMap.get(alias);
return baseVariables.get(derivedVariable) ?? resolvedMap.get(derivedVariable);
}
function parseDeclarationValue(value) {
const parsed = valueParser(value);
const variables = [];
parsed.walk(node => {
if (node.type !== "function") {
return;
}
switch (node.value) {
case "var": {
const variable = node.nodes[0];
variables.push(variable.value);
break;
}
case "url": {
const url = node.nodes[0].value;
// resolve url with some absolute url so that we get the query params without using regex
const params = new URL(url, "file://foo/bar/").searchParams;
const primary = params.get("primary");
const secondary = params.get("secondary");
if (primary) { variables.push(primary); }
if (secondary) { variables.push(secondary); }
break;
}
}
});
return variables;
}
function resolveDerivedVariable(decl, derive, maps, isDark) {
const { baseVariables, resolvedMap } = maps;
const RE_VARIABLE_VALUE = /(?:--)?((.+)--(.+)-(.+))/;
const variableCollection = parseDeclarationValue(decl.value);
for (const variable of variableCollection) {
const matches = variable.match(RE_VARIABLE_VALUE);
if (matches) {
const [, wholeVariable, baseVariable, operation, argument] = matches;
const value = baseVariables.get(baseVariable) ?? getValueFromAlias(baseVariable, maps);
if (!value) {
throw new Error(`Cannot derive from ${baseVariable} because it is neither defined in config nor is it an alias!`);
}
const derivedValue = derive(value, operation, argument, isDark);
resolvedMap.set(wholeVariable, derivedValue);
}
}
}
function extract(decl, {aliasMap, baseVariables}) {
if (decl.variable) {
// see if right side is of form "var(--foo)"
const wholeVariable = decl.value.match(/var\(--(.+)\)/)?.[1];
// remove -- from the prop
const prop = decl.prop.substring(2);
if (wholeVariable) {
aliasMap.set(prop, wholeVariable);
// Since this is an alias, we shouldn't store it in baseVariables
return;
}
baseVariables.set(prop, decl.value);
}
}
function addResolvedVariablesToRootSelector(root, {Rule, Declaration}, {resolvedMap}) {
const newRule = new Rule({ selector: ":root", source: root.source });
// Add derived css variables to :root
resolvedMap.forEach((value, key) => {
const declaration = new Declaration({prop: `--${key}`, value});
newRule.append(declaration);
});
root.append(newRule);
}
function populateMapWithDerivedVariables(map, cssFileLocation, {resolvedMap, aliasMap}) {
const location = cssFileLocation.match(/(.+)\/.+\.css/)?.[1];
const derivedVariables = [
...([...resolvedMap.keys()].filter(v => !aliasMap.has(v))),
...([...aliasMap.entries()].map(([alias, variable]) => `${alias}=${variable}`))
];
const sharedObject = map.get(location);
const output = { "derived-variables": derivedVariables };
if (sharedObject) {
Object.assign(sharedObject, output);
}
else {
map.set(location, output);
}
}
/**
* @callback derive
* @param {string} value - The base value on which an operation is applied
* @param {string} operation - The operation to be applied (eg: darker, lighter...)
* @param {string} argument - The argument for this operation
* @param {boolean} isDark - Indicates whether this theme is dark
*/
/**
*
* @param {Object} opts - Options for the plugin
* @param {derive} opts.derive - The callback which contains the logic for resolving derived variables
* @param {Map} opts.compiledVariables - A map that stores derived variables so that manifest source sections can be produced
*/
module.exports = (opts = {}) => {
const aliasMap = new Map();
const resolvedMap = new Map();
const baseVariables = new Map();
const maps = { aliasMap, resolvedMap, baseVariables };
return {
postcssPlugin: "postcss-compile-variables",
Once(root, {Rule, Declaration, result}) {
const cssFileLocation = root.source.input.from;
if (cssFileLocation.includes("type=runtime")) {
// If this is a runtime theme, don't derive variables.
return;
}
const isDark = cssFileLocation.includes("dark=true");
/*
Go through the CSS file once to extract all aliases and base variables.
We use these when resolving derived variables later.
*/
root.walkDecls(decl => extract(decl, maps));
root.walkDecls(decl => resolveDerivedVariable(decl, opts.derive, maps, isDark));
addResolvedVariablesToRootSelector(root, {Rule, Declaration}, maps);
if (opts.compiledVariables){
populateMapWithDerivedVariables(opts.compiledVariables, cssFileLocation, maps);
}
// Also produce a mapping from alias to completely resolved color
const resolvedAliasMap = new Map();
aliasMap.forEach((value, key) => {
resolvedAliasMap.set(key, resolvedMap.get(value));
});
// Publish the base-variables, derived-variables and resolved aliases to the other postcss-plugins
const combinedMap = new Map([...baseVariables, ...resolvedMap, ...resolvedAliasMap]);
result.messages.push({
type: "resolved-variable-map",
plugin: "postcss-compile-variables",
colorMap: combinedMap,
});
},
};
};
module.exports.postcss = true;

View file

@ -0,0 +1,92 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const valueParser = require("postcss-value-parser");
const resolve = require("path").resolve;
function colorsFromURL(url, colorMap) {
const params = new URL(`file://${url}`).searchParams;
const primary = params.get("primary");
if (!primary) {
return null;
}
const secondary = params.get("secondary");
const primaryColor = colorMap.get(primary);
const secondaryColor = colorMap.get(secondary);
if (!primaryColor) {
throw new Error(`Variable ${primary} not found in resolved color variables!`);
}
if (secondary && !secondaryColor) {
throw new Error(`Variable ${secondary} not found in resolved color variables!`);
}
return [primaryColor, secondaryColor];
}
function processURL(decl, replacer, colorMap, cssPath) {
const value = decl.value;
const parsed = valueParser(value);
parsed.walk(node => {
if (node.type !== "function" || node.value !== "url") {
return;
}
const urlStringNode = node.nodes[0];
const oldURL = urlStringNode.value;
const oldURLAbsolute = resolve(cssPath, oldURL);
const colors = colorsFromURL(oldURLAbsolute, colorMap);
if (!colors) {
// If no primary color is provided via url params, then this url need not be handled.
return;
}
const newURL = replacer(oldURLAbsolute.replace(/\?.+/, ""), ...colors);
if (!newURL) {
throw new Error("Replacer failed to produce a replacement URL!");
}
urlStringNode.value = newURL;
});
decl.assign({prop: decl.prop, value: parsed.toString()})
}
/* *
* @type {import('postcss').PluginCreator}
*/
module.exports = (opts = {}) => {
return {
postcssPlugin: "postcss-url-to-variable",
Once(root, {result}) {
const cssFileLocation = root.source.input.from;
if (cssFileLocation.includes("type=runtime")) {
// If this is a runtime theme, don't process urls.
return;
}
/*
postcss-compile-variables should have sent the list of resolved colours down via results
*/
const {colorMap} = result.messages.find(m => m.type === "resolved-variable-map");
if (!colorMap) {
throw new Error("Postcss results do not contain resolved colors!");
}
/*
Go through each declaration and if it contains an URL, replace the url with the result
of running replacer(url)
*/
const cssPath = root.source?.input.file.replace(/[^/]*$/, "");
root.walkDecls(decl => processURL(decl, opts.replacer, colorMap, cssPath));
},
};
};
module.exports.postcss = true;

View file

@ -0,0 +1,97 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const valueParser = require("postcss-value-parser");
/**
* This plugin extracts content inside url() into css variables and adds the variables to the root section.
* This plugin is used in conjunction with css-url-processor plugin to colorize svg icons.
*/
const idToPrepend = "icon-url";
function findAndReplaceUrl(decl, urlVariables, counter) {
const value = decl.value;
const parsed = valueParser(value);
parsed.walk(node => {
if (node.type !== "function" || node.value !== "url") {
return;
}
const url = node.nodes[0].value;
if (!url.match(/\.svg\?primary=.+/)) {
return;
}
const count = counter.next().value;
const variableName = `${idToPrepend}-${count}`;
urlVariables.set(variableName, url);
node.value = "var";
node.nodes = [{ type: "word", value: `--${variableName}` }];
});
decl.assign({prop: decl.prop, value: parsed.toString()})
}
function addResolvedVariablesToRootSelector(root, { Rule, Declaration }, urlVariables) {
const newRule = new Rule({ selector: ":root", source: root.source });
// Add derived css variables to :root
urlVariables.forEach((value, key) => {
const declaration = new Declaration({ prop: `--${key}`, value: `url("${value}")`});
newRule.append(declaration);
});
root.append(newRule);
}
function populateMapWithIcons(map, cssFileLocation, urlVariables) {
const location = cssFileLocation.match(/(.+)\/.+\.css/)?.[1];
const sharedObject = map.get(location);
const output = {"icon": Object.fromEntries(urlVariables)};
if (sharedObject) {
Object.assign(sharedObject, output);
}
else {
map.set(location, output);
}
}
function *createCounter() {
for (let i = 0; ; ++i) {
yield i;
}
}
/* *
* @type {import('postcss').PluginCreator}
*/
module.exports = (opts = {}) => {
return {
postcssPlugin: "postcss-url-to-variable",
Once(root, { Rule, Declaration }) {
const urlVariables = new Map();
const counter = createCounter();
root.walkDecls(decl => findAndReplaceUrl(decl, urlVariables, counter));
const cssFileLocation = root.source.input.from;
if (urlVariables.size && !cssFileLocation.includes("type=runtime")) {
addResolvedVariablesToRootSelector(root, { Rule, Declaration }, urlVariables);
}
if (opts.compiledVariables){
const cssFileLocation = root.source.input.from;
populateMapWithIcons(opts.compiledVariables, cssFileLocation, urlVariables);
}
},
};
};
module.exports.postcss = true;

View file

@ -0,0 +1,51 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {readFileSync, mkdirSync, writeFileSync} from "fs";
import {resolve} from "path";
import {h32} from "xxhashjs";
import {getColoredSvgString} from "../../src/platform/web/theming/shared/svg-colorizer.mjs";
function createHash(content) {
const hasher = new h32(0);
hasher.update(content);
return hasher.digest();
}
/**
* Builds a new svg with the colors replaced and returns its location.
* @param {string} svgLocation The location of the input svg file
* @param {string} primaryColor Primary color for the new svg
* @param {string} secondaryColor Secondary color for the new svg
*/
export function buildColorizedSVG(svgLocation, primaryColor, secondaryColor) {
const svgCode = readFileSync(svgLocation, { encoding: "utf8"});
const coloredSVGCode = getColoredSvgString(svgCode, primaryColor, secondaryColor);
const fileName = svgLocation.match(/.+[/\\](.+\.svg)/)[1];
const outputName = `${fileName.substring(0, fileName.length - 4)}-${createHash(coloredSVGCode)}.svg`;
const outputPath = resolve(__dirname, "./.tmp");
try {
mkdirSync(outputPath);
}
catch (e) {
if (e.code !== "EEXIST") {
throw e;
}
}
const outputFile = `${outputPath}/${outputName}`;
writeFileSync(outputFile, coloredSVGCode);
return outputFile;
}

View file

@ -0,0 +1,30 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const postcss = require("postcss");
module.exports.createTestRunner = function (plugin) {
return async function run(input, output, opts = {}, assert) {
let result = await postcss([plugin(opts)]).process(input, { from: undefined, });
assert.strictEqual(
result.css.replaceAll(/\s/g, ""),
output.replaceAll(/\s/g, "")
);
assert.strictEqual(result.warnings().length, 0);
};
}

View file

@ -0,0 +1,156 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const offColor = require("off-color").offColor;
const postcss = require("postcss");
const plugin = require("../css-compile-variables");
const derive = require("../color").derive;
const run = require("./common").createTestRunner(plugin);
module.exports.tests = function tests() {
return {
"derived variables are resolved": async (assert) => {
const inputCSS = `
:root {
--foo-color: #ff0;
}
div {
background-color: var(--foo-color--lighter-50);
}`;
const transformedColor = offColor("#ff0").lighten(0.5);
const outputCSS =
inputCSS +
`
:root {
--foo-color--lighter-50: ${transformedColor.hex()};
}
`;
await run( inputCSS, outputCSS, {derive}, assert);
},
"derived variables work with alias": async (assert) => {
const inputCSS = `
:root {
--icon-color: #fff;
}
div {
background: var(--icon-color--darker-20);
--my-alias: var(--icon-color--darker-20);
color: var(--my-alias--lighter-15);
}`;
const colorDarker = offColor("#fff").darken(0.2).hex();
const aliasLighter = offColor(colorDarker).lighten(0.15).hex();
const outputCSS = inputCSS + `:root {
--icon-color--darker-20: ${colorDarker};
--my-alias--lighter-15: ${aliasLighter};
}
`;
await run(inputCSS, outputCSS, {derive}, assert);
},
"derived variable throws if base not present in config": async (assert) => {
const css = `:root {
color: var(--icon-color--darker-20);
}`;
assert.rejects(async () => await postcss([plugin({ variables: {} })]).process(css, { from: undefined, }));
},
"multiple derived variable in single declaration is parsed correctly": async (assert) => {
const inputCSS = `
:root {
--foo-color: #ff0;
}
div {
background-color: linear-gradient(var(--foo-color--lighter-50), var(--foo-color--darker-20));
}`;
const transformedColor1 = offColor("#ff0").lighten(0.5);
const transformedColor2 = offColor("#ff0").darken(0.2);
const outputCSS =
inputCSS +
`
:root {
--foo-color--lighter-50: ${transformedColor1.hex()};
--foo-color--darker-20: ${transformedColor2.hex()};
}
`;
await run( inputCSS, outputCSS, {derive}, assert);
},
"multiple aliased-derived variable in single declaration is parsed correctly": async (assert) => {
const inputCSS = `
:root {
--foo-color: #ff0;
}
div {
--my-alias: var(--foo-color);
background-color: linear-gradient(var(--my-alias--lighter-50), var(--my-alias--darker-20));
}`;
const transformedColor1 = offColor("#ff0").lighten(0.5);
const transformedColor2 = offColor("#ff0").darken(0.2);
const outputCSS =
inputCSS +
`
:root {
--my-alias--lighter-50: ${transformedColor1.hex()};
--my-alias--darker-20: ${transformedColor2.hex()};
}
`;
await run( inputCSS, outputCSS, {derive}, assert);
},
"compiledVariables map is populated": async (assert) => {
const compiledVariables = new Map();
const inputCSS = `
:root {
--icon-color: #fff;
}
div {
background: var(--icon-color--darker-20);
--my-alias: var(--icon-color--darker-20);
color: var(--my-alias--lighter-15);
}`;
await postcss([plugin({ derive, compiledVariables })]).process(inputCSS, { from: "/foo/bar/test.css", });
const actualArray = compiledVariables.get("/foo/bar")["derived-variables"];
const expectedArray = ["icon-color--darker-20", "my-alias=icon-color--darker-20", "my-alias--lighter-15"];
assert.deepStrictEqual(actualArray.sort(), expectedArray.sort());
},
"derived variable are supported in urls": async (assert) => {
const inputCSS = `
:root {
--foo-color: #ff0;
}
div {
background-color: var(--foo-color--lighter-50);
background: url("./foo/bar/icon.svg?primary=foo-color--darker-5");
}
a {
background: url("foo/bar/icon.svg");
}`;
const transformedColorLighter = offColor("#ff0").lighten(0.5);
const transformedColorDarker = offColor("#ff0").darken(0.05);
const outputCSS =
inputCSS +
`
:root {
--foo-color--lighter-50: ${transformedColorLighter.hex()};
--foo-color--darker-5: ${transformedColorDarker.hex()};
}
`;
await run( inputCSS, outputCSS, {derive}, assert);
}
};
};

View file

@ -0,0 +1,71 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const plugin = require("../css-url-to-variables");
const run = require("./common").createTestRunner(plugin);
const postcss = require("postcss");
module.exports.tests = function tests() {
return {
"url is replaced with variable": async (assert) => {
const inputCSS = `div {
background: no-repeat center/80% url("../img/image.svg?primary=main-color--darker-20");
}
button {
background: url("/home/foo/bar/cool.svg?primary=blue&secondary=green");
}`;
const outputCSS =
`div {
background: no-repeat center/80% var(--icon-url-0);
}
button {
background: var(--icon-url-1);
}`+
`
:root {
--icon-url-0: url("../img/image.svg?primary=main-color--darker-20");
--icon-url-1: url("/home/foo/bar/cool.svg?primary=blue&secondary=green");
}
`;
await run(inputCSS, outputCSS, { }, assert);
},
"non svg urls without query params are not replaced": async (assert) => {
const inputCSS = `div {
background: no-repeat url("./img/foo/bar/image.png");
}`;
await run(inputCSS, inputCSS, {}, assert);
},
"map is populated with icons": async (assert) => {
const compiledVariables = new Map();
compiledVariables.set("/foo/bar", { "derived-variables": ["background-color--darker-20", "accent-color--lighter-15"] });
const inputCSS = `div {
background: no-repeat center/80% url("../img/image.svg?primary=main-color--darker-20");
}
button {
background: url("/home/foo/bar/cool.svg?primary=blue&secondary=green");
}`;
const expectedObject = {
"icon-url-0": "../img/image.svg?primary=main-color--darker-20",
"icon-url-1": "/home/foo/bar/cool.svg?primary=blue&secondary=green",
};
await postcss([plugin({compiledVariables})]).process(inputCSS, { from: "/foo/bar/test.css", });
const sharedVariable = compiledVariables.get("/foo/bar");
assert.deepEqual(["background-color--darker-20", "accent-color--lighter-15"], sharedVariable["derived-variables"]);
assert.deepEqual(expectedObject, sharedVariable["icon"]);
}
};
};

View file

@ -1,3 +1,4 @@
set -e
if [ -z "$1" ]; then
echo "provide a new version, current version is $(jq '.version' package.json)"
exit 1

View file

@ -0,0 +1,19 @@
{
"name": "hydrogen-view-sdk",
"description": "Embeddable matrix client library, including view components",
"version": "0.1.0",
"main": "./lib-build/hydrogen.cjs.js",
"exports": {
".": {
"import": "./lib-build/hydrogen.es.js",
"require": "./lib-build/hydrogen.cjs.js"
},
"./paths/vite": "./paths/vite.js",
"./style.css": "./asset-build/assets/theme-element-light.css",
"./theme-element-light.css": "./asset-build/assets/theme-element-light.css",
"./theme-element-dark.css": "./asset-build/assets/theme-element-dark.css",
"./main.js": "./asset-build/assets/main.js",
"./download-sandbox.html": "./asset-build/assets/download-sandbox.html",
"./assets/*": "./asset-build/assets/*"
}
}

25
scripts/sdk/build.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/bash
# Exit whenever one of the commands fail with a non-zero exit code
set -e
set -o pipefail
# Enable extended globs so we can use the `!(filename)` glob syntax
shopt -s extglob
# Only remove the directory contents instead of the whole directory to maintain
# the `npm link`/`yarn link` symlink
rm -rf target/*
yarn run vite build -c vite.sdk-assets-config.js
yarn run vite build -c vite.sdk-lib-config.js
yarn tsc -p tsconfig-declaration.json
./scripts/sdk/create-manifest.js ./target/package.json
mkdir target/paths
# this doesn't work, the ?url imports need to be in the consuming project, so disable for now
# ./scripts/sdk/transform-paths.js ./src/platform/web/sdk/paths/vite.js ./target/paths/vite.js
cp doc/SDK.md target/README.md
pushd target/asset-build
rm index.html
popd
pushd target/asset-build/assets
# Remove all `*.wasm` and `*.js` files except for `main.js`
rm !(main).js *.wasm
popd

23
scripts/sdk/create-manifest.js Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env node
const fs = require("fs");
const appManifest = require("../../package.json");
const baseSDKManifest = require("./base-manifest.json");
/*
Need to leave typescript type definitions out until the
typescript conversion is complete and all imports in the d.ts files
exists.
```
"types": "types/lib.d.ts"
```
*/
const mergeOptions = require('merge-options');
const manifestExtension = {
devDependencies: undefined,
scripts: undefined,
};
const manifest = mergeOptions(appManifest, baseSDKManifest, manifestExtension);
const json = JSON.stringify(manifest, undefined, 2);
const outFile = process.argv[2];
fs.writeFileSync(outFile, json, {encoding: "utf8"});

3
scripts/sdk/test/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules
dist
yarn.lock

2
scripts/sdk/test/deps.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
// Keep TypeScripts from complaining about hydrogen-view-sdk not having types yet
declare module "hydrogen-view-sdk";

View file

@ -0,0 +1,21 @@
import * as hydrogenViewSdk from "hydrogen-view-sdk";
import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
import workerPath from 'hydrogen-view-sdk/main.js?url';
import olmWasmPath from '@matrix-org/olm/olm.wasm?url';
import olmJsPath from '@matrix-org/olm/olm.js?url';
import olmLegacyJsPath from '@matrix-org/olm/olm_legacy.js?url';
const assetPaths = {
downloadSandbox: downloadSandboxPath,
worker: workerPath,
olm: {
wasm: olmWasmPath,
legacyBundle: olmLegacyJsPath,
wasmBundle: olmJsPath
}
};
import "hydrogen-view-sdk/assets/theme-element-light.css";
console.log('hydrogenViewSdk', hydrogenViewSdk);
console.log('assetPaths', assetPaths);
console.log('Entry ESM works ✅');

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app" class="hydrogen"></div>
<script type="module" src="./esm-entry.ts"></script>
</body>
</html>

View file

@ -0,0 +1,8 @@
{
"name": "test-sdk",
"version": "0.0.0",
"description": "",
"dependencies": {
"hydrogen-view-sdk": "link:../../../target"
}
}

View file

@ -0,0 +1,13 @@
// Make sure the SDK can be used in a CommonJS environment.
// Usage: node scripts/sdk/test/test-sdk-in-commonjs-env.js
const hydrogenViewSdk = require('hydrogen-view-sdk');
// Test that the "exports" are available:
// Worker
require.resolve('hydrogen-view-sdk/main.js');
// Styles
require.resolve('hydrogen-view-sdk/assets/theme-element-light.css');
// Can access files in the assets/* directory
require.resolve('hydrogen-view-sdk/assets/main.js');
console.log('SDK works in CommonJS ✅');

View file

@ -0,0 +1,19 @@
const { resolve } = require('path');
const { build } = require('vite');
async function main() {
await build({
outDir: './dist',
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html')
}
}
}
});
console.log('SDK works in Vite build ✅');
}
main();

36
scripts/sdk/transform-paths.js Executable file
View file

@ -0,0 +1,36 @@
#!/usr/bin/env node
/**
This script transforms the string literals in the sdk path files to adjust paths
from what they are at development time to what they will be in the sdk package.
It does this by looking in all string literals in the paths file and looking for file names
that we expect and need replacing (as they are bundled with the sdk).
Usage: ./transform-paths.js <input file> <output file>
*/
const acorn = require("acorn");
const walk = require("acorn-walk")
const escodegen = require("escodegen");
const fs = require("fs");
const code = fs.readFileSync(process.argv[2], {encoding: "utf8"});
const ast = acorn.parse(code, {ecmaVersion: "13", sourceType: "module"});
function changePrefix(value, file, newPrefix = "") {
const idx = value.indexOf(file);
if (idx !== -1) {
return newPrefix + value.substr(idx);
}
return value;
}
walk.simple(ast, {
Literal(node) {
node.value = changePrefix(node.value, "download-sandbox.html", "../");
node.value = changePrefix(node.value, "main.js", "../");
}
});
const transformedCode = escodegen.generate(ast);
fs.writeFileSync(process.argv[3], transformedCode, {encoding: "utf8"})

View file

@ -1,43 +0,0 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const finalhandler = require('finalhandler')
const http = require('http')
const serveStatic = require('serve-static')
const path = require('path');
// Serve up parent directory with cache disabled
const serve = serveStatic(
path.resolve(__dirname, "../"),
{
etag: false,
setHeaders: res => {
res.setHeader("Pragma", "no-cache");
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
res.setHeader("Expires", "Wed, 21 Oct 2015 07:28:00 GMT");
},
index: ['index.html', 'index.htm']
}
);
// Create server
const server = http.createServer(function onRequest (req, res) {
console.log(req.method, req.url);
serve(req, res, finalhandler(req, res))
});
// Listen
server.listen(3000);

View file

@ -0,0 +1,5 @@
#!/bin/sh
cp scripts/test-derived-theme/theme.json target/assets/theme-customer.json
cat target/config.json | jq '.themeManifests += ["assets/theme-customer.json"]' | cat > target/config.temp.json
rm target/config.json
mv target/config.temp.json target/config.json

View file

@ -0,0 +1,51 @@
{
"name": "Customer",
"extends": "element",
"id": "customer",
"values": {
"variants": {
"dark": {
"dark": true,
"default": true,
"name": "Dark",
"variables": {
"background-color-primary": "#21262b",
"background-color-secondary": "#2D3239",
"text-color": "#fff",
"accent-color": "#F03F5B",
"error-color": "#FF4B55",
"fixed-white": "#fff",
"room-badge": "#61708b",
"link-color": "#238cf5"
}
},
"light": {
"default": true,
"name": "Dark",
"variables": {
"background-color-primary": "#21262b",
"background-color-secondary": "#2D3239",
"text-color": "#fff",
"accent-color": "#F03F5B",
"error-color": "#FF4B55",
"fixed-white": "#fff",
"room-badge": "#61708b",
"link-color": "#238cf5"
}
},
"red": {
"name": "Gruvbox",
"variables": {
"background-color-primary": "#282828",
"background-color-secondary": "#3c3836",
"text-color": "#fbf1c7",
"accent-color": "#8ec07c",
"error-color": "#fb4934",
"fixed-white": "#fff",
"room-badge": "#cc241d",
"link-color": "#fe8019"
}
}
}
}
}

View file

@ -0,0 +1,136 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "./ViewModel";
import {KeyType} from "../matrix/ssss/index";
import {Status} from "./session/settings/KeyBackupViewModel.js";
export class AccountSetupViewModel extends ViewModel {
constructor(options) {
super(options);
this._accountSetup = options.accountSetup;
this._dehydratedDevice = undefined;
this._decryptDehydratedDeviceViewModel = undefined;
if (this._accountSetup.encryptedDehydratedDevice) {
this._decryptDehydratedDeviceViewModel = new DecryptDehydratedDeviceViewModel(this, dehydratedDevice => {
this._dehydratedDevice = dehydratedDevice;
this._decryptDehydratedDeviceViewModel = undefined;
this.emitChange("deviceDecrypted");
});
}
}
get decryptDehydratedDeviceViewModel() {
return this._decryptDehydratedDeviceViewModel;
}
get deviceDecrypted() {
return !!this._dehydratedDevice;
}
get dehydratedDeviceId() {
return this._accountSetup.encryptedDehydratedDevice.deviceId;
}
finish() {
this._accountSetup.finish(this._dehydratedDevice);
}
}
// this vm adopts the same shape as KeyBackupViewModel so the same view can be reused.
class DecryptDehydratedDeviceViewModel extends ViewModel {
constructor(accountSetupViewModel, decryptedCallback) {
super(accountSetupViewModel.options);
this._accountSetupViewModel = accountSetupViewModel;
this._isBusy = false;
this._status = Status.SetupKey;
this._error = undefined;
this._decryptedCallback = decryptedCallback;
}
get decryptAction() {
return this.i18n`Restore`;
}
get purpose() {
return this.i18n`claim your dehydrated device`;
}
get offerDehydratedDeviceSetup() {
return false;
}
get dehydratedDeviceId() {
return this._accountSetupViewModel._dehydratedDevice?.deviceId;
}
get isBusy() {
return this._isBusy;
}
get backupVersion() { return 0; }
get status() {
return this._status;
}
get error() {
return this._error?.message;
}
showPhraseSetup() {
if (this._status === Status.SetupKey) {
this._status = Status.SetupPhrase;
this.emitChange("status");
}
}
showKeySetup() {
if (this._status === Status.SetupPhrase) {
this._status = Status.SetupKey;
this.emitChange("status");
}
}
async _enterCredentials(keyType, credential) {
if (credential) {
try {
this._isBusy = true;
this.emitChange("isBusy");
const {encryptedDehydratedDevice} = this._accountSetupViewModel._accountSetup;
const dehydratedDevice = await encryptedDehydratedDevice.decrypt(keyType, credential);
this._decryptedCallback(dehydratedDevice);
} catch (err) {
console.error(err);
this._error = err;
this.emitChange("error");
} finally {
this._isBusy = false;
this.emitChange("");
}
}
}
enterSecurityPhrase(passphrase) {
this._enterCredentials(KeyType.Passphrase, passphrase);
}
enterSecurityKey(securityKey) {
this._enterCredentials(KeyType.RecoveryKey, securityKey);
}
disable() {}
}

View file

@ -1,84 +0,0 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "./ViewModel.js";
import {SessionLoadViewModel} from "./SessionLoadViewModel.js";
export class LoginViewModel extends ViewModel {
constructor(options) {
super(options);
const {ready, defaultHomeServer, createSessionContainer} = options;
this._createSessionContainer = createSessionContainer;
this._ready = ready;
this._defaultHomeServer = defaultHomeServer;
this._sessionContainer = null;
this._loadViewModel = null;
this._loadViewModelSubscription = null;
}
get defaultHomeServer() { return this._defaultHomeServer; }
get loadViewModel() {return this._loadViewModel; }
get isBusy() {
if (!this._loadViewModel) {
return false;
} else {
return this._loadViewModel.loading;
}
}
async login(username, password, homeserver) {
this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription);
if (this._loadViewModel) {
this._loadViewModel = this.disposeTracked(this._loadViewModel);
}
this._loadViewModel = this.track(new SessionLoadViewModel({
createAndStartSessionContainer: () => {
this._sessionContainer = this._createSessionContainer();
this._sessionContainer.startWithLogin(homeserver, username, password);
return this._sessionContainer;
},
ready: sessionContainer => {
// make sure we don't delete the session in dispose when navigating away
this._sessionContainer = null;
this._ready(sessionContainer);
},
homeserver,
}));
this._loadViewModel.start();
this.emitChange("loadViewModel");
this._loadViewModelSubscription = this.track(this._loadViewModel.disposableOn("change", () => {
if (!this._loadViewModel.loading) {
this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription);
}
this.emitChange("isBusy");
}));
}
get cancelUrl() {
return this.urlCreator.urlForSegment("session");
}
dispose() {
super.dispose();
if (this._sessionContainer) {
// if we move away before we're done with initial sync
// delete the session
this._sessionContainer.deleteSession();
}
}
}

View file

@ -0,0 +1,71 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {Options as BaseOptions, ViewModel} from "./ViewModel";
import {Client} from "../matrix/Client.js";
import {SegmentType} from "./navigation/index";
type Options = { sessionId: string; } & BaseOptions;
export class LogoutViewModel extends ViewModel<SegmentType, Options> {
private _sessionId: string;
private _busy: boolean;
private _showConfirm: boolean;
private _error?: Error;
constructor(options: Options) {
super(options);
this._sessionId = options.sessionId;
this._busy = false;
this._showConfirm = true;
this._error = undefined;
}
get showConfirm(): boolean {
return this._showConfirm;
}
get busy(): boolean {
return this._busy;
}
get cancelUrl(): string | undefined {
return this.urlCreator.urlForSegment("session", true);
}
async logout(): Promise<void> {
this._busy = true;
this._showConfirm = false;
this.emitChange("busy");
try {
const client = new Client(this.platform);
await client.startLogout(this._sessionId);
this.navigation.push("session", true);
} catch (err) {
this._error = err;
this._busy = false;
this.emitChange("busy");
}
}
get status(): string {
if (this._error) {
return this.i18n`Could not log out of device: ${this._error.message}`;
} else {
return this.i18n`Logging out… Please don't close the app.`;
}
}
}

View file

@ -14,62 +14,75 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {Client} from "../matrix/Client.js";
import {SessionViewModel} from "./session/SessionViewModel.js";
import {SessionLoadViewModel} from "./SessionLoadViewModel.js";
import {LoginViewModel} from "./LoginViewModel.js";
import {LoginViewModel} from "./login/LoginViewModel";
import {LogoutViewModel} from "./LogoutViewModel";
import {SessionPickerViewModel} from "./SessionPickerViewModel.js";
import {ViewModel} from "./ViewModel.js";
import {ViewModel} from "./ViewModel";
export class RootViewModel extends ViewModel {
constructor(options) {
super(options);
this._createSessionContainer = options.createSessionContainer;
this._error = null;
this._sessionPickerViewModel = null;
this._sessionLoadViewModel = null;
this._loginViewModel = null;
this._logoutViewModel = null;
this._sessionViewModel = null;
this._pendingSessionContainer = null;
this._pendingClient = null;
}
async load() {
this.track(this.navigation.observe("login").subscribe(() => this._applyNavigation()));
this.track(this.navigation.observe("session").subscribe(() => this._applyNavigation()));
this._applyNavigation(this.urlCreator.getLastUrl());
this.track(this.navigation.observe("sso").subscribe(() => this._applyNavigation()));
this._applyNavigation(true);
}
async _applyNavigation(restoreUrlIfAtDefault) {
const isLogin = this.navigation.observe("login").get();
const sessionId = this.navigation.observe("session").get();
async _applyNavigation(shouldRestoreLastUrl) {
const isLogin = this.navigation.path.get("login");
const logoutSessionId = this.navigation.path.get("logout")?.value;
const sessionId = this.navigation.path.get("session")?.value;
const loginToken = this.navigation.path.get("sso")?.value;
if (isLogin) {
if (this.activeSection !== "login") {
this._showLogin();
}
} else if (logoutSessionId) {
if (this.activeSection !== "logout") {
this._showLogout(logoutSessionId);
}
} else if (sessionId === true) {
if (this.activeSection !== "picker") {
this._showPicker();
}
} else if (sessionId) {
if (!this._sessionViewModel || this._sessionViewModel.id !== sessionId) {
// see _showLogin for where _pendingSessionContainer comes from
if (this._pendingSessionContainer && this._pendingSessionContainer.sessionId === sessionId) {
const sessionContainer = this._pendingSessionContainer;
this._pendingSessionContainer = null;
this._showSession(sessionContainer);
// see _showLogin for where _pendingClient comes from
if (this._pendingClient && this._pendingClient.sessionId === sessionId) {
const client = this._pendingClient;
this._pendingClient = null;
this._showSession(client);
} else {
// this should never happen, but we want to be sure not to leak it
if (this._pendingSessionContainer) {
this._pendingSessionContainer.dispose();
this._pendingSessionContainer = null;
if (this._pendingClient) {
this._pendingClient.dispose();
this._pendingClient = null;
}
this._showSessionLoader(sessionId);
}
}
} else {
} else if (loginToken) {
this.urlCreator.normalizeUrl();
if (this.activeSection !== "login") {
this._showLogin(loginToken);
}
}
else {
try {
if (restoreUrlIfAtDefault) {
this.urlCreator.pushUrl(restoreUrlIfAtDefault);
} else {
if (!(shouldRestoreLastUrl && this.urlCreator.tryRestoreLastUrl())) {
const sessionInfos = await this.platform.sessionInfoStorage.getAll();
if (sessionInfos.length === 0) {
this.navigation.push("login");
@ -96,12 +109,11 @@ export class RootViewModel extends ViewModel {
}
}
_showLogin() {
_showLogin(loginToken) {
this._setSection(() => {
this._loginViewModel = new LoginViewModel(this.childOptions({
defaultHomeServer: this.platform.config["defaultHomeServer"],
createSessionContainer: this._createSessionContainer,
ready: sessionContainer => {
defaultHomeserver: this.platform.config["defaultHomeServer"],
ready: client => {
// we don't want to load the session container again,
// but we also want the change of screen to go through the navigation
// so we store the session container in a temporary variable that will be
@ -110,30 +122,35 @@ export class RootViewModel extends ViewModel {
// Also, we should not call _setSection before the navigation is in the correct state,
// as url creation (e.g. in RoomTileViewModel)
// won't be using the correct navigation base path.
this._pendingSessionContainer = sessionContainer;
this.navigation.push("session", sessionContainer.sessionId);
this._pendingClient = client;
this.navigation.push("session", client.sessionId);
},
loginToken
}));
});
}
_showSession(sessionContainer) {
_showLogout(sessionId) {
this._setSection(() => {
this._sessionViewModel = new SessionViewModel(this.childOptions({sessionContainer}));
this._logoutViewModel = new LogoutViewModel(this.childOptions({sessionId}));
});
}
_showSession(client) {
this._setSection(() => {
this._sessionViewModel = new SessionViewModel(this.childOptions({client}));
this._sessionViewModel.start();
});
}
_showSessionLoader(sessionId) {
const client = new Client(this.platform);
client.startWithExistingSession(sessionId);
this._setSection(() => {
this._sessionLoadViewModel = new SessionLoadViewModel({
createAndStartSessionContainer: () => {
const sessionContainer = this._createSessionContainer();
sessionContainer.startWithExistingSession(sessionId);
return sessionContainer;
},
ready: sessionContainer => this._showSession(sessionContainer)
});
this._sessionLoadViewModel = new SessionLoadViewModel(this.childOptions({
client,
ready: client => this._showSession(client)
}));
this._sessionLoadViewModel.start();
});
}
@ -145,6 +162,8 @@ export class RootViewModel extends ViewModel {
return "session";
} else if (this._loginViewModel) {
return "login";
} else if (this._logoutViewModel) {
return "logout";
} else if (this._sessionPickerViewModel) {
return "picker";
} else if (this._sessionLoadViewModel) {
@ -160,12 +179,14 @@ export class RootViewModel extends ViewModel {
this._sessionPickerViewModel = this.disposeTracked(this._sessionPickerViewModel);
this._sessionLoadViewModel = this.disposeTracked(this._sessionLoadViewModel);
this._loginViewModel = this.disposeTracked(this._loginViewModel);
this._logoutViewModel = this.disposeTracked(this._logoutViewModel);
this._sessionViewModel = this.disposeTracked(this._sessionViewModel);
// now set it again
setter();
this._sessionPickerViewModel && this.track(this._sessionPickerViewModel);
this._sessionLoadViewModel && this.track(this._sessionLoadViewModel);
this._loginViewModel && this.track(this._loginViewModel);
this._logoutViewModel && this.track(this._logoutViewModel);
this._sessionViewModel && this.track(this._sessionViewModel);
this.emitChange("activeSection");
}
@ -173,6 +194,7 @@ export class RootViewModel extends ViewModel {
get error() { return this._error; }
get sessionViewModel() { return this._sessionViewModel; }
get loginViewModel() { return this._loginViewModel; }
get logoutViewModel() { return this._logoutViewModel; }
get sessionPickerViewModel() { return this._sessionPickerViewModel; }
get sessionLoadViewModel() { return this._sessionLoadViewModel; }
}

View file

@ -14,20 +14,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {LoadStatus, LoginFailure} from "../matrix/SessionContainer.js";
import {AccountSetupViewModel} from "./AccountSetupViewModel.js";
import {LoadStatus} from "../matrix/Client.js";
import {SyncStatus} from "../matrix/Sync.js";
import {ViewModel} from "./ViewModel.js";
import {ViewModel} from "./ViewModel";
export class SessionLoadViewModel extends ViewModel {
constructor(options) {
super(options);
const {createAndStartSessionContainer, ready, homeserver, deleteSessionOnCancel} = options;
this._createAndStartSessionContainer = createAndStartSessionContainer;
const {client, ready, homeserver, deleteSessionOnCancel} = options;
this._client = client;
this._ready = ready;
this._homeserver = homeserver;
this._deleteSessionOnCancel = deleteSessionOnCancel;
this._loading = false;
this._error = null;
this.backUrl = this.urlCreator.urlForSegment("session", true);
this._accountSetupViewModel = undefined;
}
async start() {
@ -37,12 +41,16 @@ export class SessionLoadViewModel extends ViewModel {
try {
this._loading = true;
this.emitChange("loading");
this._sessionContainer = this._createAndStartSessionContainer();
this._waitHandle = this._sessionContainer.loadStatus.waitFor(s => {
this._waitHandle = this._client.loadStatus.waitFor(s => {
if (s === LoadStatus.AccountSetup) {
this._accountSetupViewModel = new AccountSetupViewModel(this.childOptions({accountSetup: this._client.accountSetup}));
} else {
this._accountSetupViewModel = undefined;
}
this.emitChange("loadLabel");
// wait for initial sync, but not catchup sync
const isCatchupSync = s === LoadStatus.FirstSync &&
this._sessionContainer.sync.status.get() === SyncStatus.CatchupSync;
this._client.sync.status.get() === SyncStatus.CatchupSync;
return isCatchupSync ||
s === LoadStatus.LoginFailed ||
s === LoadStatus.Error ||
@ -59,15 +67,15 @@ export class SessionLoadViewModel extends ViewModel {
// much like we will once you are in the app. Probably a good idea
// did it finish or get stuck at LoginFailed or Error?
const loadStatus = this._sessionContainer.loadStatus.get();
const loadError = this._sessionContainer.loadError;
const loadStatus = this._client.loadStatus.get();
const loadError = this._client.loadError;
if (loadStatus === LoadStatus.FirstSync || loadStatus === LoadStatus.Ready) {
const sessionContainer = this._sessionContainer;
const client = this._client;
// session container is ready,
// don't dispose it anymore when
// we get disposed
this._sessionContainer = null;
this._ready(sessionContainer);
this._client = null;
this._ready(client);
}
if (loadError) {
console.error("session load error", loadError);
@ -77,16 +85,16 @@ export class SessionLoadViewModel extends ViewModel {
console.error("error thrown during session load", err.stack);
} finally {
this._loading = false;
// loadLabel in case of sc.loadError also gets updated through this
// loadLabel in case of client.loadError also gets updated through this
this.emitChange("loading");
}
}
dispose() {
if (this._sessionContainer) {
this._sessionContainer.dispose();
this._sessionContainer = null;
if (this._client) {
this._client.dispose();
this._client = null;
}
if (this._waitHandle) {
// rejects with AbortError
@ -97,33 +105,27 @@ export class SessionLoadViewModel extends ViewModel {
// to show a spinner or not
get loading() {
const client = this._client;
if (client && client.loadStatus.get() === LoadStatus.AccountSetup) {
return false;
}
return this._loading;
}
get loadLabel() {
const sc = this._sessionContainer;
const error = this._error || (sc && sc.loadError);
if (error || (sc && sc.loadStatus.get() === LoadStatus.Error)) {
const client = this._client;
const error = this._getError();
if (error || (client && client.loadStatus.get() === LoadStatus.Error)) {
return `Something went wrong: ${error && error.message}.`;
}
if (sc) {
switch (sc.loadStatus.get()) {
case LoadStatus.NotLoading:
return `Preparing…`;
case LoadStatus.Login:
return `Checking your login and password…`;
case LoadStatus.LoginFailed:
switch (sc.loginFailure) {
case LoginFailure.LoginFailure:
return `Your username and/or password don't seem to be correct.`;
case LoginFailure.Connection:
return `Can't connect to ${this._homeserver}.`;
case LoginFailure.Unknown:
return `Something went wrong while checking your login and password.`;
}
break;
// Statuses related to login are handled by respective login view models
if (client) {
switch (client.loadStatus.get()) {
case LoadStatus.QueryAccount:
return `Querying account encryption setup…`;
case LoadStatus.AccountSetup:
return ""; // we'll show a header ing AccountSetupView
case LoadStatus.SessionSetup:
return `Setting up your encryption keys…`;
case LoadStatus.Loading:
@ -131,10 +133,32 @@ export class SessionLoadViewModel extends ViewModel {
case LoadStatus.FirstSync:
return `Getting your conversations from the server…`;
default:
return this._sessionContainer.loadStatus.get();
return this._client.loadStatus.get();
}
}
return `Preparing…`;
}
_getError() {
return this._error || this._client?.loadError;
}
get hasError() {
return !!this._getError();
}
async exportLogs() {
const logExport = await this.logger.export();
this.platform.saveFileAs(logExport.asBlob(), `hydrogen-logs-${this.platform.clock.now()}.json`);
}
async logout() {
await this._client.logout();
this.navigation.push("session", true);
}
get accountSetupViewModel() {
return this._accountSetupViewModel;
}
}

View file

@ -15,8 +15,8 @@ limitations under the License.
*/
import {SortedArray} from "../observable/index.js";
import {ViewModel} from "./ViewModel.js";
import {avatarInitials, getIdentifierColorNumber} from "./avatar.js";
import {ViewModel} from "./ViewModel";
import {avatarInitials, getIdentifierColorNumber} from "./avatar";
class SessionItemViewModel extends ViewModel {
constructor(options, pickerVM) {
@ -33,44 +33,6 @@ class SessionItemViewModel extends ViewModel {
return this._error && this._error.message;
}
async delete() {
this._isDeleting = true;
this.emitChange("isDeleting");
try {
await this._pickerVM.delete(this.id);
} catch(err) {
this._error = err;
console.error(err);
this.emitChange("error");
} finally {
this._isDeleting = false;
this.emitChange("isDeleting");
}
}
async clear() {
this._isClearing = true;
this.emitChange();
try {
await this._pickerVM.clear(this.id);
} catch(err) {
this._error = err;
console.error(err);
this.emitChange("error");
} finally {
this._isClearing = false;
this.emitChange("isClearing");
}
}
get isDeleting() {
return this._isDeleting;
}
get isClearing() {
return this._isClearing;
}
get id() {
return this._sessionInfo.id;
}
@ -96,27 +58,6 @@ class SessionItemViewModel extends ViewModel {
return this._exportDataUrl;
}
async export() {
try {
const data = await this._pickerVM._exportData(this._sessionInfo.id);
const json = JSON.stringify(data, undefined, 2);
const blob = new Blob([json], {type: "application/json"});
this._exportDataUrl = URL.createObjectURL(blob);
this.emitChange("exportDataUrl");
} catch (err) {
alert(err.message);
console.error(err);
}
}
clearExport() {
if (this._exportDataUrl) {
URL.revokeObjectURL(this._exportDataUrl);
this._exportDataUrl = null;
this.emitChange("exportDataUrl");
}
}
get avatarColorNumber() {
return getIdentifierColorNumber(this._sessionInfo.userId);
}
@ -148,39 +89,6 @@ export class SessionPickerViewModel extends ViewModel {
return this._loadViewModel;
}
async _exportData(id) {
const sessionInfo = await this.platform.sessionInfoStorage.get(id);
const stores = await this.platform.storageFactory.export(id);
const data = {sessionInfo, stores};
return data;
}
async import(json) {
try {
const data = JSON.parse(json);
const {sessionInfo} = data;
sessionInfo.comment = `Imported on ${new Date().toLocaleString()} from id ${sessionInfo.id}.`;
sessionInfo.id = this._createSessionContainer().createNewSessionId();
await this.platform.storageFactory.import(sessionInfo.id, data.stores);
await this.platform.sessionInfoStorage.add(sessionInfo);
this._sessions.set(new SessionItemViewModel(sessionInfo, this));
} catch (err) {
alert(err.message);
console.error(err);
}
}
async delete(id) {
const idx = this._sessions.array.findIndex(s => s.id === id);
await this.platform.sessionInfoStorage.delete(id);
await this.platform.storageFactory.delete(id);
this._sessions.remove(idx);
}
async clear(id) {
await this.platform.storageFactory.delete(id);
}
get sessions() {
return this._sessions;
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -18,57 +19,85 @@ limitations under the License.
// as in some cases it would really be more convenient to have multiple events (like telling the timeline to scroll down)
// we do need to return a disposable from EventEmitter.on, or at least have a method here to easily track a subscription to an EventEmitter
import {EventEmitter} from "../utils/EventEmitter.js";
import {Disposables} from "../utils/Disposables.js";
import {EventEmitter} from "../utils/EventEmitter";
import {Disposables} from "../utils/Disposables";
export class ViewModel extends EventEmitter {
constructor(options = {}) {
import type {Disposable} from "../utils/Disposables";
import type {Platform} from "../platform/web/Platform";
import type {Clock} from "../platform/web/dom/Clock";
import type {ILogger} from "../logging/types";
import type {Navigation} from "./navigation/Navigation";
import type {SegmentType} from "./navigation/index";
import type {IURLRouter} from "./navigation/URLRouter";
export type Options<T extends object = SegmentType> = {
platform: Platform;
logger: ILogger;
urlCreator: IURLRouter<T>;
navigation: Navigation<T>;
emitChange?: (params: any) => void;
}
export class ViewModel<N extends object = SegmentType, O extends Options<N> = Options<N>> extends EventEmitter<{change: never}> {
private disposables?: Disposables;
private _isDisposed = false;
private _options: Readonly<O>;
constructor(options: Readonly<O>) {
super();
this.disposables = null;
this._isDisposed = false;
this._options = options;
}
childOptions(explicitOptions) {
const {navigation, urlCreator, platform} = this._options;
return Object.assign({navigation, urlCreator, platform}, explicitOptions);
childOptions<T extends Object>(explicitOptions: T): T & Options<N> {
return Object.assign({}, this._options, explicitOptions);
}
get options(): Readonly<O> { return this._options; }
// makes it easier to pass through dependencies of a sub-view model
getOption(name) {
getOption<N extends keyof O>(name: N): O[N] {
return this._options[name];
}
track(disposable) {
observeNavigation<T extends keyof N>(type: T, onChange: (value: N[T], type: T) => void): void {
const segmentObservable = this.navigation.observe(type);
const unsubscribe = segmentObservable.subscribe((value: N[T]) => {
onChange(value, type);
});
this.track(unsubscribe);
}
track<D extends Disposable>(disposable: D): D {
if (!this.disposables) {
this.disposables = new Disposables();
}
return this.disposables.track(disposable);
}
untrack(disposable) {
untrack(disposable: Disposable): undefined {
if (this.disposables) {
return this.disposables.untrack(disposable);
}
return null;
return undefined;
}
dispose() {
dispose(): void {
if (this.disposables) {
this.disposables.dispose();
}
this._isDisposed = true;
}
get isDisposed() {
get isDisposed(): boolean {
return this._isDisposed;
}
disposeTracked(disposable) {
disposeTracked(disposable: Disposable | undefined): undefined {
if (this.disposables) {
return this.disposables.disposeTracked(disposable);
}
return null;
return undefined;
}
// TODO: this will need to support binding
@ -76,7 +105,7 @@ export class ViewModel extends EventEmitter {
//
// translated string should probably always be bindings, unless we're fine with a refresh when changing the language?
// we probably are, if we're using routing with a url, we could just refresh.
i18n(parts, ...expr) {
i18n(parts: TemplateStringsArray, ...expr: any[]): string {
// just concat for now
let result = "";
for (let i = 0; i < parts.length; ++i) {
@ -88,11 +117,7 @@ export class ViewModel extends EventEmitter {
return result;
}
updateOptions(options) {
this._options = Object.assign(this._options, options);
}
emitChange(changedProps) {
emitChange(changedProps: any): void {
if (this._options.emitChange) {
this._options.emitChange(changedProps);
} else {
@ -100,27 +125,24 @@ export class ViewModel extends EventEmitter {
}
}
get platform() {
get platform(): Platform {
return this._options.platform;
}
get clock() {
get clock(): Clock {
return this._options.platform.clock;
}
get logger() {
get logger(): ILogger {
return this.platform.logger;
}
/**
* The url router, only meant to be used to create urls with from view models.
* @return {URLRouter}
*/
get urlCreator() {
get urlCreator(): IURLRouter<N> {
return this._options.urlCreator;
}
get navigation() {
return this._options.navigation;
get navigation(): Navigation<N> {
// typescript needs a little help here
return this._options.navigation as unknown as Navigation<N>;
}
}

View file

@ -14,7 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export function avatarInitials(name) {
import { Platform } from "../platform/web/Platform";
import { MediaRepository } from "../matrix/net/MediaRepository";
export function avatarInitials(name: string): string {
let firstChar = name.charAt(0);
if (firstChar === "!" || firstChar === "@" || firstChar === "#") {
firstChar = name.charAt(1);
@ -29,10 +32,10 @@ export function avatarInitials(name) {
*
* @return {number}
*/
function hashCode(str) {
function hashCode(str: string): number {
let hash = 0;
let i;
let chr;
let i: number;
let chr: number;
if (str.length === 0) {
return hash;
}
@ -44,6 +47,14 @@ function hashCode(str) {
return Math.abs(hash);
}
export function getIdentifierColorNumber(id) {
export function getIdentifierColorNumber(id: string): number {
return (hashCode(id) % 8) + 1;
}
export function getAvatarHttpUrl(avatarUrl: string, cssSize: number, platform: Platform, mediaRepository: MediaRepository): string | null {
if (avatarUrl) {
const imageSize = cssSize * platform.devicePixelRatio;
return mediaRepository.mxcUrlThumbnail(avatarUrl, imageSize, imageSize, "crop");
}
return null;
}

View file

@ -0,0 +1,76 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "../ViewModel";
import {LoginFailure} from "../../matrix/Client.js";
export class CompleteSSOLoginViewModel extends ViewModel {
constructor(options) {
super(options);
const {
loginToken,
client,
attemptLogin,
} = options;
this._loginToken = loginToken;
this._client = client;
this._attemptLogin = attemptLogin;
this._errorMessage = "";
this.performSSOLoginCompletion();
}
get errorMessage() { return this._errorMessage; }
_showError(message) {
this._errorMessage = message;
this.emitChange("errorMessage");
}
async performSSOLoginCompletion() {
if (!this._loginToken) {
return;
}
const homeserver = await this.platform.settingsStorage.getString("sso_ongoing_login_homeserver");
let loginOptions;
try {
loginOptions = await this._client.queryLogin(homeserver).result;
}
catch (err) {
this._showError(err.message);
return;
}
if (!loginOptions.token) {
this.navigation.push("session");
return;
}
const status = await this._attemptLogin(loginOptions.token(this._loginToken));
let error = "";
switch (status) {
case LoginFailure.Credentials:
error = this.i18n`Your login token is invalid.`;
break;
case LoginFailure.Connection:
error = this.i18n`Can't connect to ${homeserver}.`;
break;
case LoginFailure.Unknown:
error = this.i18n`Something went wrong while checking your login token.`;
break;
}
if (error) {
this._showError(error);
}
}
}

View file

@ -0,0 +1,293 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {Client} from "../../matrix/Client.js";
import {Options as BaseOptions, ViewModel} from "../ViewModel";
import {PasswordLoginViewModel} from "./PasswordLoginViewModel.js";
import {StartSSOLoginViewModel} from "./StartSSOLoginViewModel.js";
import {CompleteSSOLoginViewModel} from "./CompleteSSOLoginViewModel.js";
import {LoadStatus} from "../../matrix/Client.js";
import {SessionLoadViewModel} from "../SessionLoadViewModel.js";
import {SegmentType} from "../navigation/index";
import type {PasswordLoginMethod, SSOLoginHelper, TokenLoginMethod, ILoginMethod} from "../../matrix/login";
type Options = {
defaultHomeserver: string;
ready: ReadyFn;
loginToken?: string;
} & BaseOptions;
export class LoginViewModel extends ViewModel<SegmentType, Options> {
private _ready: ReadyFn;
private _loginToken?: string;
private _client: Client;
private _loginOptions?: LoginOptions;
private _passwordLoginViewModel?: PasswordLoginViewModel;
private _startSSOLoginViewModel?: StartSSOLoginViewModel;
private _completeSSOLoginViewModel?: CompleteSSOLoginViewModel;
private _loadViewModel?: SessionLoadViewModel;
private _loadViewModelSubscription?: () => void;
private _homeserver: string;
private _queriedHomeserver?: string;
private _abortHomeserverQueryTimeout?: () => void;
private _abortQueryOperation?: () => void;
private _hideHomeserver: boolean = false;
private _isBusy: boolean = false;
private _errorMessage: string = "";
constructor(options: Readonly<Options>) {
super(options);
const {ready, defaultHomeserver, loginToken} = options;
this._ready = ready;
this._loginToken = loginToken;
this._client = new Client(this.platform);
this._homeserver = defaultHomeserver;
this._initViewModels();
}
get passwordLoginViewModel(): PasswordLoginViewModel {
return this._passwordLoginViewModel;
}
get startSSOLoginViewModel(): StartSSOLoginViewModel {
return this._startSSOLoginViewModel;
}
get completeSSOLoginViewModel(): CompleteSSOLoginViewModel {
return this._completeSSOLoginViewModel;
}
get homeserver(): string {
return this._homeserver;
}
get resolvedHomeserver(): string | undefined {
return this._loginOptions?.homeserver;
}
get errorMessage(): string {
return this._errorMessage;
}
get showHomeserver(): boolean {
return !this._hideHomeserver;
}
get loadViewModel(): SessionLoadViewModel {
return this._loadViewModel;
}
get isBusy(): boolean {
return this._isBusy;
}
get isFetchingLoginOptions(): boolean {
return !!this._abortQueryOperation;
}
goBack(): void {
this.navigation.push("session");
}
private _initViewModels(): void {
if (this._loginToken) {
this._hideHomeserver = true;
this._completeSSOLoginViewModel = this.track(new CompleteSSOLoginViewModel(
this.childOptions(
{
client: this._client,
attemptLogin: (loginMethod: TokenLoginMethod) => this.attemptLogin(loginMethod),
loginToken: this._loginToken
})));
this.emitChange("completeSSOLoginViewModel");
}
else {
void this.queryHomeserver();
}
}
private _showPasswordLogin(): void {
this._passwordLoginViewModel = this.track(new PasswordLoginViewModel(
this.childOptions({
loginOptions: this._loginOptions,
attemptLogin: (loginMethod: PasswordLoginMethod) => this.attemptLogin(loginMethod)
})));
this.emitChange("passwordLoginViewModel");
}
private _showSSOLogin(): void {
this._startSSOLoginViewModel = this.track(
new StartSSOLoginViewModel(this.childOptions({loginOptions: this._loginOptions}))
);
this.emitChange("startSSOLoginViewModel");
}
private _showError(message: string): void {
this._errorMessage = message;
this.emitChange("errorMessage");
}
private _setBusy(status: boolean): void {
this._isBusy = status;
this._passwordLoginViewModel?.setBusy(status);
this._startSSOLoginViewModel?.setBusy(status);
this.emitChange("isBusy");
}
async attemptLogin(loginMethod: ILoginMethod): Promise<null> {
this._setBusy(true);
void this._client.startWithLogin(loginMethod, {inspectAccountSetup: true});
const loadStatus = this._client.loadStatus;
const handle = loadStatus.waitFor((status: LoadStatus) => status !== LoadStatus.Login);
await handle.promise;
this._setBusy(false);
const status = loadStatus.get();
if (status === LoadStatus.LoginFailed) {
return this._client.loginFailure;
}
this._hideHomeserver = true;
this.emitChange("hideHomeserver");
this._disposeViewModels();
void this._createLoadViewModel();
return null;
}
private _createLoadViewModel(): void {
this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription);
this._loadViewModel = this.disposeTracked(this._loadViewModel);
this._loadViewModel = this.track(
new SessionLoadViewModel(
this.childOptions({
ready: (client) => {
// make sure we don't delete the session in dispose when navigating away
this._client = null;
this._ready(client);
},
client: this._client,
homeserver: this._homeserver
})
)
);
void this._loadViewModel.start();
this.emitChange("loadViewModel");
this._loadViewModelSubscription = this.track(
this._loadViewModel.disposableOn("change", () => {
if (!this._loadViewModel.loading) {
this._loadViewModelSubscription = this.disposeTracked(this._loadViewModelSubscription);
}
this._setBusy(false);
})
);
}
private _disposeViewModels(): void {
this._startSSOLoginViewModel = this.disposeTracked(this._startSSOLoginViewModel);
this._passwordLoginViewModel = this.disposeTracked(this._passwordLoginViewModel);
this._completeSSOLoginViewModel = this.disposeTracked(this._completeSSOLoginViewModel);
this.emitChange("disposeViewModels");
}
async setHomeserver(newHomeserver: string): Promise<void> {
this._homeserver = newHomeserver;
// clear everything set by queryHomeserver
this._loginOptions = undefined;
this._queriedHomeserver = undefined;
this._showError("");
this._disposeViewModels();
this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation);
this.emitChange("loginViewModels"); // multiple fields changing
// also clear the timeout if it is still running
this.disposeTracked(this._abortHomeserverQueryTimeout);
const timeout = this.clock.createTimeout(1000);
this._abortHomeserverQueryTimeout = this.track(() => timeout.abort());
try {
await timeout.elapsed();
} catch (err) {
if (err.name === "AbortError") {
return; // still typing, don't query
} else {
throw err;
}
}
this._abortHomeserverQueryTimeout = this.disposeTracked(this._abortHomeserverQueryTimeout);
void this.queryHomeserver();
}
async queryHomeserver(): Promise<void> {
// don't repeat a query we've just done
if (this._homeserver === this._queriedHomeserver || this._homeserver === "") {
return;
}
this._queriedHomeserver = this._homeserver;
// given that setHomeserver already clears everything set here,
// and that is the only way to change the homeserver,
// we don't need to reset things again here.
// However, clear things set by setHomeserver:
// if query is called before the typing timeout hits (e.g. field lost focus),
// cancel the timeout so we don't query again.
this._abortHomeserverQueryTimeout = this.disposeTracked(this._abortHomeserverQueryTimeout);
// cancel ongoing query operation, if any
this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation);
try {
const queryOperation = this._client.queryLogin(this._homeserver);
this._abortQueryOperation = this.track(() => queryOperation.abort());
this.emitChange("isFetchingLoginOptions");
this._loginOptions = await queryOperation.result;
this.emitChange("resolvedHomeserver");
}
catch (e) {
if (e.name === "AbortError") {
return; //aborted, bail out
} else {
this._loginOptions = undefined;
}
} finally {
this._abortQueryOperation = this.disposeTracked(this._abortQueryOperation);
this.emitChange("isFetchingLoginOptions");
}
if (this._loginOptions) {
if (this._loginOptions.sso) { this._showSSOLogin(); }
if (this._loginOptions.password) { this._showPasswordLogin(); }
if (!this._loginOptions.sso && !this._loginOptions.password) {
this._showError("This homeserver supports neither SSO nor password based login flows");
}
}
else {
this._showError(`Could not query login methods supported by ${this.homeserver}`);
}
}
dispose(): void {
super.dispose();
if (this._client) {
// if we move away before we're done with initial sync
// delete the session
void this._client.deleteSession();
}
}
}
type ReadyFn = (client: Client) => void;
// TODO: move to Client.js when its converted to typescript.
type LoginOptions = {
homeserver: string;
password?: (username: string, password: string) => PasswordLoginMethod;
sso?: SSOLoginHelper;
token?: (loginToken: string) => TokenLoginMethod;
};

View file

@ -0,0 +1,63 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "../ViewModel";
import {LoginFailure} from "../../matrix/Client.js";
export class PasswordLoginViewModel extends ViewModel {
constructor(options) {
super(options);
const {loginOptions, attemptLogin} = options;
this._loginOptions = loginOptions;
this._attemptLogin = attemptLogin;
this._isBusy = false;
this._errorMessage = "";
}
get isBusy() { return this._isBusy; }
get errorMessage() { return this._errorMessage; }
setBusy(status) {
this._isBusy = status;
this.emitChange("isBusy");
}
_showError(message) {
this._errorMessage = message;
this.emitChange("errorMessage");
}
async login(username, password) {
this._errorMessage = "";
this.emitChange("errorMessage");
const status = await this._attemptLogin(this._loginOptions.password(username, password));
let error = "";
switch (status) {
case LoginFailure.Credentials:
error = this.i18n`Your username and/or password don't seem to be correct.`;
break;
case LoginFailure.Connection:
error = this.i18n`Can't connect to ${this._loginOptions.homeserver}.`;
break;
case LoginFailure.Unknown:
error = this.i18n`Something went wrong while checking your login and password.`;
break;
}
if (error) {
this._showError(error);
}
}
}

View file

@ -0,0 +1,38 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "../ViewModel";
export class StartSSOLoginViewModel extends ViewModel{
constructor(options) {
super(options);
this._sso = options.loginOptions.sso;
this._isBusy = false;
}
get isBusy() { return this._isBusy; }
setBusy(status) {
this._isBusy = status;
this.emitChange("isBusy");
}
async startSSOLogin() {
await this.platform.settingsStorage.setString("sso_ongoing_login_homeserver", this._sso.homeserver);
const link = this._sso.createSSORedirectURL(this.urlCreator.createSSOCallbackURL());
this.platform.openUrl(link);
}
}

View file

@ -14,29 +14,51 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {BaseObservableValue, ObservableValue} from "../../observable/ObservableValue.js";
import {BaseObservableValue, ObservableValue} from "../../observable/ObservableValue";
export class Navigation {
constructor(allowsChild) {
type AllowsChild<T> = (parent: Segment<T> | undefined, child: Segment<T>) => boolean;
/**
* OptionalValue is basically stating that if SegmentType[type] = true:
* - Allow this type to be optional
* - Give it a default value of undefined
* - Also allow it to be true
* This lets us do:
* const s: Segment<SegmentType> = new Segment("create-room");
* instead of
* const s: Segment<SegmentType> = new Segment("create-room", undefined);
*/
export type OptionalValue<T> = T extends true? [(undefined | true)?]: [T];
export class Navigation<T extends object> {
private readonly _allowsChild: AllowsChild<T>;
private _path: Path<T>;
private readonly _observables: Map<keyof T, SegmentObservable<T>> = new Map();
private readonly _pathObservable: ObservableValue<Path<T>>;
constructor(allowsChild: AllowsChild<T>) {
this._allowsChild = allowsChild;
this._path = new Path([], allowsChild);
this._observables = new Map();
this._pathObservable = new ObservableValue(this._path);
}
get pathObservable() {
get pathObservable(): ObservableValue<Path<T>> {
return this._pathObservable;
}
get path() {
get path(): Path<T> {
return this._path;
}
push(type, value = undefined) {
return this.applyPath(this.path.with(new Segment(type, value)));
push<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): void {
const newPath = this.path.with(new Segment(type, ...value));
if (newPath) {
this.applyPath(newPath);
}
}
applyPath(path) {
applyPath(path: Path<T>): void {
// Path is not exported, so you can only create a Path through Navigation,
// so we assume it respects the allowsChild rules
const oldPath = this._path;
@ -60,7 +82,7 @@ export class Navigation {
this._pathObservable.set(this._path);
}
observe(type) {
observe(type: keyof T): SegmentObservable<T> {
let observable = this._observables.get(type);
if (!observable) {
observable = new SegmentObservable(this, type);
@ -69,9 +91,9 @@ export class Navigation {
return observable;
}
pathFrom(segments) {
let parent;
let i;
pathFrom(segments: Segment<any>[]): Path<T> {
let parent: Segment<any> | undefined;
let i: number;
for (i = 0; i < segments.length; i += 1) {
if (!this._allowsChild(parent, segments[i])) {
return new Path(segments.slice(0, i), this._allowsChild);
@ -81,12 +103,12 @@ export class Navigation {
return new Path(segments, this._allowsChild);
}
segment(type, value) {
return new Segment(type, value);
segment<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): Segment<T> {
return new Segment(type, ...value);
}
}
function segmentValueEqual(a, b) {
function segmentValueEqual<T>(a?: T[keyof T], b?: T[keyof T]): boolean {
if (a === b) {
return true;
}
@ -103,24 +125,29 @@ function segmentValueEqual(a, b) {
return false;
}
export class Segment {
constructor(type, value) {
this.type = type;
this.value = value === undefined ? true : value;
export class Segment<T, K extends keyof T = any> {
public value: T[K];
constructor(public type: K, ...value: OptionalValue<T[K]>) {
this.value = (value[0] === undefined ? true : value[0]) as unknown as T[K];
}
}
class Path {
constructor(segments = [], allowsChild) {
class Path<T> {
private readonly _segments: Segment<T, any>[];
private readonly _allowsChild: AllowsChild<T>;
constructor(segments: Segment<T>[] = [], allowsChild: AllowsChild<T>) {
this._segments = segments;
this._allowsChild = allowsChild;
}
clone() {
clone(): Path<T> {
return new Path(this._segments.slice(), this._allowsChild);
}
with(segment) {
with(segment: Segment<T>): Path<T> | undefined {
let index = this._segments.length - 1;
do {
if (this._allowsChild(this._segments[index], segment)) {
@ -132,10 +159,10 @@ class Path {
index -= 1;
} while(index >= -1);
// allow -1 as well so we check if the segment is allowed as root
return null;
return undefined;
}
until(type) {
until(type: keyof T): Path<T> {
const index = this._segments.findIndex(s => s.type === type);
if (index !== -1) {
return new Path(this._segments.slice(0, index + 1), this._allowsChild)
@ -143,11 +170,27 @@ class Path {
return new Path([], this._allowsChild);
}
get(type) {
get(type: keyof T): Segment<T> | undefined {
return this._segments.find(s => s.type === type);
}
get segments() {
replace(segment: Segment<T>): Path<T> | undefined {
const index = this._segments.findIndex(s => s.type === segment.type);
if (index !== -1) {
const parent = this._segments[index - 1];
if (this._allowsChild(parent, segment)) {
const child = this._segments[index + 1];
if (!child || this._allowsChild(segment, child)) {
const newSegments = this._segments.slice();
newSegments[index] = segment;
return new Path(newSegments, this._allowsChild);
}
}
}
return undefined;
}
get segments(): Segment<T>[] {
return this._segments;
}
}
@ -156,43 +199,49 @@ class Path {
* custom observable so it always returns what is in navigation.path, even if we haven't emitted the change yet.
* This ensures that observers of a segment can also read the most recent value of other segments.
*/
class SegmentObservable extends BaseObservableValue {
constructor(navigation, type) {
class SegmentObservable<T extends object> extends BaseObservableValue<T[keyof T] | undefined> {
private readonly _navigation: Navigation<T>;
private _type: keyof T;
private _lastSetValue?: T[keyof T];
constructor(navigation: Navigation<T>, type: keyof T) {
super();
this._navigation = navigation;
this._type = type;
this._lastSetValue = navigation.path.get(type)?.value;
}
get() {
get(): T[keyof T] | undefined {
const path = this._navigation.path;
const segment = path.get(this._type);
const value = segment?.value;
return value;
}
emitIfChanged() {
emitIfChanged(): void {
const newValue = this.get();
if (!segmentValueEqual(newValue, this._lastSetValue)) {
if (!segmentValueEqual<T>(newValue, this._lastSetValue)) {
this._lastSetValue = newValue;
this.emit(newValue);
}
}
}
export type {Path};
export function tests() {
function createMockNavigation() {
return new Navigation((parent, {type}) => {
switch (parent?.type) {
case undefined:
return type === "1" || "2";
return type === "1" || type === "2";
case "1":
return type === "1.1";
case "1.1":
return type === "1.1.1";
case "2":
return type === "2.1" || "2.2";
return type === "2.1" || type === "2.2";
default:
return false;
}
@ -200,7 +249,7 @@ export function tests() {
}
function observeTypes(nav, types) {
const changes = [];
const changes: {type:string, value:any}[] = [];
for (const type of types) {
nav.observe(type).subscribe(value => {
changes.push({type, value});
@ -209,6 +258,12 @@ export function tests() {
return changes;
}
type SegmentType = {
"foo": number;
"bar": number;
"baz": number;
}
return {
"applying a path emits an event on the observable": assert => {
const nav = createMockNavigation();
@ -226,9 +281,20 @@ export function tests() {
assert.equal(changes[1].value, 8);
},
"path.get": assert => {
const path = new Path([new Segment("foo", 5), new Segment("bar", 6)], () => true);
assert.equal(path.get("foo").value, 5);
assert.equal(path.get("bar").value, 6);
const path = new Path<SegmentType>([new Segment("foo", 5), new Segment("bar", 6)], () => true);
assert.equal(path.get("foo")!.value, 5);
assert.equal(path.get("bar")!.value, 6);
},
"path.replace success": assert => {
const path = new Path<SegmentType>([new Segment("foo", 5), new Segment("bar", 6)], () => true);
const newPath = path.replace(new Segment("foo", 1));
assert.equal(newPath!.get("foo")!.value, 1);
assert.equal(newPath!.get("bar")!.value, 6);
},
"path.replace not found": assert => {
const path = new Path<SegmentType>([new Segment("foo", 5), new Segment("bar", 6)], () => true);
const newPath = path.replace(new Segment("baz", 1));
assert.equal(newPath, null);
}
};
}

View file

@ -1,98 +0,0 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
export class URLRouter {
constructor({history, navigation, parseUrlPath, stringifyPath}) {
this._history = history;
this._navigation = navigation;
this._parseUrlPath = parseUrlPath;
this._stringifyPath = stringifyPath;
this._subscription = null;
this._pathSubscription = null;
this._isApplyingUrl = false;
}
attach() {
this._subscription = this._history.subscribe(url => this._applyUrl(url));
this._applyUrl(this._history.get());
this._pathSubscription = this._navigation.pathObservable.subscribe(path => this._applyNavigationPath(path));
}
dispose() {
this._subscription = this._subscription();
this._pathSubscription = this._pathSubscription();
}
_applyNavigationPath(path) {
const url = this.urlForPath(path);
if (url !== this._history.get()) {
if (this._isApplyingUrl) {
// redirect
this._history.replaceUrlSilently(url);
} else {
this._history.pushUrlSilently(url);
}
}
}
_applyUrl(url) {
const urlPath = this._history.urlAsPath(url)
const navPath = this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path));
// this will cause _applyNavigationPath to be called,
// so set a flag whether this request came from ourselves
// (in which case it is a redirect if the url does not match the current one)
this._isApplyingUrl = true;
this._navigation.applyPath(navPath);
this._isApplyingUrl = false;
}
pushUrl(url) {
this._history.pushUrl(url);
}
getLastUrl() {
return this._history.getLastUrl();
}
urlForSegments(segments) {
let path = this._navigation.path;
for (const segment of segments) {
path = path.with(segment);
if (!path) {
return;
}
}
return this.urlForPath(path);
}
urlForSegment(type, value) {
return this.urlForSegments([this._navigation.segment(type, value)]);
}
urlUntilSegment(type) {
return this.urlForPath(this._navigation.path.until(type));
}
urlForPath(path) {
return this._history.pathAsUrl(this._stringifyPath(path));
}
openRoomActionUrl(roomId) {
// not a segment to navigation knowns about, so append it manually
const urlPath = `${this._stringifyPath(this._navigation.path.until("session"))}/open-room/${roomId}`;
return this._history.pathAsUrl(urlPath);
}
}

View file

@ -0,0 +1,160 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import type {History} from "../../platform/web/dom/History.js";
import type {Navigation, Segment, Path, OptionalValue} from "./Navigation";
import type {SubscriptionHandle} from "../../observable/BaseObservable";
type ParseURLPath<T> = (urlPath: string, currentNavPath: Path<T>, defaultSessionId?: string) => Segment<T>[];
type StringifyPath<T> = (path: Path<T>) => string;
export interface IURLRouter<T> {
attach(): void;
dispose(): void;
pushUrl(url: string): void;
tryRestoreLastUrl(): boolean;
urlForSegments(segments: Segment<T>[]): string | undefined;
urlForSegment<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): string | undefined;
urlUntilSegment(type: keyof T): string;
urlForPath(path: Path<T>): string;
openRoomActionUrl(roomId: string): string;
createSSOCallbackURL(): string;
normalizeUrl(): void;
}
export class URLRouter<T extends {session: string | boolean}> implements IURLRouter<T> {
private readonly _history: History;
private readonly _navigation: Navigation<T>;
private readonly _parseUrlPath: ParseURLPath<T>;
private readonly _stringifyPath: StringifyPath<T>;
private _subscription?: SubscriptionHandle;
private _pathSubscription?: SubscriptionHandle;
private _isApplyingUrl: boolean = false;
private _defaultSessionId?: string;
constructor(history: History, navigation: Navigation<T>, parseUrlPath: ParseURLPath<T>, stringifyPath: StringifyPath<T>) {
this._history = history;
this._navigation = navigation;
this._parseUrlPath = parseUrlPath;
this._stringifyPath = stringifyPath;
this._defaultSessionId = this._getLastSessionId();
}
private _getLastSessionId(): string | undefined {
const navPath = this._urlAsNavPath(this._history.getLastSessionUrl() || "");
const sessionId = navPath.get("session")?.value;
if (typeof sessionId === "string") {
return sessionId;
}
return undefined;
}
attach(): void {
this._subscription = this._history.subscribe(url => this._applyUrl(url));
// subscribe to path before applying initial url
// so redirects in _applyNavPathToHistory are reflected in url bar
this._pathSubscription = this._navigation.pathObservable.subscribe(path => this._applyNavPathToHistory(path));
this._applyUrl(this._history.get());
}
dispose(): void {
if (this._subscription) { this._subscription = this._subscription(); }
if (this._pathSubscription) { this._pathSubscription = this._pathSubscription(); }
}
private _applyNavPathToHistory(path: Path<T>): void {
const url = this.urlForPath(path);
if (url !== this._history.get()) {
if (this._isApplyingUrl) {
// redirect
this._history.replaceUrlSilently(url);
} else {
this._history.pushUrlSilently(url);
}
}
}
private _applyNavPathToNavigation(navPath: Path<T>): void {
// this will cause _applyNavPathToHistory to be called,
// so set a flag whether this request came from ourselves
// (in which case it is a redirect if the url does not match the current one)
this._isApplyingUrl = true;
this._navigation.applyPath(navPath);
this._isApplyingUrl = false;
}
private _urlAsNavPath(url: string): Path<T> {
const urlPath = this._history.urlAsPath(url);
return this._navigation.pathFrom(this._parseUrlPath(urlPath, this._navigation.path, this._defaultSessionId));
}
private _applyUrl(url: string): void {
const navPath = this._urlAsNavPath(url);
this._applyNavPathToNavigation(navPath);
}
pushUrl(url: string): void {
this._history.pushUrl(url);
}
tryRestoreLastUrl(): boolean {
const lastNavPath = this._urlAsNavPath(this._history.getLastSessionUrl() || "");
if (lastNavPath.segments.length !== 0) {
this._applyNavPathToNavigation(lastNavPath);
return true;
}
return false;
}
urlForSegments(segments: Segment<T>[]): string | undefined {
let path: Path<T> | undefined = this._navigation.path;
for (const segment of segments) {
path = path.with(segment);
if (!path) {
return;
}
}
return this.urlForPath(path);
}
urlForSegment<K extends keyof T>(type: K, ...value: OptionalValue<T[K]>): string | undefined {
return this.urlForSegments([this._navigation.segment(type, ...value)]);
}
urlUntilSegment(type: keyof T): string {
return this.urlForPath(this._navigation.path.until(type));
}
urlForPath(path: Path<T>): string {
return this._history.pathAsUrl(this._stringifyPath(path));
}
openRoomActionUrl(roomId: string): string {
// not a segment to navigation knowns about, so append it manually
const urlPath = `${this._stringifyPath(this._navigation.path.until("session"))}/open-room/${roomId}`;
return this._history.pathAsUrl(urlPath);
}
createSSOCallbackURL(): string {
return window.location.origin;
}
normalizeUrl(): void {
// Remove any queryParameters from the URL
// Gets rid of the loginToken after SSO
this._history.replaceUrlSilently(`${window.location.origin}/${window.location.hash}`);
}
}

View file

@ -1,248 +0,0 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {Navigation, Segment} from "./Navigation.js";
import {URLRouter} from "./URLRouter.js";
export function createNavigation() {
return new Navigation(allowsChild);
}
export function createRouter({history, navigation}) {
return new URLRouter({history, navigation, stringifyPath, parseUrlPath});
}
function allowsChild(parent, child) {
const {type} = child;
switch (parent?.type) {
case undefined:
// allowed root segments
return type === "login" || type === "session";
case "session":
return type === "room" || type === "rooms" || type === "settings";
case "rooms":
// downside of the approach: both of these will control which tile is selected
return type === "room" || type === "empty-grid-tile";
case "room":
return type === "lightbox";
default:
return false;
}
}
function roomsSegmentWithRoom(rooms, roomId, path) {
if(!rooms.value.includes(roomId)) {
const emptyGridTile = path.get("empty-grid-tile");
const oldRoom = path.get("room");
let index = 0;
if (emptyGridTile) {
index = emptyGridTile.value;
} else if (oldRoom) {
index = rooms.value.indexOf(oldRoom.value);
}
const roomIds = rooms.value.slice();
roomIds[index] = roomId;
return new Segment("rooms", roomIds);
} else {
return rooms;
}
}
export function parseUrlPath(urlPath, currentNavPath) {
// substr(1) to take of initial /
const parts = urlPath.substr(1).split("/");
const iterator = parts[Symbol.iterator]();
const segments = [];
let next;
while (!(next = iterator.next()).done) {
const type = next.value;
if (type === "rooms") {
const roomsValue = iterator.next().value;
if (roomsValue === undefined) { break; }
const roomIds = roomsValue.split(",");
segments.push(new Segment(type, roomIds));
const selectedIndex = parseInt(iterator.next().value || "0", 10);
const roomId = roomIds[selectedIndex];
if (roomId) {
segments.push(new Segment("room", roomId));
} else {
segments.push(new Segment("empty-grid-tile", selectedIndex));
}
} else if (type === "open-room") {
const roomId = iterator.next().value;
if (!roomId) { break; }
const rooms = currentNavPath.get("rooms");
if (rooms) {
segments.push(roomsSegmentWithRoom(rooms, roomId, currentNavPath));
}
segments.push(new Segment("room", roomId));
} else {
// might be undefined, which will be turned into true by Segment
const value = iterator.next().value;
segments.push(new Segment(type, value));
}
}
return segments;
}
export function stringifyPath(path) {
let urlPath = "";
let prevSegment;
for (const segment of path.segments) {
switch (segment.type) {
case "rooms":
urlPath += `/rooms/${segment.value.join(",")}`;
break;
case "empty-grid-tile":
urlPath += `/${segment.value}`;
break;
case "room":
if (prevSegment?.type === "rooms") {
const index = prevSegment.value.indexOf(segment.value);
urlPath += `/${index}`;
} else {
urlPath += `/${segment.type}/${segment.value}`;
}
break;
default:
urlPath += `/${segment.type}`;
if (segment.value && segment.value !== true) {
urlPath += `/${segment.value}`;
}
}
prevSegment = segment;
}
return urlPath;
}
export function tests() {
return {
"stringify grid url with focused empty tile": assert => {
const nav = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("empty-grid-tile", 3)
]);
const urlPath = stringifyPath(path);
assert.equal(urlPath, "/session/1/rooms/a,b,c/3");
},
"stringify grid url with focused room": assert => {
const nav = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const urlPath = stringifyPath(path);
assert.equal(urlPath, "/session/1/rooms/a,b,c/1");
},
"parse grid url path with focused empty tile": assert => {
const segments = parseUrlPath("/session/1/rooms/a,b,c/3");
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "empty-grid-tile");
assert.equal(segments[2].value, 3);
},
"parse grid url path with focused room": assert => {
const segments = parseUrlPath("/session/1/rooms/a,b,c/1");
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "b");
},
"parse empty grid url": assert => {
const segments = parseUrlPath("/session/1/rooms/");
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, [""]);
assert.equal(segments[2].type, "empty-grid-tile");
assert.equal(segments[2].value, 0);
},
"parse empty grid url with focus": assert => {
const segments = parseUrlPath("/session/1/rooms//1");
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, [""]);
assert.equal(segments[2].type, "empty-grid-tile");
assert.equal(segments[2].value, 1);
},
"parse open-room action replacing the current focused room": assert => {
const nav = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const segments = parseUrlPath("/session/1/open-room/d", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "d", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "d");
},
"parse open-room action changing focus to an existing room": assert => {
const nav = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const segments = parseUrlPath("/session/1/open-room/a", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "a");
},
"parse open-room action setting a room in an empty tile": assert => {
const nav = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("empty-grid-tile", 4)
]);
const segments = parseUrlPath("/session/1/open-room/d", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c", , "d"]); //eslint-disable-line no-sparse-arrays
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "d");
},
"parse session url path without id": assert => {
const segments = parseUrlPath("/session");
assert.equal(segments.length, 1);
assert.equal(segments[0].type, "session");
assert.strictEqual(segments[0].value, true);
}
}
}

View file

@ -0,0 +1,490 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {Navigation, Segment} from "./Navigation";
import {URLRouter} from "./URLRouter";
import type {Path, OptionalValue} from "./Navigation";
export type SegmentType = {
"login": true;
"session": string | boolean;
"sso": string;
"logout": true;
"room": string;
"rooms": string[];
"settings": true;
"create-room": true;
"empty-grid-tile": number;
"lightbox": string;
"right-panel": true;
"details": true;
"members": true;
"member": string;
};
export function createNavigation(): Navigation<SegmentType> {
return new Navigation(allowsChild);
}
export function createRouter({history, navigation}: {history: History, navigation: Navigation<SegmentType>}): URLRouter<SegmentType> {
return new URLRouter(history, navigation, parseUrlPath, stringifyPath);
}
function allowsChild(parent: Segment<SegmentType> | undefined, child: Segment<SegmentType>): boolean {
const {type} = child;
switch (parent?.type) {
case undefined:
// allowed root segments
return type === "login" || type === "session" || type === "sso" || type === "logout";
case "session":
return type === "room" || type === "rooms" || type === "settings" || type === "create-room";
case "rooms":
// downside of the approach: both of these will control which tile is selected
return type === "room" || type === "empty-grid-tile";
case "room":
return type === "lightbox" || type === "right-panel";
case "right-panel":
return type === "details"|| type === "members" || type === "member";
default:
return false;
}
}
export function removeRoomFromPath(path: Path<SegmentType>, roomId: string): Path<SegmentType> | undefined {
let newPath: Path<SegmentType> | undefined = path;
const rooms = newPath.get("rooms");
let roomIdGridIndex = -1;
// first delete from rooms segment
if (rooms) {
roomIdGridIndex = rooms.value.indexOf(roomId);
if (roomIdGridIndex !== -1) {
const idsWithoutRoom = rooms.value.slice();
idsWithoutRoom[roomIdGridIndex] = "";
newPath = newPath.replace(new Segment("rooms", idsWithoutRoom));
}
}
const room = newPath!.get("room");
// then from room (which occurs with or without rooms)
if (room && room.value === roomId) {
if (roomIdGridIndex !== -1) {
newPath = newPath!.with(new Segment("empty-grid-tile", roomIdGridIndex));
} else {
newPath = newPath!.until("session");
}
}
return newPath;
}
function roomsSegmentWithRoom(rooms: Segment<SegmentType, "rooms">, roomId: string, path: Path<SegmentType>): Segment<SegmentType, "rooms"> {
if(!rooms.value.includes(roomId)) {
const emptyGridTile = path.get("empty-grid-tile");
const oldRoom = path.get("room");
let index = 0;
if (emptyGridTile) {
index = emptyGridTile.value;
} else if (oldRoom) {
index = rooms.value.indexOf(oldRoom.value);
}
const roomIds = rooms.value.slice();
roomIds[index] = roomId;
return new Segment("rooms", roomIds);
} else {
return rooms;
}
}
function pushRightPanelSegment<T extends keyof SegmentType>(array: Segment<SegmentType>[], segment: T, ...value: OptionalValue<SegmentType[T]>): void {
array.push(new Segment("right-panel"));
array.push(new Segment(segment, ...value));
}
export function addPanelIfNeeded<T extends SegmentType>(navigation: Navigation<T>, path: Path<T>): Path<T> {
const segments = navigation.path.segments;
const i = segments.findIndex(segment => segment.type === "right-panel");
let _path = path;
if (i !== -1) {
_path = path.until("room");
_path = _path.with(segments[i])!;
_path = _path.with(segments[i + 1])!;
}
return _path;
}
export function parseUrlPath(urlPath: string, currentNavPath: Path<SegmentType>, defaultSessionId?: string): Segment<SegmentType>[] {
// substring(1) to take of initial /
const parts = urlPath.substring(1).split("/");
const iterator = parts[Symbol.iterator]();
const segments: Segment<SegmentType>[] = [];
let next;
while (!(next = iterator.next()).done) {
const type = next.value;
if (type === "rooms") {
const roomsValue = iterator.next().value;
if (roomsValue === undefined) { break; }
const roomIds = roomsValue.split(",");
segments.push(new Segment(type, roomIds));
const selectedIndex = parseInt(iterator.next().value || "0", 10);
const roomId = roomIds[selectedIndex];
if (roomId) {
segments.push(new Segment("room", roomId));
} else {
segments.push(new Segment("empty-grid-tile", selectedIndex));
}
} else if (type === "open-room") {
const roomId = iterator.next().value;
if (!roomId) { break; }
const rooms = currentNavPath.get("rooms");
if (rooms) {
segments.push(roomsSegmentWithRoom(rooms, roomId, currentNavPath));
}
segments.push(new Segment("room", roomId));
const openRoomPartIndex = parts.findIndex(part => part === "open-room");
const hasOnlyRoomIdAfterPart = openRoomPartIndex >= parts.length - 2;
if (hasOnlyRoomIdAfterPart) {
// Copy right-panel segments from previous path only if there are no other parts after open-room
// fixes memberlist -> member details closing/opening grid view
const previousSegments = currentNavPath.segments;
const i = previousSegments.findIndex(s => s.type === "right-panel");
if (i !== -1) {
segments.push(...previousSegments.slice(i));
}
}
} else if (type === "last-session") {
let sessionSegment = currentNavPath.get("session");
if (typeof sessionSegment?.value !== "string" && defaultSessionId) {
sessionSegment = new Segment("session", defaultSessionId);
}
if (sessionSegment) {
segments.push(sessionSegment);
}
} else if (type === "details" || type === "members") {
pushRightPanelSegment(segments, type);
} else if (type === "member") {
const userId = iterator.next().value;
if (!userId) { break; }
pushRightPanelSegment(segments, type, userId);
} else if (type.includes("loginToken")) {
// Special case for SSO-login with query parameter loginToken=<token>
const loginToken = type.split("=").pop();
segments.push(new Segment("sso", loginToken));
} else {
// might be undefined, which will be turned into true by Segment
const value = iterator.next().value;
segments.push(new Segment(type, value));
}
}
return segments;
}
export function stringifyPath(path: Path<SegmentType>): string {
let urlPath = "";
let prevSegment: Segment<SegmentType> | undefined;
for (const segment of path.segments) {
switch (segment.type) {
case "rooms":
urlPath += `/rooms/${segment.value.join(",")}`;
break;
case "empty-grid-tile":
urlPath += `/${segment.value}`;
break;
case "room":
if (prevSegment?.type === "rooms") {
const index = prevSegment.value.indexOf(segment.value);
urlPath += `/${index}`;
} else {
urlPath += `/${segment.type}/${segment.value}`;
}
break;
case "right-panel":
case "sso":
// Do not put these segments in URL
continue;
default:
urlPath += `/${segment.type}`;
if (segment.value && segment.value !== true) {
urlPath += `/${segment.value}`;
}
}
prevSegment = segment;
}
return urlPath;
}
export function tests() {
function createEmptyPath() {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([]);
return path;
}
return {
"stringify grid url with focused empty tile": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("empty-grid-tile", 3)
]);
const urlPath = stringifyPath(path);
assert.equal(urlPath, "/session/1/rooms/a,b,c/3");
},
"stringify grid url with focused room": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const urlPath = stringifyPath(path);
assert.equal(urlPath, "/session/1/rooms/a,b,c/1");
},
"stringify url with right-panel and details segment": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b"),
new Segment("right-panel"),
new Segment("details")
]);
const urlPath = stringifyPath(path);
assert.equal(urlPath, "/session/1/rooms/a,b,c/1/details");
},
"Parse loginToken query parameter into SSO segment": assert => {
const path = createEmptyPath();
const segments = parseUrlPath("?loginToken=a1232aSD123", path);
assert.equal(segments.length, 1);
assert.equal(segments[0].type, "sso");
assert.equal(segments[0].value, "a1232aSD123");
},
"parse grid url path with focused empty tile": assert => {
const path = createEmptyPath();
const segments = parseUrlPath("/session/1/rooms/a,b,c/3", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "empty-grid-tile");
assert.equal(segments[2].value, 3);
},
"parse grid url path with focused room": assert => {
const path = createEmptyPath();
const segments = parseUrlPath("/session/1/rooms/a,b,c/1", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "b");
},
"parse empty grid url": assert => {
const path = createEmptyPath();
const segments = parseUrlPath("/session/1/rooms/", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, [""]);
assert.equal(segments[2].type, "empty-grid-tile");
assert.equal(segments[2].value, 0);
},
"parse empty grid url with focus": assert => {
const path = createEmptyPath();
const segments = parseUrlPath("/session/1/rooms//1", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, [""]);
assert.equal(segments[2].type, "empty-grid-tile");
assert.equal(segments[2].value, 1);
},
"parse open-room action replacing the current focused room": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const segments = parseUrlPath("/session/1/open-room/d", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "d", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "d");
},
"parse open-room action changing focus to an existing room": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const segments = parseUrlPath("/session/1/open-room/a", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "a");
},
"parse open-room action changing focus to an existing room with details open": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b"),
new Segment("right-panel", true),
new Segment("details", true)
]);
const segments = parseUrlPath("/session/1/open-room/a", path);
assert.equal(segments.length, 5);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "a");
assert.equal(segments[3].type, "right-panel");
assert.equal(segments[3].value, true);
assert.equal(segments[4].type, "details");
assert.equal(segments[4].value, true);
},
"open-room action should only copy over previous segments if there are no parts after open-room": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b"),
new Segment("right-panel", true),
new Segment("members", true)
]);
const segments = parseUrlPath("/session/1/open-room/a/member/foo", path);
assert.equal(segments.length, 5);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c"]);
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "a");
assert.equal(segments[3].type, "right-panel");
assert.equal(segments[3].value, true);
assert.equal(segments[4].type, "member");
assert.equal(segments[4].value, "foo");
},
"parse open-room action setting a room in an empty tile": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("empty-grid-tile", 4)
]);
const segments = parseUrlPath("/session/1/open-room/d", path);
assert.equal(segments.length, 3);
assert.equal(segments[0].type, "session");
assert.equal(segments[0].value, "1");
assert.equal(segments[1].type, "rooms");
assert.deepEqual(segments[1].value, ["a", "b", "c", , "d"]); //eslint-disable-line no-sparse-arrays
assert.equal(segments[2].type, "room");
assert.equal(segments[2].value, "d");
},
"parse session url path without id": assert => {
const path = createEmptyPath();
const segments = parseUrlPath("/session", path);
assert.equal(segments.length, 1);
assert.equal(segments[0].type, "session");
assert.strictEqual(segments[0].value, true);
},
"remove active room from grid path turns it into empty tile": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const newPath = removeRoomFromPath(path, "b");
assert.equal(newPath?.segments.length, 3);
assert.equal(newPath?.segments[0].type, "session");
assert.equal(newPath?.segments[0].value, 1);
assert.equal(newPath?.segments[1].type, "rooms");
assert.deepEqual(newPath?.segments[1].value, ["a", "", "c"]);
assert.equal(newPath?.segments[2].type, "empty-grid-tile");
assert.equal(newPath?.segments[2].value, 1);
},
"remove inactive room from grid path": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", "c"]),
new Segment("room", "b")
]);
const newPath = removeRoomFromPath(path, "a");
assert.equal(newPath?.segments.length, 3);
assert.equal(newPath?.segments[0].type, "session");
assert.equal(newPath?.segments[0].value, 1);
assert.equal(newPath?.segments[1].type, "rooms");
assert.deepEqual(newPath?.segments[1].value, ["", "b", "c"]);
assert.equal(newPath?.segments[2].type, "room");
assert.equal(newPath?.segments[2].value, "b");
},
"remove inactive room from grid path with empty tile": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("rooms", ["a", "b", ""]),
new Segment("empty-grid-tile", 3)
]);
const newPath = removeRoomFromPath(path, "b");
assert.equal(newPath?.segments.length, 3);
assert.equal(newPath?.segments[0].type, "session");
assert.equal(newPath?.segments[0].value, 1);
assert.equal(newPath?.segments[1].type, "rooms");
assert.deepEqual(newPath?.segments[1].value, ["a", "", ""]);
assert.equal(newPath?.segments[2].type, "empty-grid-tile");
assert.equal(newPath?.segments[2].value, 3);
},
"remove active room": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("room", "b")
]);
const newPath = removeRoomFromPath(path, "b");
assert.equal(newPath?.segments.length, 1);
assert.equal(newPath?.segments[0].type, "session");
assert.equal(newPath?.segments[0].value, 1);
},
"remove inactive room doesn't do anything": assert => {
const nav: Navigation<SegmentType> = new Navigation(allowsChild);
const path = nav.pathFrom([
new Segment("session", 1),
new Segment("room", "b")
]);
const newPath = removeRoomFromPath(path, "a");
assert.equal(newPath?.segments.length, 2);
assert.equal(newPath?.segments[0].type, "session");
assert.equal(newPath?.segments[0].value, 1);
assert.equal(newPath?.segments[1].type, "room");
assert.equal(newPath?.segments[1].value, "b");
},
}
}

65
src/domain/rageshake.ts Normal file
View file

@ -0,0 +1,65 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import type {BlobHandle} from "../platform/web/dom/BlobHandle";
import type {RequestFunction} from "../platform/types/types";
// see https://github.com/matrix-org/rageshake#readme
type RageshakeData = {
// A textual description of the problem. Included in the details.log.gz file.
text: string | undefined;
// Application user-agent. Included in the details.log.gz file.
userAgent: string;
// Identifier for the application (eg 'riot-web'). Should correspond to a mapping configured in the configuration file for github issue reporting to work.
app: string;
// Application version. Included in the details.log.gz file.
version: string;
// Label to attach to the github issue, and include in the details file.
label: string | undefined;
};
export async function submitLogsToRageshakeServer(data: RageshakeData, logsBlob: BlobHandle, submitUrl: string, request: RequestFunction): Promise<void> {
const formData = new Map<string, string | {name: string, blob: BlobHandle}>();
if (data.text) {
formData.set("text", data.text);
}
formData.set("user_agent", data.userAgent);
formData.set("app", data.app);
formData.set("version", data.version);
if (data.label) {
formData.set("label", data.label);
}
formData.set("file", {name: "logs.json", blob: logsBlob});
const headers: Map<string, string> = new Map();
headers.set("Accept", "application/json");
const result = request(submitUrl, {
method: "POST",
body: formData,
headers
});
let response;
try {
response = await result.response();
} catch (err) {
throw new Error(`Could not submit logs to ${submitUrl}, got error ${err.message}`);
}
const {status, body} = response;
if (status < 200 || status >= 300) {
throw new Error(`Could not submit logs to ${submitUrl}, got status code ${status} with body ${body}`);
}
// we don't bother with reading report_url from the body as the rageshake server doesn't always return it
// and would have to have CORS setup properly for us to be able to read it.
}

View file

@ -0,0 +1,144 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "../ViewModel";
import {imageToInfo} from "./common.js";
import {RoomType} from "../../matrix/room/common";
export class CreateRoomViewModel extends ViewModel {
constructor(options) {
super(options);
const {session} = options;
this._session = session;
this._name = undefined;
this._topic = undefined;
this._roomAlias = undefined;
this._isPublic = false;
this._isEncrypted = true;
this._isAdvancedShown = false;
this._isFederationDisabled = false;
this._avatarScaledBlob = undefined;
this._avatarFileName = undefined;
this._avatarInfo = undefined;
}
get isPublic() { return this._isPublic; }
get isEncrypted() { return this._isEncrypted; }
get canCreate() { return !!this._name; }
avatarUrl() { return this._avatarScaledBlob.url; }
get avatarTitle() { return this._name; }
get avatarLetter() { return ""; }
get avatarColorNumber() { return 0; }
get hasAvatar() { return !!this._avatarScaledBlob; }
get isFederationDisabled() { return this._isFederationDisabled; }
get isAdvancedShown() { return this._isAdvancedShown; }
setName(name) {
this._name = name;
this.emitChange("canCreate");
}
setRoomAlias(roomAlias) {
this._roomAlias = roomAlias;
}
setTopic(topic) {
this._topic = topic;
}
setPublic(isPublic) {
this._isPublic = isPublic;
this.emitChange("isPublic");
}
setEncrypted(isEncrypted) {
this._isEncrypted = isEncrypted;
this.emitChange("isEncrypted");
}
setFederationDisabled(disable) {
this._isFederationDisabled = disable;
this.emitChange("isFederationDisabled");
}
toggleAdvancedShown() {
this._isAdvancedShown = !this._isAdvancedShown;
this.emitChange("isAdvancedShown");
}
create() {
let avatar;
if (this._avatarScaledBlob) {
avatar = {
info: this._avatarInfo,
name: this._avatarFileName,
blob: this._avatarScaledBlob
}
}
const roomBeingCreated = this._session.createRoom({
type: this.isPublic ? RoomType.Public : RoomType.Private,
name: this._name ?? undefined,
topic: this._topic ?? undefined,
isEncrypted: !this.isPublic && this._isEncrypted,
isFederationDisabled: this._isFederationDisabled,
alias: this.isPublic ? ensureAliasIsLocalPart(this._roomAlias) : undefined,
avatar,
});
this.navigation.push("room", roomBeingCreated.id);
}
async selectAvatar() {
if (!this.platform.hasReadPixelPermission()) {
alert("Please allow canvas image data access, so we can scale your images down.");
return;
}
if (this._avatarScaledBlob) {
this._avatarScaledBlob.dispose();
}
this._avatarScaledBlob = undefined;
this._avatarFileName = undefined;
this._avatarInfo = undefined;
const file = await this.platform.openFile("image/*");
if (!file || !file.blob.mimeType.startsWith("image/")) {
// allow to clear the avatar by not selecting an image
this.emitChange("hasAvatar");
return;
}
let image = await this.platform.loadImage(file.blob);
const limit = 800;
if (image.maxDimension > limit) {
const scaledImage = await image.scale(limit);
image.dispose();
image = scaledImage;
}
this._avatarScaledBlob = image.blob;
this._avatarInfo = imageToInfo(image);
this._avatarFileName = file.name;
this.emitChange("hasAvatar");
}
}
function ensureAliasIsLocalPart(roomAliasLocalPart) {
if (roomAliasLocalPart.startsWith("#")) {
roomAliasLocalPart = roomAliasLocalPart.substr(1);
}
const colonIdx = roomAliasLocalPart.indexOf(":");
if (colonIdx !== -1) {
roomAliasLocalPart = roomAliasLocalPart.substr(0, colonIdx);
}
return roomAliasLocalPart;
}

View file

@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "../ViewModel.js";
import {ViewModel} from "../ViewModel";
import {addPanelIfNeeded} from "../navigation/index";
function dedupeSparse(roomIds) {
return roomIds.map((id, idx) => {
@ -32,10 +33,9 @@ export class RoomGridViewModel extends ViewModel {
this._width = options.width;
this._height = options.height;
this._createRoomViewModel = options.createRoomViewModel;
this._createRoomViewModelObservable = options.createRoomViewModelObservable;
this._selectedIndex = 0;
this._viewModels = [];
this._viewModelsObservables = [];
this._setupNavigation();
}
@ -54,9 +54,9 @@ export class RoomGridViewModel extends ViewModel {
this.track(focusedRoom.subscribe(roomId => {
if (roomId) {
// as the room will be in the "rooms" observable
// (monitored by the parent vm) as well,
// (monitored by the parent vmo) as well,
// we only change the focus here and trust
// setRoomIds to have created the vm already
// setRoomIds to have created the vmo already
this._setFocusRoom(roomId);
}
}));
@ -64,7 +64,7 @@ export class RoomGridViewModel extends ViewModel {
}
roomViewModelAt(i) {
return this._viewModels[i];
return this._viewModelsObservables[i]?.get();
}
get focusIndex() {
@ -79,13 +79,20 @@ export class RoomGridViewModel extends ViewModel {
return this._height;
}
_switchToRoom(roomId) {
let path = this.navigation.path.until("rooms");
path = path.with(this.navigation.segment("room", roomId));
path = addPanelIfNeeded(this.navigation, path);
this.navigation.applyPath(path);
}
focusTile(index) {
if (index === this._selectedIndex) {
return;
}
const vm = this._viewModels[index];
if (vm) {
this.navigation.push("room", vm.id);
const vmo = this._viewModelsObservables[index];
if (vmo) {
this._switchToRoom(vmo.id);
} else {
this.navigation.push("empty-grid-tile", index);
}
@ -98,7 +105,8 @@ export class RoomGridViewModel extends ViewModel {
if (existingRoomVM) {
const index = roomIds.indexOf(existingRoomVM.id);
if (index !== -1) {
this._viewModels[index] = this.track(existingRoomVM);
this._viewModelsObservables[index] = this.track(existingRoomVM);
existingRoomVM.subscribe(viewModel => this._refreshRoomViewModel(viewModel));
transfered = true;
}
}
@ -106,7 +114,7 @@ export class RoomGridViewModel extends ViewModel {
// now all view models exist, set the focus to the selected room
const focusedRoom = this.navigation.path.get("room");
if (focusedRoom) {
const index = this._viewModels.findIndex(vm => vm && vm.id === focusedRoom.value);
const index = this._viewModelsObservables.findIndex(vmo => vmo && vmo.id === focusedRoom.value);
if (index !== -1) {
this._selectedIndex = index;
}
@ -121,17 +129,17 @@ export class RoomGridViewModel extends ViewModel {
const len = this._height * this._width;
for (let i = 0; i < len; i += 1) {
const newId = roomIds[i];
const vm = this._viewModels[i];
const vmo = this._viewModelsObservables[i];
// did anything change?
if ((!vm && newId) || (vm && vm.id !== newId)) {
if (vm) {
this._viewModels[i] = this.disposeTracked(vm);
if ((!vmo && newId) || (vmo && vmo.id !== newId)) {
if (vmo) {
this._viewModelsObservables[i] = this.disposeTracked(vmo);
}
if (newId) {
const newVM = this._createRoomViewModel(newId);
if (newVM) {
this._viewModels[i] = this.track(newVM);
}
const vmo = this._createRoomViewModelObservable(newId);
this._viewModelsObservables[i] = this.track(vmo);
vmo.subscribe(viewModel => this._refreshRoomViewModel(viewModel));
vmo.initialize();
}
changed = true;
}
@ -142,14 +150,20 @@ export class RoomGridViewModel extends ViewModel {
return changed;
}
_refreshRoomViewModel(viewModel) {
this.emitChange();
viewModel?.focus();
}
/** called from SessionViewModel */
releaseRoomViewModel(roomId) {
const index = this._viewModels.findIndex(vm => vm && vm.id === roomId);
const index = this._viewModelsObservables.findIndex(vmo => vmo && vmo.id === roomId);
if (index !== -1) {
const vm = this._viewModels[index];
this.untrack(vm);
this._viewModels[index] = null;
return vm;
const vmo = this._viewModelsObservables[index];
this.untrack(vmo);
vmo.unsubscribeAll();
this._viewModelsObservables[index] = null;
return vmo;
}
}
@ -158,20 +172,22 @@ export class RoomGridViewModel extends ViewModel {
return;
}
this._selectedIndex = idx;
const vm = this._viewModels[this._selectedIndex];
vm?.focus();
const vmo = this._viewModelsObservables[this._selectedIndex];
vmo?.get()?.focus();
this.emitChange("focusIndex");
}
_setFocusRoom(roomId) {
const index = this._viewModels.findIndex(vm => vm?.id === roomId);
const index = this._viewModelsObservables.findIndex(vmo => vmo?.id === roomId);
if (index >= 0) {
this._setFocusIndex(index);
}
}
}
import {createNavigation} from "../navigation/index.js";
import {createNavigation} from "../navigation/index";
import {ObservableValue} from "../../observable/ObservableValue";
export function tests() {
class RoomVMMock {
constructor(id) {
@ -187,6 +203,12 @@ export function tests() {
}
}
class RoomViewModelObservableMock extends ObservableValue {
async initialize() {}
dispose() { this.get()?.dispose(); }
get id() { return this.get()?.id; }
}
function createNavigationForRoom(rooms, room) {
const navigation = createNavigation();
navigation.applyPath(navigation.pathFrom([
@ -211,7 +233,7 @@ export function tests() {
"initialize with duplicate set of rooms": assert => {
const navigation = createNavigationForRoom(["c", "a", "b", undefined, "a"], "a");
const gridVM = new RoomGridViewModel({
createRoomViewModel: id => new RoomVMMock(id),
createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),
navigation,
width: 3,
height: 2,
@ -228,12 +250,12 @@ export function tests() {
"transfer room view model": assert => {
const navigation = createNavigationForRoom(["a"], "a");
const gridVM = new RoomGridViewModel({
createRoomViewModel: () => assert.fail("no vms should be created"),
createRoomViewModelObservable: () => assert.fail("no vms should be created"),
navigation,
width: 3,
height: 2,
});
const existingRoomVM = new RoomVMMock("a");
const existingRoomVM = new RoomViewModelObservableMock(new RoomVMMock("a"));
const transfered = gridVM.initializeRoomIdsAndTransferVM(navigation.path.get("rooms").value, existingRoomVM);
assert.equal(transfered, true);
assert.equal(gridVM.focusIndex, 0);
@ -242,12 +264,12 @@ export function tests() {
"reject transfer for non-matching room view model": assert => {
const navigation = createNavigationForRoom(["a"], "a");
const gridVM = new RoomGridViewModel({
createRoomViewModel: id => new RoomVMMock(id),
createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),
navigation,
width: 3,
height: 2,
});
const existingRoomVM = new RoomVMMock("f");
const existingRoomVM = new RoomViewModelObservableMock(new RoomVMMock("f"));
const transfered = gridVM.initializeRoomIdsAndTransferVM(navigation.path.get("rooms").value, existingRoomVM);
assert.equal(transfered, false);
assert.equal(gridVM.focusIndex, 0);
@ -256,7 +278,7 @@ export function tests() {
"created & released room view model is not disposed": assert => {
const navigation = createNavigationForRoom(["a"], "a");
const gridVM = new RoomGridViewModel({
createRoomViewModel: id => new RoomVMMock(id),
createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),
navigation,
width: 3,
height: 2,
@ -265,27 +287,27 @@ export function tests() {
assert.equal(transfered, false);
const releasedVM = gridVM.releaseRoomViewModel("a");
gridVM.dispose();
assert.equal(releasedVM.disposed, false);
assert.equal(releasedVM.get().disposed, false);
},
"transfered & released room view model is not disposed": assert => {
const navigation = createNavigationForRoom([undefined, "a"], "a");
const gridVM = new RoomGridViewModel({
createRoomViewModel: () => assert.fail("no vms should be created"),
createRoomViewModelObservable: () => assert.fail("no vms should be created"),
navigation,
width: 3,
height: 2,
});
const existingRoomVM = new RoomVMMock("a");
const existingRoomVM = new RoomViewModelObservableMock(new RoomVMMock("a"));
const transfered = gridVM.initializeRoomIdsAndTransferVM(navigation.path.get("rooms").value, existingRoomVM);
assert.equal(transfered, true);
const releasedVM = gridVM.releaseRoomViewModel("a");
gridVM.dispose();
assert.equal(releasedVM.disposed, false);
assert.equal(releasedVM.get().disposed, false);
},
"try release non-existing room view model is": assert => {
const navigation = createNavigationForEmptyTile([undefined, "b"], 3);
const gridVM = new RoomGridViewModel({
createRoomViewModel: id => new RoomVMMock(id),
createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),
navigation,
width: 3,
height: 2,
@ -297,7 +319,7 @@ export function tests() {
"initial focus is set to empty tile": assert => {
const navigation = createNavigationForEmptyTile(["a"], 1);
const gridVM = new RoomGridViewModel({
createRoomViewModel: id => new RoomVMMock(id),
createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),
navigation,
width: 3,
height: 2,
@ -309,7 +331,7 @@ export function tests() {
"change room ids after creation": assert => {
const navigation = createNavigationForRoom(["a", "b"], "a");
const gridVM = new RoomGridViewModel({
createRoomViewModel: id => new RoomVMMock(id),
createRoomViewModelObservable: id => new RoomViewModelObservableMock(new RoomVMMock(id)),
navigation,
width: 3,
height: 2,

View file

@ -0,0 +1,91 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {ObservableValue} from "../../observable/ObservableValue";
import {RoomStatus} from "../../matrix/room/common";
/**
Depending on the status of a room (invited, joined, archived, or none),
we want to show a different view with a different view model
when showing a room. Furthermore, this logic is needed both in the
single room view and in the grid view. So this logic is extracted here,
and this observable updates with the right view model as the status for
a room changes.
To not have to track the subscription manually in the SessionViewModel and
the RoomGridViewModel, all subscriptions are removed in the dispose method.
Only when transferring a RoomViewModelObservable between the SessionViewModel
and RoomGridViewModel, unsubscribeAll should be called prior to doing
the transfer, so either parent view model don't keep getting updates for
the now transferred child view model.
This is also why there is an explicit initialize method, see comment there.
*/
export class RoomViewModelObservable extends ObservableValue {
constructor(sessionViewModel, roomIdOrLocalId) {
super(null);
this._statusSubscription = null;
this._sessionViewModel = sessionViewModel;
this.id = roomIdOrLocalId;
}
/**
Separate initialize method rather than doing this onSubscribeFirst because
we don't want to run this again when transferring this value between
SessionViewModel and RoomGridViewModel, as onUnsubscribeLast and onSubscribeFirst
are called in that case.
*/
async initialize() {
const {session} = this._sessionViewModel._client;
const statusObservable = await session.observeRoomStatus(this.id);
this.set(await this._statusToViewModel(statusObservable.get()));
this._statusSubscription = statusObservable.subscribe(async status => {
// first dispose existing VM, if any
this.get()?.dispose();
this.set(await this._statusToViewModel(status));
});
}
async _statusToViewModel(status) {
if (status & RoomStatus.Replaced) {
if (status & RoomStatus.BeingCreated) {
const {session} = this._sessionViewModel._client;
const roomBeingCreated = session.roomsBeingCreated.get(this.id);
this._sessionViewModel.notifyRoomReplaced(roomBeingCreated.id, roomBeingCreated.roomId);
} else {
throw new Error("Don't know how to replace a room with this status: " + (status ^ RoomStatus.Replaced));
}
} else if (status & RoomStatus.BeingCreated) {
return this._sessionViewModel._createRoomBeingCreatedViewModel(this.id);
} else if (status & RoomStatus.Invited) {
return this._sessionViewModel._createInviteViewModel(this.id);
} else if (status & RoomStatus.Joined) {
return this._sessionViewModel._createRoomViewModelInstance(this.id);
} else if (status & RoomStatus.Archived) {
return await this._sessionViewModel._createArchivedRoomViewModel(this.id);
} else {
return this._sessionViewModel._createUnknownRoomViewModel(this.id);
}
}
dispose() {
if (this._statusSubscription) {
this._statusSubscription = this._statusSubscription();
}
this.unsubscribeAll();
this.get()?.dispose();
}
}

View file

@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "../ViewModel.js";
import {createEnum} from "../../utils/enum.js";
import {ConnectionStatus} from "../../matrix/net/Reconnector.js";
import {ViewModel} from "../ViewModel";
import {createEnum} from "../../utils/enum";
import {ConnectionStatus} from "../../matrix/net/Reconnector";
import {SyncStatus} from "../../matrix/Sync.js";
const SessionStatus = createEnum(
@ -36,7 +36,7 @@ export class SessionStatusViewModel extends ViewModel {
this._reconnector = reconnector;
this._status = this._calculateState(reconnector.connectionStatus.get(), sync.status.get());
this._session = session;
this._setupSessionBackupUrl = this.urlCreator.urlForSegment("settings");
this._setupKeyBackupUrl = this.urlCreator.urlForSegment("settings");
this._dismissSecretStorage = false;
}
@ -44,17 +44,17 @@ export class SessionStatusViewModel extends ViewModel {
const update = () => this._updateStatus();
this.track(this._sync.status.subscribe(update));
this.track(this._reconnector.connectionStatus.subscribe(update));
this.track(this._session.needsSessionBackup.subscribe(() => {
this.track(this._session.needsKeyBackup.subscribe(() => {
this.emitChange();
}));
}
get setupSessionBackupUrl () {
return this._setupSessionBackupUrl;
get setupKeyBackupUrl () {
return this._setupKeyBackupUrl;
}
get isShown() {
return (this._session.needsSessionBackup.get() && !this._dismissSecretStorage) || this._status !== SessionStatus.Syncing;
return (this._session.needsKeyBackup.get() && !this._dismissSecretStorage) || this._status !== SessionStatus.Syncing;
}
get statusLabel() {
@ -70,7 +70,7 @@ export class SessionStatusViewModel extends ViewModel {
case SessionStatus.SyncError:
return this.i18n`Sync failed because of ${this._sync.error}`;
}
if (this._session.needsSessionBackup.get()) {
if (this._session.needsKeyBackup.get()) {
return this.i18n`Set up session backup to decrypt older messages.`;
}
return "";
@ -135,7 +135,7 @@ export class SessionStatusViewModel extends ViewModel {
get isSecretStorageShown() {
// TODO: we need a model here where we can have multiple messages queued up and their buttons don't bleed into each other.
return this._status === SessionStatus.Syncing && this._session.needsSessionBackup.get() && !this._dismissSecretStorage;
return this._status === SessionStatus.Syncing && this._session.needsKeyBackup.get() && !this._dismissSecretStorage;
}
get canDismiss() {

View file

@ -17,28 +17,33 @@ limitations under the License.
import {LeftPanelViewModel} from "./leftpanel/LeftPanelViewModel.js";
import {RoomViewModel} from "./room/RoomViewModel.js";
import {UnknownRoomViewModel} from "./room/UnknownRoomViewModel.js";
import {InviteViewModel} from "./room/InviteViewModel.js";
import {RoomBeingCreatedViewModel} from "./room/RoomBeingCreatedViewModel.js";
import {LightboxViewModel} from "./room/LightboxViewModel.js";
import {SessionStatusViewModel} from "./SessionStatusViewModel.js";
import {RoomGridViewModel} from "./RoomGridViewModel.js";
import {SettingsViewModel} from "./settings/SettingsViewModel.js";
import {ViewModel} from "../ViewModel.js";
import {CreateRoomViewModel} from "./CreateRoomViewModel.js";
import {ViewModel} from "../ViewModel";
import {RoomViewModelObservable} from "./RoomViewModelObservable.js";
import {RightPanelViewModel} from "./rightpanel/RightPanelViewModel.js";
export class SessionViewModel extends ViewModel {
constructor(options) {
super(options);
const {sessionContainer} = options;
this._sessionContainer = this.track(sessionContainer);
const {client} = options;
this._client = this.track(client);
this._sessionStatusViewModel = this.track(new SessionStatusViewModel(this.childOptions({
sync: sessionContainer.sync,
reconnector: sessionContainer.reconnector,
session: sessionContainer.session,
})));
this._leftPanelViewModel = this.track(new LeftPanelViewModel(this.childOptions({
rooms: this._sessionContainer.session.rooms
sync: client.sync,
reconnector: client.reconnector,
session: client.session,
})));
this._leftPanelViewModel = this.track(new LeftPanelViewModel(this.childOptions({session: this._client.session})));
this._settingsViewModel = null;
this._currentRoomViewModel = null;
this._roomViewModelObservable = null;
this._gridViewModel = null;
this._createRoomViewModel = null;
this._setupNavigation();
}
@ -58,6 +63,7 @@ export class SessionViewModel extends ViewModel {
if (!this._gridViewModel) {
this._updateRoom(roomId);
}
this._updateRightPanel();
}));
if (!this._gridViewModel) {
this._updateRoom(currentRoomId.get());
@ -69,30 +75,34 @@ export class SessionViewModel extends ViewModel {
}));
this._updateSettings(settings.get());
const createRoom = this.navigation.observe("create-room");
this.track(createRoom.subscribe(createRoomOpen => {
this._updateCreateRoom(createRoomOpen);
}));
this._updateCreateRoom(createRoom.get());
const lightbox = this.navigation.observe("lightbox");
this.track(lightbox.subscribe(eventId => {
this._updateLightbox(eventId);
}));
this._updateLightbox(lightbox.get());
const rightpanel = this.navigation.observe("right-panel");
this.track(rightpanel.subscribe(() => this._updateRightPanel()));
this._updateRightPanel();
}
get id() {
return this._sessionContainer.sessionId;
return this._client.sessionId;
}
start() {
this._sessionStatusViewModel.start();
}
get activeSection() {
if (this._currentRoomViewModel) {
return this._currentRoomViewModel.id;
} else if (this._gridViewModel) {
return "roomgrid";
} else if (this._settingsViewModel) {
return "settings";
}
return "placeholder";
get activeMiddleViewModel() {
return this._roomViewModelObservable?.get() || this._gridViewModel || this._settingsViewModel || this._createRoomViewModel;
}
get roomGridViewModel() {
@ -111,12 +121,16 @@ export class SessionViewModel extends ViewModel {
return this._settingsViewModel;
}
get roomList() {
return this._roomList;
get currentRoomViewModel() {
return this._roomViewModelObservable?.get();
}
get currentRoomViewModel() {
return this._currentRoomViewModel;
get rightPanelViewModel() {
return this._rightPanelViewModel;
}
get createRoomViewModel() {
return this._createRoomViewModel;
}
_updateGrid(roomIds) {
@ -127,66 +141,107 @@ export class SessionViewModel extends ViewModel {
this._gridViewModel = this.track(new RoomGridViewModel(this.childOptions({
width: 3,
height: 2,
createRoomViewModel: roomId => this._createRoomViewModel(roomId),
createRoomViewModelObservable: roomId => new RoomViewModelObservable(this, roomId),
})));
if (this._gridViewModel.initializeRoomIdsAndTransferVM(roomIds, this._currentRoomViewModel)) {
this._currentRoomViewModel = this.untrack(this._currentRoomViewModel);
} else if (this._currentRoomViewModel) {
this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
// try to transfer the current room view model, so we don't have to reload the timeline
this._roomViewModelObservable?.unsubscribeAll();
if (this._gridViewModel.initializeRoomIdsAndTransferVM(roomIds, this._roomViewModelObservable)) {
this._roomViewModelObservable = this.untrack(this._roomViewModelObservable);
} else if (this._roomViewModelObservable) {
this._roomViewModelObservable = this.disposeTracked(this._roomViewModelObservable);
}
} else {
this._gridViewModel.setRoomIds(roomIds);
}
} else if (this._gridViewModel && !roomIds) {
// closing grid, try to show focused room in grid
if (currentRoomId) {
const vm = this._gridViewModel.releaseRoomViewModel(currentRoomId.value);
if (vm) {
this._currentRoomViewModel = this.track(vm);
} else {
const newVM = this._createRoomViewModel(currentRoomId.value);
if (newVM) {
this._currentRoomViewModel = this.track(newVM);
}
const vmo = this._gridViewModel.releaseRoomViewModel(currentRoomId.value);
if (vmo) {
this._roomViewModelObservable = this.track(vmo);
this._roomViewModelObservable.subscribe(() => {
this.emitChange("activeMiddleViewModel");
});
}
}
this._gridViewModel = this.disposeTracked(this._gridViewModel);
}
if (changed) {
this.emitChange("activeSection");
this.emitChange("activeMiddleViewModel");
}
}
_createRoomViewModel(roomId) {
const room = this._sessionContainer.session.rooms.get(roomId);
if (!room) {
return null;
_createRoomViewModelInstance(roomId) {
const room = this._client.session.rooms.get(roomId);
if (room) {
const roomVM = new RoomViewModel(this.childOptions({room}));
roomVM.load();
return roomVM;
}
const roomVM = new RoomViewModel(this.childOptions({
room,
ownUserId: this._sessionContainer.session.user.id,
return null;
}
_createUnknownRoomViewModel(roomIdOrAlias) {
return new UnknownRoomViewModel(this.childOptions({
roomIdOrAlias,
session: this._client.session,
}));
roomVM.load();
return roomVM;
}
async _createArchivedRoomViewModel(roomId) {
const room = await this._client.session.loadArchivedRoom(roomId);
if (room) {
const roomVM = new RoomViewModel(this.childOptions({room}));
roomVM.load();
return roomVM;
}
return null;
}
_createInviteViewModel(roomId) {
const invite = this._client.session.invites.get(roomId);
if (invite) {
return new InviteViewModel(this.childOptions({
invite,
mediaRepository: this._client.session.mediaRepository,
}));
}
return null;
}
_createRoomBeingCreatedViewModel(localId) {
const roomBeingCreated = this._client.session.roomsBeingCreated.get(localId);
if (roomBeingCreated) {
return new RoomBeingCreatedViewModel(this.childOptions({
roomBeingCreated,
mediaRepository: this._client.session.mediaRepository,
}));
}
return null;
}
_updateRoom(roomId) {
// opening a room and already open?
if (this._roomViewModelObservable?.id === roomId) {
return;
}
// close if needed
if (this._roomViewModelObservable) {
this._roomViewModelObservable = this.disposeTracked(this._roomViewModelObservable);
}
if (!roomId) {
if (this._currentRoomViewModel) {
this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
this.emitChange("currentRoom");
}
// if clearing the activeMiddleViewModel rather than changing to a different one,
// emit so the view picks it up and show the placeholder
this.emitChange("activeMiddleViewModel");
return;
}
// already open?
if (this._currentRoomViewModel?.id === roomId) {
return;
}
this._currentRoomViewModel = this.disposeTracked(this._currentRoomViewModel);
const roomVM = this._createRoomViewModel(roomId);
if (roomVM) {
this._currentRoomViewModel = this.track(roomVM);
}
this.emitChange("currentRoom");
const vmo = new RoomViewModelObservable(this, roomId);
this._roomViewModelObservable = this.track(vmo);
// subscription is unsubscribed in RoomViewModelObservable.dispose, and thus handled by track
this._roomViewModelObservable.subscribe(() => {
this.emitChange("activeMiddleViewModel");
});
vmo.initialize();
}
_updateSettings(settingsOpen) {
@ -195,11 +250,21 @@ export class SessionViewModel extends ViewModel {
}
if (settingsOpen) {
this._settingsViewModel = this.track(new SettingsViewModel(this.childOptions({
session: this._sessionContainer.session,
client: this._client,
})));
this._settingsViewModel.load();
}
this.emitChange("activeSection");
this.emitChange("activeMiddleViewModel");
}
_updateCreateRoom(createRoomOpen) {
if (this._createRoomViewModel) {
this._createRoomViewModel = this.disposeTracked(this._createRoomViewModel);
}
if (createRoomOpen) {
this._createRoomViewModel = this.track(new CreateRoomViewModel(this.childOptions({session: this._client.session})));
}
this.emitChange("activeMiddleViewModel");
}
_updateLightbox(eventId) {
@ -207,8 +272,7 @@ export class SessionViewModel extends ViewModel {
this._lightboxViewModel = this.disposeTracked(this._lightboxViewModel);
}
if (eventId) {
const roomId = this.navigation.path.get("room").value;
const room = this._sessionContainer.session.rooms.get(roomId);
const room = this._roomFromNavigation();
this._lightboxViewModel = this.track(new LightboxViewModel(this.childOptions({eventId, room})));
}
this.emitChange("lightboxViewModel");
@ -217,4 +281,24 @@ export class SessionViewModel extends ViewModel {
get lightboxViewModel() {
return this._lightboxViewModel;
}
_roomFromNavigation() {
const roomId = this.navigation.path.get("room")?.value;
const room = this._client.session.rooms.get(roomId);
return room;
}
_updateRightPanel() {
this._rightPanelViewModel = this.disposeTracked(this._rightPanelViewModel);
const enable = !!this.navigation.path.get("right-panel")?.value;
if (enable) {
const room = this._roomFromNavigation();
this._rightPanelViewModel = this.track(new RightPanelViewModel(this.childOptions({room, session: this._client.session})));
}
this.emitChange("rightPanelViewModel");
}
notifyRoomReplaced(oldId, newId) {
this.navigation.push("room", newId);
}
}

View file

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export class ReplayDetectionEntry {
constructor(sessionId, messageIndex, event) {
this.sessionId = sessionId;
this.messageIndex = messageIndex;
this.eventId = event.event_id;
this.timestamp = event.origin_server_ts;
}
export function imageToInfo(image) {
return {
w: image.width,
h: image.height,
mimetype: image.blob.mimeType,
size: image.blob.size
};
}

View file

@ -0,0 +1,82 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {avatarInitials, getIdentifierColorNumber, getAvatarHttpUrl} from "../../avatar";
import {ViewModel} from "../../ViewModel";
const KIND_ORDER = ["roomBeingCreated", "invite", "room"];
export class BaseTileViewModel extends ViewModel {
constructor(options) {
super(options);
this._isOpen = false;
this._hidden = false;
}
get hidden() {
return this._hidden;
}
set hidden(value) {
if (value !== this._hidden) {
this._hidden = value;
this.emitChange("hidden");
}
}
close() {
if (this._isOpen) {
this._isOpen = false;
this.emitChange("isOpen");
}
}
open() {
if (!this._isOpen) {
this._isOpen = true;
this.emitChange("isOpen");
}
}
get isOpen() {
return this._isOpen;
}
compare(other) {
if (other.kind !== this.kind) {
return KIND_ORDER.indexOf(this.kind) - KIND_ORDER.indexOf(other.kind);
}
return 0;
}
// Avatar view model contract
get avatarLetter() {
return avatarInitials(this.name);
}
get avatarColorNumber() {
return getIdentifierColorNumber(this._avatarSource.avatarColorId);
}
avatarUrl(size) {
return getAvatarHttpUrl(this._avatarSource.avatarUrl, size, this.platform, this._avatarSource.mediaRepository);
}
get avatarTitle() {
return this.name;
}
}

View file

@ -0,0 +1,64 @@
/*
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {BaseTileViewModel} from "./BaseTileViewModel.js";
import {comparePrimitive} from "./common";
export class InviteTileViewModel extends BaseTileViewModel {
constructor(options) {
super(options);
const {invite} = options;
this._invite = invite;
this._url = this.urlCreator.openRoomActionUrl(this._invite.id);
}
get busy() { return this._invite.accepting || this._invite.rejecting; }
get kind() { return "invite"; }
get url() { return this._url; }
get name() { return this._invite.name; }
get isHighlighted() { return true; }
get isUnread() { return true; }
get badgeCount() { return this.i18n`!`; }
get _avatarSource() { return this._invite; }
/** very important that sorting order is stable and that comparing
* to itself always returns 0, otherwise SortedMapList will
* remove the wrong children, etc ... */
compare(other) {
const parentComparison = super.compare(other);
if (parentComparison !== 0) {
return parentComparison;
}
const timeDiff = other._invite.timestamp - this._invite.timestamp;
if (timeDiff !== 0) {
return timeDiff;
}
return comparePrimitive(this._invite.id, other._invite.id);
}
}
export function tests() {
return {
"test compare with timestamp": assert => {
const urlCreator = {openRoomActionUrl() { return "";}}
const vm1 = new InviteTileViewModel({invite: {timestamp: 500, id: "1"}, urlCreator});
const vm2 = new InviteTileViewModel({invite: {timestamp: 250, id: "2"}, urlCreator});
assert(vm1.compare(vm2) < 0);
assert(vm2.compare(vm1) > 0);
assert.equal(vm1.compare(vm1), 0);
},
}
}

View file

@ -15,37 +15,55 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {ViewModel} from "../../ViewModel.js";
import {ViewModel} from "../../ViewModel";
import {RoomTileViewModel} from "./RoomTileViewModel.js";
import {InviteTileViewModel} from "./InviteTileViewModel.js";
import {RoomBeingCreatedTileViewModel} from "./RoomBeingCreatedTileViewModel.js";
import {RoomFilter} from "./RoomFilter.js";
import {ApplyMap} from "../../../observable/map/ApplyMap.js";
import {addPanelIfNeeded} from "../../navigation/index";
export class LeftPanelViewModel extends ViewModel {
constructor(options) {
super(options);
const {rooms} = options;
this._roomTileViewModels = rooms.mapValues((room, emitChange) => {
const isOpen = this.navigation.path.get("room")?.value === room.id;
const vm = new RoomTileViewModel(this.childOptions({
isOpen,
room,
emitChange
}));
// need to also update the current vm here as
// we can't call `_open` from the ctor as the map
// is only populated when the view subscribes.
if (isOpen) {
this._currentTileVM?.close();
this._currentTileVM = vm;
}
return vm;
});
this._roomListFilterMap = new ApplyMap(this._roomTileViewModels);
this._roomList = this._roomListFilterMap.sortValues((a, b) => a.compare(b));
const {session} = options;
this._tileViewModelsMap = this._mapTileViewModels(session.roomsBeingCreated, session.invites, session.rooms);
this._tileViewModelsFilterMap = new ApplyMap(this._tileViewModelsMap);
this._tileViewModels = this._tileViewModelsFilterMap.sortValues((a, b) => a.compare(b));
this._currentTileVM = null;
this._setupNavigation();
this._closeUrl = this.urlCreator.urlForSegment("session");
this._settingsUrl = this.urlCreator.urlForSegment("settings");
this._createRoomUrl = this.urlCreator.urlForSegment("create-room");
}
_mapTileViewModels(roomsBeingCreated, invites, rooms) {
// join is not commutative, invites will take precedence over rooms
const allTiles = invites.join(roomsBeingCreated, rooms).mapValues((item, emitChange) => {
let vm;
if (item.isBeingCreated) {
vm = new RoomBeingCreatedTileViewModel(this.childOptions({roomBeingCreated: item, emitChange}));
} else if (item.isInvite) {
vm = new InviteTileViewModel(this.childOptions({invite: item, emitChange}));
} else {
vm = new RoomTileViewModel(this.childOptions({room: item, emitChange}));
}
const isOpen = this.navigation.path.get("room")?.value === item.id;
if (isOpen) {
vm.open();
this._updateCurrentVM(vm);
}
return vm;
});
return allTiles;
}
_updateCurrentVM(vm) {
// need to also update the current vm here as
// we can't call `_open` from the ctor as the map
// is only populated when the view subscribes.
this._currentTileVM?.close();
this._currentTileVM = vm;
}
get closeUrl() {
@ -56,6 +74,8 @@ export class LeftPanelViewModel extends ViewModel {
return this._settingsUrl;
}
get createRoomUrl() { return this._createRoomUrl; }
_setupNavigation() {
const roomObservable = this.navigation.observe("room");
this.track(roomObservable.subscribe(roomId => this._open(roomId)));
@ -75,51 +95,53 @@ export class LeftPanelViewModel extends ViewModel {
this._currentTileVM?.close();
this._currentTileVM = null;
if (roomId) {
this._currentTileVM = this._roomTileViewModels.get(roomId);
this._currentTileVM = this._tileViewModelsMap.get(roomId);
this._currentTileVM?.open();
}
}
toggleGrid() {
const room = this.navigation.path.get("room");
let path = this.navigation.path.until("session");
if (this.gridEnabled) {
let path = this.navigation.path.until("session");
const room = this.navigation.path.get("room");
if (room) {
path = path.with(room);
path = addPanelIfNeeded(this.navigation, path);
}
this.navigation.applyPath(path);
} else {
let path = this.navigation.path.until("session");
const room = this.navigation.path.get("room");
if (room) {
path = path.with(this.navigation.segment("rooms", [room.value]));
path = path.with(room);
path = addPanelIfNeeded(this.navigation, path);
} else {
path = path.with(this.navigation.segment("rooms", []));
path = path.with(this.navigation.segment("empty-grid-tile", 0));
}
this.navigation.applyPath(path);
}
this.navigation.applyPath(path);
}
get roomList() {
return this._roomList;
get tileViewModels() {
return this._tileViewModels;
}
clearFilter() {
this._roomListFilterMap.setApply(null);
this._roomListFilterMap.applyOnce((roomId, vm) => vm.hidden = false);
this._tileViewModelsFilterMap.setApply(null);
this._tileViewModelsFilterMap.applyOnce((roomId, vm) => vm.hidden = false);
}
setFilter(query) {
query = query.trim();
if (query.length === 0) {
this.clearFilter();
return false;
} else {
const startFiltering = !this._tileViewModelsFilterMap.hasApply();
const filter = new RoomFilter(query);
this._roomListFilterMap.setApply((roomId, vm) => {
this._tileViewModelsFilterMap.setApply((roomId, vm) => {
vm.hidden = !filter.matches(vm);
});
return startFiltering;
}
}
}

View file

@ -0,0 +1,70 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {BaseTileViewModel} from "./BaseTileViewModel.js";
import {comparePrimitive} from "./common";
export class RoomBeingCreatedTileViewModel extends BaseTileViewModel {
constructor(options) {
super(options);
const {roomBeingCreated} = options;
this._roomBeingCreated = roomBeingCreated;
this._url = this.urlCreator.openRoomActionUrl(this._roomBeingCreated.id);
}
get busy() { return !this._roomBeingCreated.error; }
get kind() { return "roomBeingCreated"; }
get isHighlighted() { return !this.busy; }
get badgeCount() { return !this.busy && this.i18n`Failed`; }
get url() { return this._url; }
get name() { return this._roomBeingCreated.name; }
get _avatarSource() { return this._roomBeingCreated; }
/** very important that sorting order is stable and that comparing
* to itself always returns 0, otherwise SortedMapList will
* remove the wrong children, etc ... */
compare(other) {
const parentCmp = super.compare(other);
if (parentCmp !== 0) {
return parentCmp;
}
const nameCmp = comparePrimitive(this.name, other.name);
if (nameCmp === 0) {
return comparePrimitive(this._roomBeingCreated.id, other._roomBeingCreated.id);
} else {
return nameCmp;
}
}
avatarUrl(size) {
// allow blob url which doesn't need mxc => http resolution
return this._roomBeingCreated.avatarBlobUrl ?? super.avatarUrl(size);
}
}
export function tests() {
return {
"test compare with names": assert => {
const urlCreator = {openRoomActionUrl() { return "";}}
const vm1 = new RoomBeingCreatedTileViewModel({roomBeingCreated: {name: "A", id: "1"}, urlCreator});
const vm2 = new RoomBeingCreatedTileViewModel({roomBeingCreated: {name: "B", id: "2"}, urlCreator});
assert(vm1.compare(vm2) < 0);
assert(vm2.compare(vm1) > 0);
assert.equal(vm1.compare(vm1), 0);
},
}
}

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