mirror of
https://github.com/element-hq/element-web.git
synced 2025-12-05 01:10:40 +00:00
Compare commits
1735 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25d8a7999f | ||
|
|
014f7ade86 | ||
|
|
2b671fcafc | ||
|
|
df5b243e75 | ||
|
|
5e43f2752a | ||
|
|
0cb14ba5ec | ||
|
|
d65477891e | ||
|
|
bb1854814c | ||
|
|
9e15bda803 | ||
|
|
e7ac66ad04 | ||
|
|
4a0a911f3b | ||
|
|
7286aeb013 | ||
|
|
244265d52b | ||
|
|
ab93e96a1f | ||
|
|
5a17d8b450 | ||
|
|
43e3662e60 | ||
|
|
19d5063814 | ||
|
|
da7a556629 | ||
|
|
c2cb4dff4c | ||
|
|
dd6868c255 | ||
|
|
b411a2e489 | ||
|
|
28343aaa33 | ||
|
|
3f615c6664 | ||
|
|
804b17fbf1 | ||
|
|
9798e14733 | ||
|
|
b742342062 | ||
|
|
f8fa19ed47 | ||
|
|
fe4bb3e413 | ||
|
|
ff13782ab5 | ||
|
|
b1dd7efed8 | ||
|
|
699a58e0b3 | ||
|
|
6a19655886 | ||
|
|
afc889ff4d | ||
|
|
b06c9f037e | ||
|
|
1c812b340d | ||
|
|
bd9f5d3e06 | ||
|
|
cf8164bcc3 | ||
|
|
33612b7076 | ||
|
|
5a97786cc6 | ||
|
|
ee2329d236 | ||
|
|
66f80b2239 | ||
|
|
62ebeb9fd8 | ||
|
|
72ba708bfe | ||
|
|
d0d7f2d2d2 | ||
|
|
b3459be707 | ||
|
|
d1e22d50f0 | ||
|
|
bf46c3cfab | ||
|
|
7880734d77 | ||
|
|
5ce1aba493 | ||
|
|
c929076a83 | ||
|
|
99625067fe | ||
|
|
c9ab977d73 | ||
|
|
b99e7598f9 | ||
|
|
b9e06bcf66 | ||
|
|
eb108c7866 | ||
|
|
a190862ed3 | ||
|
|
34a31a71fd | ||
|
|
7774756ed1 | ||
|
|
ee9f78d156 | ||
|
|
70754db27a | ||
|
|
16f8143f3e | ||
|
|
be1f014294 | ||
|
|
f53f9af1c5 | ||
|
|
58b1d62976 | ||
|
|
8e0f7f18a0 | ||
|
|
75ea62f351 | ||
|
|
ee8572559f | ||
|
|
f640be90ad | ||
|
|
472acd4792 | ||
|
|
8087e99808 | ||
|
|
51b74251f9 | ||
|
|
8c7aede0cc | ||
|
|
2829d95705 | ||
|
|
28057fd086 | ||
|
|
2e3ad3206c | ||
|
|
4f94cf5dfb | ||
|
|
51e8c28ab6 | ||
|
|
0f73228d55 | ||
|
|
d21e4fb86a | ||
|
|
645db7fa2f | ||
|
|
0d4f35bed1 | ||
|
|
2123fec8ed | ||
|
|
3860488bb5 | ||
|
|
ef5ea46830 | ||
|
|
d4faacf462 | ||
|
|
4a7d2901ac | ||
|
|
f382946138 | ||
|
|
bb93a59cfb | ||
|
|
0ad84fd7b0 | ||
|
|
87bddcd8ce | ||
|
|
18701a2dae | ||
|
|
2f65064688 | ||
|
|
2f08f2441f | ||
|
|
6c4c0bf57a | ||
|
|
55a10ee275 | ||
|
|
49545ce0c2 | ||
|
|
b87058508e | ||
|
|
fd53f10fbd | ||
|
|
6329e274ab | ||
|
|
b5fdaac947 | ||
|
|
22bf74dc65 | ||
|
|
d376df478a | ||
|
|
53f4da1d30 | ||
|
|
e34db7b8c6 | ||
|
|
074bbc7149 | ||
|
|
4b33164ab6 | ||
|
|
dab707a893 | ||
|
|
5cf164fcc1 | ||
|
|
37a7ce809a | ||
|
|
15f9f5dbe8 | ||
|
|
187818aaa0 | ||
|
|
30b1e7078f | ||
|
|
791980cd1f | ||
|
|
6c19504c8b | ||
|
|
497ebce88a | ||
|
|
2768cd2010 | ||
|
|
204e42494a | ||
|
|
72b4a86eed | ||
|
|
ce5311191f | ||
|
|
7eff6d968e | ||
|
|
d01e2506f5 | ||
|
|
53fe372a0c | ||
|
|
4cb04d1e40 | ||
|
|
24e021b91f | ||
|
|
e988f5ca3b | ||
|
|
633a3f4867 | ||
|
|
df163d8cb7 | ||
|
|
73bb317925 | ||
|
|
ad74d264a3 | ||
|
|
e9db975d7d | ||
|
|
0958a6bb62 | ||
|
|
f3586a79c2 | ||
|
|
882d09bf85 | ||
|
|
25eb2e2daf | ||
|
|
d8d9912f2d | ||
|
|
8a28da1986 | ||
|
|
384425582a | ||
|
|
b87b356722 | ||
|
|
6ae0dda9d3 | ||
|
|
6c680ff424 | ||
|
|
c6b455f470 | ||
|
|
f6d7052928 | ||
|
|
2ee1a9c440 | ||
|
|
8fd12d530d | ||
|
|
3f7bd48c0a | ||
|
|
28108476bd | ||
|
|
722cedc92e | ||
|
|
f182e32e3d | ||
|
|
3cfcc13387 | ||
|
|
953bb64e0b | ||
|
|
54f4443428 | ||
|
|
a039450d10 | ||
|
|
1e963a6c3a | ||
|
|
80ad45df06 | ||
|
|
348f133e77 | ||
|
|
cd865bbe8f | ||
|
|
e668d7685d | ||
|
|
6607bee91a | ||
|
|
3a89a5af0b | ||
|
|
35e3621ae7 | ||
|
|
040ef73886 | ||
|
|
74c9ac0872 | ||
|
|
adbad509f4 | ||
|
|
15a97a653f | ||
|
|
9fb97a6b10 | ||
|
|
579f210cfc | ||
|
|
2fb1e156ed | ||
|
|
707ddc61bf | ||
|
|
09e861637f | ||
|
|
1f43f904d5 | ||
|
|
446f74b3dd | ||
|
|
d2391f999e | ||
|
|
e1779ca8bc | ||
|
|
65371c9a39 | ||
|
|
a4dc844338 | ||
|
|
0a4af647c8 | ||
|
|
503bf541c7 | ||
|
|
1fbc249de5 | ||
|
|
959dcd0c49 | ||
|
|
ef4f2f10d9 | ||
|
|
19dbd85d44 | ||
|
|
ef4b604caf | ||
|
|
74f459f8a4 | ||
|
|
fef81748bb | ||
|
|
3e53879adc | ||
|
|
145e61b00f | ||
|
|
49dd93ffab | ||
|
|
7174879ac9 | ||
|
|
486adb717b | ||
|
|
773f592c3f | ||
|
|
2ca9d87b95 | ||
|
|
4bc4292ceb | ||
|
|
cbb72c2f29 | ||
|
|
5f477b313b | ||
|
|
d45e44d01c | ||
|
|
c12839dc7b | ||
|
|
c35c9f7c3a | ||
|
|
ff5c7072d7 | ||
|
|
eca453ee5a | ||
|
|
ad2541299f | ||
|
|
dc840fdf48 | ||
|
|
510bb5785e | ||
|
|
9556795611 | ||
|
|
4c6fa740f3 | ||
|
|
f4a3e9a39b | ||
|
|
bb820bebd1 | ||
|
|
664f809362 | ||
|
|
034c045b37 | ||
|
|
4dfb0e9a90 | ||
|
|
654429dbdb | ||
|
|
c7d0214aaa | ||
|
|
607923b58f | ||
|
|
895fb63d5d | ||
|
|
b172018d08 | ||
|
|
95a0bc92d6 | ||
|
|
a90492e393 | ||
|
|
ec7067e7bd | ||
|
|
42b7410a5d | ||
|
|
4cfcdfa040 | ||
|
|
795986f146 | ||
|
|
d38c338f89 | ||
|
|
a373849b5b | ||
|
|
8cd4637316 | ||
|
|
0635a6f562 | ||
|
|
a8cee87c08 | ||
|
|
a7598ea815 | ||
|
|
22cef7a6a0 | ||
|
|
e507339324 | ||
|
|
b643d8ff6a | ||
|
|
3547bd8d00 | ||
|
|
ab1f37b0bb | ||
|
|
eb37032d8a | ||
|
|
f6ed21559a | ||
|
|
a5986ade51 | ||
|
|
d7504aeda5 | ||
|
|
b2459b2dc6 | ||
|
|
819e06e2cd | ||
|
|
b7f1a3db57 | ||
|
|
a030e46c69 | ||
|
|
f61cfbc542 | ||
|
|
c4b7571c45 | ||
|
|
4dd477e064 | ||
|
|
f595f6f141 | ||
|
|
24602119c5 | ||
|
|
3040d0a474 | ||
|
|
e4ea00ca23 | ||
|
|
4fc311da90 | ||
|
|
7894e52529 | ||
|
|
7999a70cab | ||
|
|
f6aa9a7ea4 | ||
|
|
b3ae9cc9d4 | ||
|
|
5f29729e82 | ||
|
|
e24851456a | ||
|
|
1e40fd750f | ||
|
|
ed1554f4af | ||
|
|
c6cf5febd5 | ||
|
|
f9aaf7d903 | ||
|
|
a2e73cceee | ||
|
|
c672919d1e | ||
|
|
7ab2449ac1 | ||
|
|
e68a2b5e1d | ||
|
|
ddc4ac187c | ||
|
|
ded66bbdfc | ||
|
|
1a11c402fa | ||
|
|
4ec77eeca7 | ||
|
|
635fd927cd | ||
|
|
481a7b160d | ||
|
|
cadedd2919 | ||
|
|
e0bf23fa7c | ||
|
|
a53acb3b58 | ||
|
|
f6ec858ac9 | ||
|
|
d976046e6a | ||
|
|
1902b631c7 | ||
|
|
dda2129354 | ||
|
|
12157edd62 | ||
|
|
7e563b89c7 | ||
|
|
6a6118e136 | ||
|
|
bede9a814b | ||
|
|
d70842c3c7 | ||
|
|
a288c5b85d | ||
|
|
c4d408d095 | ||
|
|
ac24d6707f | ||
|
|
e5835d2731 | ||
|
|
d8c32db14b | ||
|
|
2b3606d44d | ||
|
|
5feb31911a | ||
|
|
9483e42508 | ||
|
|
e640f65640 | ||
|
|
8a9c85c97d | ||
|
|
bbae809012 | ||
|
|
bc166f19b7 | ||
|
|
8fb521c83c | ||
|
|
d9077584cd | ||
|
|
8467d5d760 | ||
|
|
d96e5a55e1 | ||
|
|
4f64f70a12 | ||
|
|
9cd8f7c7f3 | ||
|
|
c64f71a3cb | ||
|
|
af9838408b | ||
|
|
974a187e74 | ||
|
|
efe1c767f0 | ||
|
|
4bfefa9396 | ||
|
|
4cddda67d9 | ||
|
|
c2d4409241 | ||
|
|
ee88fe55c1 | ||
|
|
a72d0c5b7f | ||
|
|
3cb092051e | ||
|
|
b8018942fc | ||
|
|
3136eb0962 | ||
|
|
02f58ef9e3 | ||
|
|
b5f029d10e | ||
|
|
caff20cbb3 | ||
|
|
e71ca328e7 | ||
|
|
a0b460b084 | ||
|
|
abca28c80b | ||
|
|
5627089a2a | ||
|
|
da7909f1ce | ||
|
|
210cb31852 | ||
|
|
aeb438dc62 | ||
|
|
10a053019d | ||
|
|
58c431abc2 | ||
|
|
d512e25cca | ||
|
|
65d9333104 | ||
|
|
fbd974df55 | ||
|
|
fdf83a5ad5 | ||
|
|
c98e06e1aa | ||
|
|
b58265a69c | ||
|
|
37fbad0dbe | ||
|
|
756da03b9a | ||
|
|
48e082e124 | ||
|
|
c606912a8d | ||
|
|
7cd24e7dbd | ||
|
|
c7d717f0a4 | ||
|
|
cf3cdaccf3 | ||
|
|
d0d4760ddc | ||
|
|
51bc18aef0 | ||
|
|
26d12bebe4 | ||
|
|
90ae024a4e | ||
|
|
57c7d81f43 | ||
|
|
eab206c3bd | ||
|
|
72745b05dc | ||
|
|
f8d5101dbc | ||
|
|
cc1e30c963 | ||
|
|
121fe34180 | ||
|
|
5450223cc7 | ||
|
|
25b5c14527 | ||
|
|
6bc4c87ce4 | ||
|
|
0f0c3d0ca1 | ||
|
|
96c4a24d3d | ||
|
|
c6b501811f | ||
|
|
0996a0b140 | ||
|
|
8557a3b70e | ||
|
|
8b6cf1fc41 | ||
|
|
4eb762d52b | ||
|
|
4d221c6099 | ||
|
|
314bfbd541 | ||
|
|
5cdd234bf2 | ||
|
|
b6d5849bec | ||
|
|
035b15f330 | ||
|
|
77355cbeb4 | ||
|
|
ff5dff45f5 | ||
|
|
0deb52ac5e | ||
|
|
29ff9c11a8 | ||
|
|
cb3ae0e069 | ||
|
|
bf31d6d5fa | ||
|
|
181a6a61ff | ||
|
|
322af6513d | ||
|
|
69ce3c43cf | ||
|
|
438453e61a | ||
|
|
50f94eb040 | ||
|
|
5794c30def | ||
|
|
a512e600a7 | ||
|
|
429d110212 | ||
|
|
b5248c06a7 | ||
|
|
18bd1058d3 | ||
|
|
b18fcf7f9e | ||
|
|
05e963d1e2 | ||
|
|
5d9c8f3726 | ||
|
|
be55882f46 | ||
|
|
356a4a4392 | ||
|
|
34bdd40953 | ||
|
|
c5524851f3 | ||
|
|
cff1c3010b | ||
|
|
46572ae793 | ||
|
|
b1ba69fd00 | ||
|
|
8c619fedeb | ||
|
|
efd01d6929 | ||
|
|
a1b78f93fe | ||
|
|
cdc89c0623 | ||
|
|
d107151f8a | ||
|
|
48abc75665 | ||
|
|
41373f30f7 | ||
|
|
ad9d032f82 | ||
|
|
d7eb23db53 | ||
|
|
333f1e46ca | ||
|
|
d414127f80 | ||
|
|
ff2885087d | ||
|
|
a5258978d6 | ||
|
|
8c0a23dd8b | ||
|
|
d434ea55a8 | ||
|
|
4331fbf422 | ||
|
|
cf17ea6254 | ||
|
|
8247bb4a76 | ||
|
|
08a41bf093 | ||
|
|
d7157696f4 | ||
|
|
bf055688b7 | ||
|
|
28b9892486 | ||
|
|
512a9125bf | ||
|
|
00a92452e8 | ||
|
|
32576e97d5 | ||
|
|
20f93e761b | ||
|
|
bdf8f655fb | ||
|
|
8603dd4bb4 | ||
|
|
212a070a02 | ||
|
|
e15358f77e | ||
|
|
851b601d2c | ||
|
|
f52a1cf311 | ||
|
|
0ddb2cf183 | ||
|
|
cf0340c1c7 | ||
|
|
6c5b4a298b | ||
|
|
c5c5e6d811 | ||
|
|
2462ede539 | ||
|
|
b6e4c59877 | ||
|
|
0bc1624d4e | ||
|
|
f81f7db6cd | ||
|
|
9e95d2e4ac | ||
|
|
95a46ae201 | ||
|
|
8764b44325 | ||
|
|
090db5490b | ||
|
|
cfcb050822 | ||
|
|
66e36e9d40 | ||
|
|
4507117f89 | ||
|
|
cad48b62e4 | ||
|
|
5138dc9fd8 | ||
|
|
c12a77bc15 | ||
|
|
695b5d950c | ||
|
|
17b2d14ffc | ||
|
|
752f8363a7 | ||
|
|
a3a4ff569e | ||
|
|
948b862c31 | ||
|
|
27ab79fd38 | ||
|
|
8cbc8db1cb | ||
|
|
e32299a50c | ||
|
|
c843ee3157 | ||
|
|
490cbbd05f | ||
|
|
e4aa43944f | ||
|
|
827f891ae7 | ||
|
|
54c34ab8d5 | ||
|
|
3339fa2a39 | ||
|
|
d4a36f5081 | ||
|
|
19cb06d040 | ||
|
|
a5e0aa763b | ||
|
|
a1e3cf76e1 | ||
|
|
866bd55bc3 | ||
|
|
a6257a1148 | ||
|
|
92e187da64 | ||
|
|
f950b675bd | ||
|
|
aca4457a89 | ||
|
|
725b9a6619 | ||
|
|
d7a851a353 | ||
|
|
c6f95dfb3b | ||
|
|
4164688c24 | ||
|
|
cddfa62e27 | ||
|
|
a6e3b39f16 | ||
|
|
f8fb3a7fe1 | ||
|
|
50d18ac771 | ||
|
|
1e0c4d8797 | ||
|
|
05e479a76c | ||
|
|
fe9816abf5 | ||
|
|
29815b1d13 | ||
|
|
cb45db36c2 | ||
|
|
31915db6f6 | ||
|
|
eb01cb9cba | ||
|
|
2a8afd49fb | ||
|
|
9b85d88036 | ||
|
|
c2049e991b | ||
|
|
3224a4e49e | ||
|
|
cfc4b89225 | ||
|
|
8b1444c954 | ||
|
|
d4d2ef326e | ||
|
|
2a62fdb652 | ||
|
|
6215326f8e | ||
|
|
da7e9840f3 | ||
|
|
cc18458bec | ||
|
|
928915873b | ||
|
|
8fa059fc4b | ||
|
|
33b7e2046d | ||
|
|
e777db3fc4 | ||
|
|
a4d22ffe8c | ||
|
|
622a4b0e4c | ||
|
|
7433f50c8c | ||
|
|
9c82938b35 | ||
|
|
4e3985d446 | ||
|
|
d732927dba | ||
|
|
0343bd1022 | ||
|
|
b75cb3c6cb | ||
|
|
2586280f2c | ||
|
|
516647ad04 | ||
|
|
04fb8f9a81 | ||
|
|
52f259ccfa | ||
|
|
f89fe4fff8 | ||
|
|
adb6ab311c | ||
|
|
b03e6a8d73 | ||
|
|
2271ec7634 | ||
|
|
332354e5b6 | ||
|
|
71cb04c50a | ||
|
|
3b2d0a6c01 | ||
|
|
731d94eea4 | ||
|
|
c3b819b4da | ||
|
|
c4b4f5a44a | ||
|
|
21410354ab | ||
|
|
de50518282 | ||
|
|
025a026f45 | ||
|
|
49525f22c2 | ||
|
|
642c33e8d9 | ||
|
|
fa322e8488 | ||
|
|
874123bbfa | ||
|
|
b088651211 | ||
|
|
a69c63cc60 | ||
|
|
04b51f6f0f | ||
|
|
ebd5399626 | ||
|
|
5d63d2269e | ||
|
|
ef00bc6d56 | ||
|
|
8e25a4ca36 | ||
|
|
1ecedd2769 | ||
|
|
9c9e260410 | ||
|
|
3f9f3e9d92 | ||
|
|
ca89c09e73 | ||
|
|
10f6277de2 | ||
|
|
acb443537a | ||
|
|
55c7c08ac2 | ||
|
|
72393bf75f | ||
|
|
497cb8bc9c | ||
|
|
a063efe250 | ||
|
|
45c8500f28 | ||
|
|
b04ce6a376 | ||
|
|
f2fc4a88ae | ||
|
|
77b45e7231 | ||
|
|
484736bf57 | ||
|
|
e91a108738 | ||
|
|
2ca5529faa | ||
|
|
a35ab6bcef | ||
|
|
e33e9b5d4a | ||
|
|
3910877f8f | ||
|
|
f538a0d5de | ||
|
|
4e5b971d3a | ||
|
|
ffb1584e10 | ||
|
|
dbf3439e35 | ||
|
|
11ac6defed | ||
|
|
718c1cbe6f | ||
|
|
27222a54c7 | ||
|
|
3263076ea6 | ||
|
|
3c659dcf57 | ||
|
|
2cd3150c56 | ||
|
|
cdc1447804 | ||
|
|
11c9aff69a | ||
|
|
58959d014a | ||
|
|
1a9125a886 | ||
|
|
8e008572e5 | ||
|
|
18c6aa38e4 | ||
|
|
177fba360d | ||
|
|
ad9935739c | ||
|
|
a2193d61b0 | ||
|
|
0ca6efdfca | ||
|
|
f3df0d07f8 | ||
|
|
95a3354b90 | ||
|
|
82b498017d | ||
|
|
b929f80be8 | ||
|
|
05a30e7c68 | ||
|
|
3c75c43d37 | ||
|
|
369e357742 | ||
|
|
93f8fcbacc | ||
|
|
155728b136 | ||
|
|
98551c3f9a | ||
|
|
1732805f31 | ||
|
|
562a1d8836 | ||
|
|
bab9de1899 | ||
|
|
27791c06ee | ||
|
|
01bfb67b5b | ||
|
|
485343864c | ||
|
|
842584177f | ||
|
|
ab9dfd185c | ||
|
|
f035b8c50e | ||
|
|
15fec7f27c | ||
|
|
c0c7ae1596 | ||
|
|
6c5a00162c | ||
|
|
b307a6d64e | ||
|
|
4628cf82a7 | ||
|
|
5eed734325 | ||
|
|
f0a1f6d926 | ||
|
|
deb1aa468f | ||
|
|
c76b1e7479 | ||
|
|
4129c9bc24 | ||
|
|
e22d36fc08 | ||
|
|
043f6991a4 | ||
|
|
2b0f4d5db1 | ||
|
|
1c267c69ac | ||
|
|
2a7b24e969 | ||
|
|
d132c75263 | ||
|
|
b363eafbc8 | ||
|
|
2986b241c4 | ||
|
|
d1dc082489 | ||
|
|
f94272c539 | ||
|
|
3b18b9b54b | ||
|
|
3585437af9 | ||
|
|
6a36b022e4 | ||
|
|
35600f11ad | ||
|
|
75886c4143 | ||
|
|
cd853ab5b4 | ||
|
|
292af1e59c | ||
|
|
d3bc0feb83 | ||
|
|
19bfd829d0 | ||
|
|
759b318bb5 | ||
|
|
dd19d2aaee | ||
|
|
e0ecde6760 | ||
|
|
5e21059144 | ||
|
|
0a32874b39 | ||
|
|
302c63058b | ||
|
|
0ee1892a0b | ||
|
|
12afbcbc45 | ||
|
|
e11ae99a9f | ||
|
|
0752c3a6d5 | ||
|
|
87f3603047 | ||
|
|
ce45f0b1e6 | ||
|
|
a76167d175 | ||
|
|
3a23189435 | ||
|
|
f3064a2994 | ||
|
|
029cf754ee | ||
|
|
1a248c8e5c | ||
|
|
568f9bb19d | ||
|
|
b913e72735 | ||
|
|
4d4c6e06ec | ||
|
|
2d917910da | ||
|
|
0a70fe2bd1 | ||
|
|
a08d00c672 | ||
|
|
bd75234a2f | ||
|
|
6573ba8c20 | ||
|
|
f4a2b6ab7e | ||
|
|
6117e09a3f | ||
|
|
1fd60f1e44 | ||
|
|
d979511696 | ||
|
|
ac2e69ba28 | ||
|
|
48052415b7 | ||
|
|
e87e22a438 | ||
|
|
5b2f921190 | ||
|
|
b830f43371 | ||
|
|
6332e3908e | ||
|
|
82742f9f13 | ||
|
|
39eb0f9c8f | ||
|
|
888fde0f53 | ||
|
|
eb62456007 | ||
|
|
60d2d45255 | ||
|
|
6ecdb02e81 | ||
|
|
4d3918109a | ||
|
|
9a9db53e0a | ||
|
|
ad6a19a0df | ||
|
|
3e8c6a42d3 | ||
|
|
3b8e5073b5 | ||
|
|
a775086e81 | ||
|
|
d5c291ae62 | ||
|
|
d6f6f32c74 | ||
|
|
317e1fb9cd | ||
|
|
37e8a35967 | ||
|
|
6182c983ab | ||
|
|
ac4b221690 | ||
|
|
fdf5b0a4fc | ||
|
|
7c0fffa79b | ||
|
|
fef02f2fd1 | ||
|
|
bdcf683942 | ||
|
|
ae14210763 | ||
|
|
830160f074 | ||
|
|
0ef03c5ca4 | ||
|
|
5a1b0a1dab | ||
|
|
77621fe035 | ||
|
|
7e40d81a44 | ||
|
|
8792be4db2 | ||
|
|
1fef91b82c | ||
|
|
b2a1c89e83 | ||
|
|
40d2018c17 | ||
|
|
a61afa0f31 | ||
|
|
4e7c58e242 | ||
|
|
8771369937 | ||
|
|
394fb2373b | ||
|
|
eb8064f961 | ||
|
|
531aa153bb | ||
|
|
d7ffe70d44 | ||
|
|
17489ae350 | ||
|
|
762dd69f0a | ||
|
|
cb8b052dc0 | ||
|
|
1fcb4ba94f | ||
|
|
ea6ed81517 | ||
|
|
2dd2acd4e0 | ||
|
|
41ae851df4 | ||
|
|
c3469b5b51 | ||
|
|
7412fc7f97 | ||
|
|
d1f26e3911 | ||
|
|
c1aac1aaca | ||
|
|
f2fa9fe398 | ||
|
|
a65a15e9bb | ||
|
|
9ada5f7ddd | ||
|
|
17e540d372 | ||
|
|
db2d7fdbff | ||
|
|
927ac5fe64 | ||
|
|
378f4bb85c | ||
|
|
0577edb055 | ||
|
|
513c7e3b73 | ||
|
|
56718650b9 | ||
|
|
e3eef45684 | ||
|
|
68f846e129 | ||
|
|
ad434cb82e | ||
|
|
f2171c11f0 | ||
|
|
629883731e | ||
|
|
0475bcd9de | ||
|
|
1d5e661bd0 | ||
|
|
ac87830e4e | ||
|
|
7fc5ab3c6e | ||
|
|
c4cb37606b | ||
|
|
e5b7a47fee | ||
|
|
1c03c208e1 | ||
|
|
7232659195 | ||
|
|
10d3076d6b | ||
|
|
cae3ab410f | ||
|
|
2bada93fdc | ||
|
|
9fb8c9f67a | ||
|
|
b9080c770d | ||
|
|
977b223929 | ||
|
|
7f95362dd2 | ||
|
|
f706f75a6e | ||
|
|
fc3f356dc0 | ||
|
|
6d510db2db | ||
|
|
ee13dd7b6c | ||
|
|
f898986c73 | ||
|
|
6cd0aeb607 | ||
|
|
1e57aa8c78 | ||
|
|
93c4fc8785 | ||
|
|
8a156dcac8 | ||
|
|
d49f8721ce | ||
|
|
c02d9890c5 | ||
|
|
dfcc923c91 | ||
|
|
ccd518cc4c | ||
|
|
a369c862a0 | ||
|
|
1a8a4728cd | ||
|
|
d2635373f0 | ||
|
|
5b4457d33c | ||
|
|
36e5ac3d7f | ||
|
|
df111223fc | ||
|
|
c0f1ae0133 | ||
|
|
c96ce482bc | ||
|
|
eacadbff40 | ||
|
|
cb29a04674 | ||
|
|
f9a34d21c8 | ||
|
|
bb313d1f3c | ||
|
|
a1bdfaa8a2 | ||
|
|
1df4f2d556 | ||
|
|
b996022db2 | ||
|
|
bbf08d99cc | ||
|
|
17b8982c75 | ||
|
|
3abdcbf806 | ||
|
|
a78a693903 | ||
|
|
b9a0b82537 | ||
|
|
10e481a8d5 | ||
|
|
fa4bd09f0c | ||
|
|
8dd90980d8 | ||
|
|
e9db7b1dcc | ||
|
|
388eb1ff4c | ||
|
|
647c01fe2a | ||
|
|
0ab8466a3b | ||
|
|
94609db3a6 | ||
|
|
f9f85ec542 | ||
|
|
d3938220cf | ||
|
|
73d9ef54c3 | ||
|
|
755ea0dfb8 | ||
|
|
f681ce5cdb | ||
|
|
ceadfef942 | ||
|
|
f665848c5e | ||
|
|
031b048c07 | ||
|
|
d82c4c5ef3 | ||
|
|
ae02d8d30a | ||
|
|
85db381153 | ||
|
|
67aff6b9f2 | ||
|
|
50aa988a34 | ||
|
|
fdc94ccf98 | ||
|
|
cb1fabc578 | ||
|
|
59f419b310 | ||
|
|
dcea0dd601 | ||
|
|
dc1e2010a6 | ||
|
|
d7b87743f3 | ||
|
|
7cc1573f33 | ||
|
|
f7c4cca675 | ||
|
|
869c08a790 | ||
|
|
a7b9e54594 | ||
|
|
5f3a1cb6e1 | ||
|
|
63d0477223 | ||
|
|
1a90a2c426 | ||
|
|
856156ef5c | ||
|
|
c8ca1dd8d0 | ||
|
|
d945050de6 | ||
|
|
d3ecce2d2e | ||
|
|
a495aecdd8 | ||
|
|
c553258aff | ||
|
|
9c8984b308 | ||
|
|
c2061ed439 | ||
|
|
96cddc5ca8 | ||
|
|
dd08f53756 | ||
|
|
056017007a | ||
|
|
7cfca3ff4d | ||
|
|
c36d1df417 | ||
|
|
762281cd96 | ||
|
|
898cb399a3 | ||
|
|
3fb0c9883b | ||
|
|
f2fb1836df | ||
|
|
fea3fed460 | ||
|
|
4c82d86092 | ||
|
|
1c9d61d731 | ||
|
|
7b702c4594 | ||
|
|
d6bcf80431 | ||
|
|
e26efd475c | ||
|
|
9d15bf2847 | ||
|
|
8ecf70dda0 | ||
|
|
4b8d7a612a | ||
|
|
1d97a5f06b | ||
|
|
f8e5bbef0b | ||
|
|
b532fd046a | ||
|
|
a8e24802ab | ||
|
|
862ab0c115 | ||
|
|
2f905e13e1 | ||
|
|
39778330b5 | ||
|
|
1af1297afc | ||
|
|
da5c687320 | ||
|
|
25eeaaf1e5 | ||
|
|
ac8fcbb264 | ||
|
|
759e35003b | ||
|
|
97dd4e2e6b | ||
|
|
56b7b65920 | ||
|
|
782174069c | ||
|
|
db5d9e2f6e | ||
|
|
547f59b1bc | ||
|
|
2d2386ace5 | ||
|
|
c603c1e37f | ||
|
|
de144cf4cd | ||
|
|
8df62e3b02 | ||
|
|
939733b736 | ||
|
|
6db1151699 | ||
|
|
0210670e91 | ||
|
|
d837d02ac9 | ||
|
|
cba27a7488 | ||
|
|
349a88d640 | ||
|
|
95e71a531e | ||
|
|
d2c6e2195e | ||
|
|
a63bf7cb35 | ||
|
|
7ff5e42f3e | ||
|
|
2c12b9128b | ||
|
|
e21d435d84 | ||
|
|
a2b28b826c | ||
|
|
8d31f72f83 | ||
|
|
e304a1925d | ||
|
|
819b80d30f | ||
|
|
c281fe785a | ||
|
|
8083e7f118 | ||
|
|
5098c7d16f | ||
|
|
e07c03a7bb | ||
|
|
e7f6e09def | ||
|
|
075d2b508d | ||
|
|
e55e2bdd0d | ||
|
|
2a025201b1 | ||
|
|
f89dcacf07 | ||
|
|
99f47b8601 | ||
|
|
1896ab67d1 | ||
|
|
2bc3b665d2 | ||
|
|
8f5f71ec80 | ||
|
|
021056cfd1 | ||
|
|
6a4038daeb | ||
|
|
cadfbcbed3 | ||
|
|
6f646260aa | ||
|
|
badfdb5e3e | ||
|
|
2345624d31 | ||
|
|
ddc4f30bb6 | ||
|
|
4e7aa78ed7 | ||
|
|
e792ebb837 | ||
|
|
4409f07c2e | ||
|
|
b89bd35cad | ||
|
|
4614017b8f | ||
|
|
7636645efc | ||
|
|
bd906cbc69 | ||
|
|
cb30cbb09a | ||
|
|
2b37e5334a | ||
|
|
f947400131 | ||
|
|
fd4d7eba12 | ||
|
|
8a00e71139 | ||
|
|
83b3702769 | ||
|
|
450b2d4d67 | ||
|
|
cd040ae0dd | ||
|
|
9a64dc27fc | ||
|
|
af6bd53d38 | ||
|
|
01f0e61d6e | ||
|
|
fc02331cd3 | ||
|
|
e43edee9bb | ||
|
|
8de94d45b1 | ||
|
|
9e97160c85 | ||
|
|
8a5828620c | ||
|
|
07001ae35e | ||
|
|
cd216e218c | ||
|
|
054836cd50 | ||
|
|
0ef7c8d16d | ||
|
|
af30ef1f72 | ||
|
|
df86e85492 | ||
|
|
05c9b44b81 | ||
|
|
0f3ea9f4ce | ||
|
|
a5d00c73b2 | ||
|
|
f9a5f4b61e | ||
|
|
40b974f22d | ||
|
|
f969ccb50c | ||
|
|
b007edca63 | ||
|
|
3ce29622ed | ||
|
|
1a6afc2ef0 | ||
|
|
6dea8e7256 | ||
|
|
7595071e6a | ||
|
|
fdad00790e | ||
|
|
9ecf5bed64 | ||
|
|
ff9608c914 | ||
|
|
49e5f18f62 | ||
|
|
c5b0ea7e9f | ||
|
|
83eae1b64a | ||
|
|
35ee9c9ddd | ||
|
|
0a8f5b6223 | ||
|
|
3f120c7027 | ||
|
|
343670c5c4 | ||
|
|
f21b6203ed | ||
|
|
8517f9f2bf | ||
|
|
3c12191cb7 | ||
|
|
ab22ca6a28 | ||
|
|
8898b444af | ||
|
|
d079617ce2 | ||
|
|
93c67771af | ||
|
|
f9040e08ce | ||
|
|
fd8864d528 | ||
|
|
841c790337 | ||
|
|
324c3e7dcf | ||
|
|
92728ff4e6 | ||
|
|
01641543da | ||
|
|
9038b984ff | ||
|
|
da97185fcd | ||
|
|
b6e9c1eaab | ||
|
|
76c6d6d4d6 | ||
|
|
4c11de787e | ||
|
|
96c825b89f | ||
|
|
29af81e827 | ||
|
|
3cf9f5248b | ||
|
|
9ec10e2b43 | ||
|
|
06427d663d | ||
|
|
2c51a5c199 | ||
|
|
6dc5dd4930 | ||
|
|
29ee7d2b13 | ||
|
|
2e376b1eb9 | ||
|
|
37254e6243 | ||
|
|
1edea2a62c | ||
|
|
99ccff098c | ||
|
|
3075c97bae | ||
|
|
f62312fbf3 | ||
|
|
02d5154aaf | ||
|
|
41eaf18470 | ||
|
|
d372018e61 | ||
|
|
bb6eeea0d8 | ||
|
|
3cf9f786aa | ||
|
|
2d481a6302 | ||
|
|
e700a5a219 | ||
|
|
f7127ab701 | ||
|
|
eaafc11064 | ||
|
|
3a003341ad | ||
|
|
f7fe871fee | ||
|
|
09b0d221df | ||
|
|
ed3d3a9e23 | ||
|
|
eb1c6b347d | ||
|
|
5f57cd9559 | ||
|
|
0dd85d9adf | ||
|
|
23d45d7f33 | ||
|
|
69fdd485e6 | ||
|
|
bf3e90bb47 | ||
|
|
68a005bf1f | ||
|
|
884d0de90b | ||
|
|
6e3afcde53 | ||
|
|
d66006893a | ||
|
|
8fed464cf6 | ||
|
|
08ba0457e8 | ||
|
|
098491e350 | ||
|
|
46541a3f2e | ||
|
|
c9fe0b96b7 | ||
|
|
742ae354e5 | ||
|
|
bc55959fad | ||
|
|
5424567a66 | ||
|
|
f0df3f29b9 | ||
|
|
b4c0625961 | ||
|
|
025b9e2fc8 | ||
|
|
1099892784 | ||
|
|
c42d4f901b | ||
|
|
ed3527e243 | ||
|
|
8e8b27c893 | ||
|
|
c63dd376d8 | ||
|
|
da55081c68 | ||
|
|
80c2bd0c7f | ||
|
|
714c96283e | ||
|
|
c57fb44c71 | ||
|
|
8602e0665d | ||
|
|
af1e3373ea | ||
|
|
79e39429b7 | ||
|
|
7b3eea0b58 | ||
|
|
88c5a5e074 | ||
|
|
e23b90abd5 | ||
|
|
7f61a0252f | ||
|
|
816f20e068 | ||
|
|
bb59e9276b | ||
|
|
d6b86598e5 | ||
|
|
bf91155e60 | ||
|
|
ef181f55d5 | ||
|
|
1c7e7cd111 | ||
|
|
063e387a65 | ||
|
|
ca07c8f429 | ||
|
|
2fd7196cdd | ||
|
|
ff59fc84c5 | ||
|
|
9d620dfb1d | ||
|
|
bc2c744bed | ||
|
|
2fabf69ce3 | ||
|
|
f8d628d336 | ||
|
|
20f84ce322 | ||
|
|
2cf0ceb260 | ||
|
|
36b7deac35 | ||
|
|
04305460db | ||
|
|
caa2fd97d1 | ||
|
|
e0efb6862e | ||
|
|
1ac47f32fe | ||
|
|
b1438355e2 | ||
|
|
021eaf5c29 | ||
|
|
726afd30bb | ||
|
|
58472b8251 | ||
|
|
8826eb60cc | ||
|
|
c8a8306165 | ||
|
|
c12c716dc0 | ||
|
|
05eda88ea2 | ||
|
|
2cae5e7a00 | ||
|
|
2fff6f4d5f | ||
|
|
81128ef06e | ||
|
|
dd3427d8d0 | ||
|
|
2c9273a86c | ||
|
|
bc3ee949f5 | ||
|
|
5aa468f1e3 | ||
|
|
c2af09fbaa | ||
|
|
bbd7124ac7 | ||
|
|
adb7915b3e | ||
|
|
5c92b09da1 | ||
|
|
2e9e03bd45 | ||
|
|
19b31ff30d | ||
|
|
801154fd8a | ||
|
|
f628591e27 | ||
|
|
9cbd4ae2e4 | ||
|
|
0825e0a2e2 | ||
|
|
68c1ddd5d2 | ||
|
|
9a6624d1c7 | ||
|
|
c9823d07fd | ||
|
|
450036a6ed | ||
|
|
ef7a38e558 | ||
|
|
2ca64d9c15 | ||
|
|
cb887c699e | ||
|
|
2ccd881665 | ||
|
|
87bb7c9b7b | ||
|
|
6d9817e5e7 | ||
|
|
23c93de82e | ||
|
|
bea64082a9 | ||
|
|
c3385d597a | ||
|
|
752f8bdbb8 | ||
|
|
1f69760173 | ||
|
|
66add5673b | ||
|
|
a3082753ef | ||
|
|
eaa2f94327 | ||
|
|
615879ffdd | ||
|
|
05d921256f | ||
|
|
9526deb024 | ||
|
|
567176ea6c | ||
|
|
5494a4ea6c | ||
|
|
827c0da33c | ||
|
|
f0dbb422f6 | ||
|
|
5c406856ed | ||
|
|
b15def84bc | ||
|
|
bbbe074d92 | ||
|
|
36da1accca | ||
|
|
e289235e17 | ||
|
|
c60e8736c1 | ||
|
|
69899e3718 | ||
|
|
ed4c5b9f73 | ||
|
|
4e170a2831 | ||
|
|
07200d7953 | ||
|
|
4a195dd3f0 | ||
|
|
fe442f5c24 | ||
|
|
89327bd38f | ||
|
|
f102e3b3b7 | ||
|
|
1150e22190 | ||
|
|
886ffbf158 | ||
|
|
c884c5fc33 | ||
|
|
d462e0b21b | ||
|
|
fdf79d709e | ||
|
|
e20388388e | ||
|
|
3a8c263e8e | ||
|
|
804af341ac | ||
|
|
0aa90d918c | ||
|
|
4bf6992398 | ||
|
|
8842147ec3 | ||
|
|
942659df0d | ||
|
|
61e55b3ca3 | ||
|
|
7fe7af6026 | ||
|
|
7dc5f91fad | ||
|
|
5b773b99c0 | ||
|
|
8b9b268ec0 | ||
|
|
27cf9cf561 | ||
|
|
b1b2704bed | ||
|
|
2a4a02f36e | ||
|
|
ff35e02b4d | ||
|
|
4dac9bc1b8 | ||
|
|
11c38014e5 | ||
|
|
842d6b6c2c | ||
|
|
6e63153d83 | ||
|
|
159f0c9594 | ||
|
|
e869814f2d | ||
|
|
3b82884947 | ||
|
|
38780ad492 | ||
|
|
e25d31a9fe | ||
|
|
56d00c2ec7 | ||
|
|
79af89fd1b | ||
|
|
11e176df66 | ||
|
|
75bc878657 | ||
|
|
ddbc8dffb3 | ||
|
|
6aad99a505 | ||
|
|
35cebc56d3 | ||
|
|
668234be4c | ||
|
|
626e8bab1a | ||
|
|
633bbd8f29 | ||
|
|
ffb9ce89c7 | ||
|
|
ce9a91e155 | ||
|
|
fdfa0cbd0e | ||
|
|
d315e4afcd | ||
|
|
80c04048d0 | ||
|
|
05d96f4cfb | ||
|
|
8239e57fa1 | ||
|
|
f4ca30bb38 | ||
|
|
cc313f350c | ||
|
|
ae2768af9c | ||
|
|
511b1f409c | ||
|
|
f00ee95563 | ||
|
|
9fd2bf0989 | ||
|
|
2c05515141 | ||
|
|
eb6fbe6a5a | ||
|
|
2ee840922d | ||
|
|
0ade5ff640 | ||
|
|
dc401075a7 | ||
|
|
de1e8e9f93 | ||
|
|
dbac2e299e | ||
|
|
8eaa96b0b3 | ||
|
|
cb095ba5a0 | ||
|
|
0bfb1416c1 | ||
|
|
3db86b1f59 | ||
|
|
5c77395faa | ||
|
|
74afa710d1 | ||
|
|
e48e636c44 | ||
|
|
22369729f9 | ||
|
|
59d8cbe742 | ||
|
|
00a7ea994a | ||
|
|
4118c05d15 | ||
|
|
7fbe38e74d | ||
|
|
cee37c4152 | ||
|
|
4175dcd102 | ||
|
|
35862e0c66 | ||
|
|
424d1b84db | ||
|
|
3423a493e7 | ||
|
|
a25207960c | ||
|
|
04aff6aab7 | ||
|
|
cbaf134625 | ||
|
|
731ad26be4 | ||
|
|
9dfd0bc3bb | ||
|
|
b8fc926255 | ||
|
|
05dba9c2d4 | ||
|
|
ed52bc37b2 | ||
|
|
99e8a54a27 | ||
|
|
9455d02d50 | ||
|
|
c98f7f926a | ||
|
|
c91b642a8b | ||
|
|
ce33c8cdf6 | ||
|
|
aac00db16b | ||
|
|
7c445cc108 | ||
|
|
58bac0fbdc | ||
|
|
9217ae8fbb | ||
|
|
363e3f4e21 | ||
|
|
78cff9f20d | ||
|
|
86fb313b9b | ||
|
|
8840895e70 | ||
|
|
ed76a46739 | ||
|
|
7fdb82d87f | ||
|
|
729babae4f | ||
|
|
731881ee7b | ||
|
|
e580cb809d | ||
|
|
47dca51c38 | ||
|
|
1ecf0f0183 | ||
|
|
5844fb4020 | ||
|
|
8257f325c4 | ||
|
|
379fed813e | ||
|
|
435a9cd9e4 | ||
|
|
8cd6d70c0a | ||
|
|
1d3c821672 | ||
|
|
6d6e1366dc | ||
|
|
19d272b171 | ||
|
|
1188c4c69f | ||
|
|
88dd135b5a | ||
|
|
8ebb8ba427 | ||
|
|
9c215efcbf | ||
|
|
6e3e0a1447 | ||
|
|
d16968d528 | ||
|
|
28c3787fb3 | ||
|
|
d8bc362a89 | ||
|
|
13f3548057 | ||
|
|
39871e52df | ||
|
|
0cffd8dd84 | ||
|
|
f4d21f883a | ||
|
|
a74cef0d64 | ||
|
|
e59bfe16dc | ||
|
|
e718cad053 | ||
|
|
b6cac2bc89 | ||
|
|
a86861e9b9 | ||
|
|
6e2362e8a9 | ||
|
|
300005243c | ||
|
|
5392afdec4 | ||
|
|
e844b7aa21 | ||
|
|
4019e359ca | ||
|
|
185efb00fb | ||
|
|
d946b39671 | ||
|
|
c74dc8ef47 | ||
|
|
7292a2ced5 | ||
|
|
baf777a418 | ||
|
|
11e6cf9757 | ||
|
|
30b2156278 | ||
|
|
d66427ddde | ||
|
|
f618585bd6 | ||
|
|
7c6fb36520 | ||
|
|
7f65ba506b | ||
|
|
351a94b4a1 | ||
|
|
ad4e3418ff | ||
|
|
82affac438 | ||
|
|
08270b26ee | ||
|
|
4b645bcd66 | ||
|
|
1f3a6e408c | ||
|
|
3779ff7691 | ||
|
|
3d3680e42f | ||
|
|
af67df4c4a | ||
|
|
bf40011815 | ||
|
|
a9b093b7f5 | ||
|
|
5e7bd1e51f | ||
|
|
7142ea8f1e | ||
|
|
c8f6d46c8b | ||
|
|
5d1ad4d259 | ||
|
|
006f0b00c6 | ||
|
|
fe0707535c | ||
|
|
044c75270f | ||
|
|
209889210b | ||
|
|
93d81d27ba | ||
|
|
da6c5653b1 | ||
|
|
79eda12656 | ||
|
|
c74f7c956f | ||
|
|
137439243a | ||
|
|
bb5895c157 | ||
|
|
34ad48a5d3 | ||
|
|
078134d481 | ||
|
|
f882ecc31b | ||
|
|
d302f3eebb | ||
|
|
bdbfc2b6e0 | ||
|
|
f256f79418 | ||
|
|
d1cda75c8b | ||
|
|
530b077a8e | ||
|
|
11aa6f8c37 | ||
|
|
e3e49daddb | ||
|
|
4b904d90f2 | ||
|
|
48924a6106 | ||
|
|
2adb8bac5c | ||
|
|
c0938f270e | ||
|
|
47c1bb35db | ||
|
|
7598be684c | ||
|
|
1dd707775a | ||
|
|
293ee1bbcb | ||
|
|
b5357d3298 | ||
|
|
c561647460 | ||
|
|
f5039ac9af | ||
|
|
353af6c647 | ||
|
|
17c81c1101 | ||
|
|
50ebce69b7 | ||
|
|
191d56673b | ||
|
|
5d962e1feb | ||
|
|
201caed773 | ||
|
|
9be98058b7 | ||
|
|
ba0ae5ba59 | ||
|
|
9a8a9a4ce4 | ||
|
|
b05f3343e2 | ||
|
|
ae506b5b1f | ||
|
|
93de2307c1 | ||
|
|
8bdb5c0745 | ||
|
|
47ed8971e3 | ||
|
|
a8d51cdf58 | ||
|
|
a05437e81f | ||
|
|
93f266a4fa | ||
|
|
aed1fe9bf1 | ||
|
|
7296cbfd5b | ||
|
|
023034ce4f | ||
|
|
c68ef38399 | ||
|
|
ccc5f30c9b | ||
|
|
c22442f6d1 | ||
|
|
fca65a8cdb | ||
|
|
807e947146 | ||
|
|
85636ccdad | ||
|
|
490e56bfbb | ||
|
|
61f951a33e | ||
|
|
53c8b9bcf7 | ||
|
|
df39c3a281 | ||
|
|
050c6cf72f | ||
|
|
2247d951d6 | ||
|
|
7b9cd7c232 | ||
|
|
c687f32f39 | ||
|
|
3845a989f6 | ||
|
|
94a6f856d1 | ||
|
|
c62d97ca04 | ||
|
|
fd6e7663cb | ||
|
|
7d540572fd | ||
|
|
c3f32b74e4 | ||
|
|
588dbf5693 | ||
|
|
91c0df4450 | ||
|
|
3ecf19df49 | ||
|
|
f778f6adf9 | ||
|
|
796f424a3f | ||
|
|
409697b35b | ||
|
|
f020f4397c | ||
|
|
5fe41e28d7 | ||
|
|
a5a6a35122 | ||
|
|
bfa4cda2c6 | ||
|
|
c21dd853f9 | ||
|
|
1901fdf889 | ||
|
|
b11abae8e8 | ||
|
|
7e72ee891a | ||
|
|
40594fc5fa | ||
|
|
dd4cfb25f8 | ||
|
|
148dbc23ed | ||
|
|
682392d02a | ||
|
|
09b81f46b0 | ||
|
|
2f0df6d37e | ||
|
|
616b4fe0f1 | ||
|
|
ef3603cd1a | ||
|
|
61c94d63e7 | ||
|
|
260e22186b | ||
|
|
048260bb1b | ||
|
|
a545007a19 | ||
|
|
56c5f6f46e | ||
|
|
81db1b2360 | ||
|
|
240d5502fe | ||
|
|
7a50166dc6 | ||
|
|
9c8b540d14 | ||
|
|
e991beb900 | ||
|
|
f1120562f3 | ||
|
|
901574b56e | ||
|
|
fe586f6a36 | ||
|
|
01d3f2f119 | ||
|
|
0aec086ebb | ||
|
|
f89fbffe89 | ||
|
|
2b65b4c2dc | ||
|
|
ce2632bbe6 | ||
|
|
370310bf82 | ||
|
|
f384aa7d9e | ||
|
|
353269370f | ||
|
|
7866979c79 | ||
|
|
5e3698de64 | ||
|
|
59986d8b72 | ||
|
|
fc892b3580 | ||
|
|
e3b02a295c | ||
|
|
77401e215e | ||
|
|
ce9fcdbbb5 | ||
|
|
980c71076e | ||
|
|
ee4da24b84 | ||
|
|
737fc74756 | ||
|
|
027ab6ee99 | ||
|
|
8214ee8fad | ||
|
|
ab068cc372 | ||
|
|
5bab440a1f | ||
|
|
ef027706b9 | ||
|
|
2351ad997c | ||
|
|
cb25740961 | ||
|
|
e3798e1b85 | ||
|
|
80c3b2c8a3 | ||
|
|
a2e7c4aa77 | ||
|
|
25a4f1fde0 | ||
|
|
6b72c992c5 | ||
|
|
cb7f1aa916 | ||
|
|
1176168960 | ||
|
|
24630f598f | ||
|
|
316a28838f | ||
|
|
960a38fe43 | ||
|
|
87feb6b076 | ||
|
|
c5e33352b0 | ||
|
|
e1efb165fd | ||
|
|
12e53f5046 | ||
|
|
4851adf3b0 | ||
|
|
9ed5ca3ccb | ||
|
|
88095d4360 | ||
|
|
7bdf612ad5 | ||
|
|
6d390ebd2f | ||
|
|
ca09758210 | ||
|
|
e5099ce3b7 | ||
|
|
a3879b507a | ||
|
|
7a8537f3dc | ||
|
|
001d1c50ef | ||
|
|
fec266f1c0 | ||
|
|
b580fba7db | ||
|
|
8bb836ad49 | ||
|
|
eb36a2b242 | ||
|
|
18be8530fe | ||
|
|
cf77a96ac5 | ||
|
|
5153954a28 | ||
|
|
bf10a03ab1 | ||
|
|
566c0437c0 | ||
|
|
2ffa450e31 | ||
|
|
8fd26509ac | ||
|
|
3fc4aee269 | ||
|
|
a20b4d2d2c | ||
|
|
be5aaeaad7 | ||
|
|
18c56a171e | ||
|
|
b44d19d305 | ||
|
|
a9fc47efd7 | ||
|
|
a45785fe1a | ||
|
|
19d350e876 | ||
|
|
7a1796870a | ||
|
|
96cedc237e | ||
|
|
6ab993f1a9 | ||
|
|
efcc2061b8 | ||
|
|
10053fa770 | ||
|
|
3519555710 | ||
|
|
0eceb737de | ||
|
|
64727cb60e | ||
|
|
711bf583ab | ||
|
|
2771907573 | ||
|
|
68d408bfff | ||
|
|
cdbbf4bfef | ||
|
|
5cffee7ce6 | ||
|
|
9d8d4e4896 | ||
|
|
259d9dc3a2 | ||
|
|
69ac0bd368 | ||
|
|
9f98c4e9f7 | ||
|
|
278a8282fe | ||
|
|
55a4f3e3a1 | ||
|
|
2d1b88e50d | ||
|
|
7017f68db7 | ||
|
|
2391c21eeb | ||
|
|
f5b9f470b2 | ||
|
|
7ac852d1fe | ||
|
|
a24d7a9bce | ||
|
|
65cf8f030c | ||
|
|
05cc604e8d | ||
|
|
dc112e718e | ||
|
|
2f9e3fcaea | ||
|
|
33f2955927 | ||
|
|
674f90f27f | ||
|
|
79c2a5abd6 | ||
|
|
02846c4fff | ||
|
|
cc9123a33d | ||
|
|
5248b0c631 | ||
|
|
e2644e3c13 | ||
|
|
ccaeb43ff3 | ||
|
|
11f5561be6 | ||
|
|
59f380d3fc | ||
|
|
d4efb37b03 | ||
|
|
0781c767ff | ||
|
|
11e1a45ed5 | ||
|
|
6f62211465 | ||
|
|
c2e92045d0 | ||
|
|
9847383ba6 | ||
|
|
772053713a | ||
|
|
7ffd684a9e | ||
|
|
51d2677525 | ||
|
|
c726257e9b | ||
|
|
fbb6775523 | ||
|
|
8b0db49b8b | ||
|
|
b8c18130da | ||
|
|
431c93fc29 | ||
|
|
0016c2ad83 | ||
|
|
ed1cb44deb | ||
|
|
b9fd174f72 | ||
|
|
d4147c1315 | ||
|
|
7e42072952 | ||
|
|
6bfc566d9a | ||
|
|
5835ea0a97 | ||
|
|
e80cf8a133 | ||
|
|
5fb6ea94b2 | ||
|
|
e1aca588b5 | ||
|
|
79e03597b3 | ||
|
|
6b44fa7642 | ||
|
|
772f987489 | ||
|
|
663646f845 | ||
|
|
f6be51bd98 | ||
|
|
538536eb88 | ||
|
|
c8f3f5841c | ||
|
|
0f8686b8cd | ||
|
|
8c25855f38 | ||
|
|
92e346a842 | ||
|
|
904754b20d | ||
|
|
2eba2280d8 | ||
|
|
67d3368e1e | ||
|
|
ceb214f192 | ||
|
|
0b3a9baa44 | ||
|
|
b2130a5295 | ||
|
|
a2f5933417 | ||
|
|
9af09de7d4 | ||
|
|
aaa96f1ac1 | ||
|
|
e03809b224 | ||
|
|
6fc910a259 | ||
|
|
959c0f0669 | ||
|
|
8dc73cb6cc | ||
|
|
2160440ff1 | ||
|
|
f036a10a7d | ||
|
|
6bb6eafdc0 | ||
|
|
e0496305aa | ||
|
|
2698405e2f | ||
|
|
c1de5e9e95 | ||
|
|
ed738b6398 | ||
|
|
e36ca10e6c | ||
|
|
71f5d1f6cb | ||
|
|
4679e005bf | ||
|
|
c5d84562ba | ||
|
|
16846c36fd | ||
|
|
2fe56fd86d | ||
|
|
89f6459915 | ||
|
|
b8e1927e82 | ||
|
|
d81260c92a | ||
|
|
76c014b9ef | ||
|
|
2454a71b38 | ||
|
|
726ee7b50b | ||
|
|
3474f08334 | ||
|
|
13f04f77dc | ||
|
|
085e07c5b1 | ||
|
|
2e2cecdd4f | ||
|
|
0e9074b0de | ||
|
|
2bec7ec981 | ||
|
|
0b1b6057d6 | ||
|
|
af190f286c | ||
|
|
3657029fc7 | ||
|
|
12c824323d | ||
|
|
fe71f69f0a | ||
|
|
e0673eee29 | ||
|
|
36b1280f0c | ||
|
|
dcecdc8260 | ||
|
|
813cf0481e | ||
|
|
0a3cddbd89 | ||
|
|
e9b2cd1364 | ||
|
|
5d59a5b297 | ||
|
|
3a7ebf73eb | ||
|
|
9d110d58e5 | ||
|
|
bcab2f231a | ||
|
|
1b6ca2b0ee | ||
|
|
eae0972820 | ||
|
|
6fe842e130 | ||
|
|
bb06484732 | ||
|
|
05d9afc040 | ||
|
|
04f17c963c | ||
|
|
0039ccf203 | ||
|
|
f2bd802bdc | ||
|
|
28022534f7 | ||
|
|
d8494ff89b | ||
|
|
715db89204 | ||
|
|
19ee75577e | ||
|
|
0baa2141fc | ||
|
|
08c16e0d7a | ||
|
|
87dd9e8bb4 | ||
|
|
46764c3614 | ||
|
|
0fa7f6cb63 | ||
|
|
29b4f59982 | ||
|
|
a477c8be4c | ||
|
|
b82d932a51 | ||
|
|
2121ddc295 | ||
|
|
6eaba4ff04 | ||
|
|
337e6b329f | ||
|
|
caa7f813eb | ||
|
|
e8b944c0e1 | ||
|
|
e1f3c80f19 | ||
|
|
73c8eb7738 | ||
|
|
5aa913f201 | ||
|
|
85748c09cf | ||
|
|
58907e5842 | ||
|
|
202c155788 | ||
|
|
2d25414b57 | ||
|
|
d239070adb | ||
|
|
1e1f7492d8 | ||
|
|
919e1cf84f | ||
|
|
981bcbe74f | ||
|
|
27d19f2ec8 | ||
|
|
ea8737d957 | ||
|
|
c83ff1c623 | ||
|
|
db94a93fde | ||
|
|
fc5f9bb70c | ||
|
|
5b83974edd | ||
|
|
5a12a4a1a3 | ||
|
|
aa4f9abd5c | ||
|
|
97981058f0 | ||
|
|
00d2d82a14 | ||
|
|
4ded74765a | ||
|
|
b685e784f1 | ||
|
|
10cdf46c2c | ||
|
|
0d34728190 | ||
|
|
15edb9a80b | ||
|
|
706abe654a | ||
|
|
d4541f54a3 | ||
|
|
348aa3e5e1 | ||
|
|
6280adc6e7 | ||
|
|
6394978326 | ||
|
|
d09ddc48e0 | ||
|
|
48063608c7 | ||
|
|
aa70dd7b67 | ||
|
|
5f367f7c75 | ||
|
|
5c7bef3107 | ||
|
|
b60a3b61bb | ||
|
|
ddb426095e | ||
|
|
214ef4b4ce | ||
|
|
88167358bb | ||
|
|
7bef6245f7 | ||
|
|
812eb66ed5 | ||
|
|
827e777079 | ||
|
|
ff567faeaa | ||
|
|
782234de6d | ||
|
|
328d5004d8 | ||
|
|
a730804943 | ||
|
|
1df8e65cdf | ||
|
|
383a958abe | ||
|
|
9a79539978 | ||
|
|
5c6b8a4cf9 | ||
|
|
b6f9ca0f95 | ||
|
|
6a76d8ace8 | ||
|
|
8fe6411539 | ||
|
|
b53640f892 | ||
|
|
70fce0989b | ||
|
|
00c45e48a9 | ||
|
|
37108ac56c | ||
|
|
1b03aa8119 | ||
|
|
2bb2295499 | ||
|
|
001372ec39 | ||
|
|
006907e52f | ||
|
|
c8caf34777 | ||
|
|
891ba40114 | ||
|
|
1a95148dae | ||
|
|
a2ca5f2847 | ||
|
|
28dcfb2f12 | ||
|
|
0f39ec580f | ||
|
|
8ccce4d702 | ||
|
|
d08c47a328 | ||
|
|
cfbef0177e | ||
|
|
8feeb93215 | ||
|
|
1126769686 | ||
|
|
789e70f2d6 | ||
|
|
df999fb2f8 | ||
|
|
d4a98b3850 | ||
|
|
ce3dab3c5b | ||
|
|
91943d8a45 | ||
|
|
9aa66c1d8b | ||
|
|
03733e6a0f | ||
|
|
59ca26ee93 | ||
|
|
98160c3947 | ||
|
|
68d60aadd1 | ||
|
|
aa1b763518 | ||
|
|
75f8363be0 | ||
|
|
3a6423dd0a | ||
|
|
6b81022e28 | ||
|
|
47f4c0dfff | ||
|
|
ebedf0b907 | ||
|
|
95968bf619 | ||
|
|
50edc619af | ||
|
|
ebe6072225 | ||
|
|
cd26d1323f | ||
|
|
155dd74a6f | ||
|
|
b043889169 | ||
|
|
1677a3bf3a | ||
|
|
dac94d2293 | ||
|
|
41bab56133 | ||
|
|
42c9766203 | ||
|
|
6fbb7d7da4 | ||
|
|
cf38b8a5bb | ||
|
|
c708976635 | ||
|
|
0a3a3dac1a | ||
|
|
c4379e4827 | ||
|
|
41f659db4c | ||
|
|
a6f857e9d8 | ||
|
|
5f3721f471 | ||
|
|
c056bdf104 | ||
|
|
50f9d34211 | ||
|
|
eedd437ca7 | ||
|
|
7ffd97b5dc | ||
|
|
73a1c2b581 | ||
|
|
5d9db52b32 | ||
|
|
f17a9b14c2 | ||
|
|
3555f35737 | ||
|
|
ca593b8544 | ||
|
|
5d99abf18c | ||
|
|
139b92fcd6 | ||
|
|
ecd1f09095 | ||
|
|
02045858f7 | ||
|
|
edc3302d89 | ||
|
|
bc93aeb50e | ||
|
|
1b4358624f | ||
|
|
4f132c418f | ||
|
|
14a4da54f8 | ||
|
|
37c9c8fbb4 | ||
|
|
6316f1b195 | ||
|
|
2b81f46030 | ||
|
|
dfd54f3b95 | ||
|
|
7e30c0f47b | ||
|
|
28cebab9a3 | ||
|
|
7cbb43fddb | ||
|
|
78bea916e1 | ||
|
|
844a883ad8 | ||
|
|
f94a061fda | ||
|
|
aba103b8e0 | ||
|
|
2fc08aeb12 | ||
|
|
c69606df44 | ||
|
|
8db6c17220 | ||
|
|
b739e7be9e | ||
|
|
ce38d4ea12 | ||
|
|
9b384e52b5 | ||
|
|
c0bd574997 | ||
|
|
9a51cace34 | ||
|
|
77e76972f0 | ||
|
|
dff9353339 | ||
|
|
69d1844773 | ||
|
|
6b0167375c | ||
|
|
341fe868e4 | ||
|
|
a5d24329c2 | ||
|
|
10b3f250ec | ||
|
|
18066d848a | ||
|
|
06dfbbf821 | ||
|
|
20f1c075fc | ||
|
|
bfe0cdcfd1 |
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,4 +1,12 @@
|
||||
node_modules
|
||||
build
|
||||
bundle.css
|
||||
bundle.js
|
||||
/cert.pem
|
||||
/karma-reports
|
||||
/key.pem
|
||||
/lib
|
||||
/node_modules
|
||||
/packages/
|
||||
/vector/bundle.*
|
||||
/vector/components.css
|
||||
/vector/emojione/
|
||||
/vector/config.json
|
||||
/vector/olm.js
|
||||
.DS_Store
|
||||
|
||||
14
.modernizr.json
Normal file
14
.modernizr.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"minify": true,
|
||||
"classPrefix": "modernizr_",
|
||||
"options": [
|
||||
"setClasses"
|
||||
],
|
||||
"feature-detects": [
|
||||
"test/css/displaytable",
|
||||
"test/css/flexbox",
|
||||
"test/es5/specification",
|
||||
"test/css/objectfit",
|
||||
"test/storage/localstorage"
|
||||
]
|
||||
}
|
||||
12
AUTHORS.rst
Normal file
12
AUTHORS.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
Vector is written mainly by the Vector team, building upon the Matrix React
|
||||
SDK. Vector also welcomes external contributions. Third party contributors
|
||||
include:
|
||||
|
||||
* Nolan Darilek (https://github.com/ndarilek)
|
||||
Accessibility and semantic markup contributions
|
||||
|
||||
* https://github.com/neko259
|
||||
Improved scrollbar CSS
|
||||
|
||||
* Florent VIOLLEAU (https://github.com/floviolleau) <floviolleau at gmail dot com>
|
||||
Improve README.md for a better understanding of installation instructions
|
||||
273
CHANGELOG.md
Normal file
273
CHANGELOG.md
Normal file
@@ -0,0 +1,273 @@
|
||||
Changes in [0.7.4](https://github.com/vector-im/vector-web/releases/tag/v0.7.4) (2016-08-11)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.3...v0.7.4)
|
||||
|
||||
* Don't show border on composer when not in RTE mode
|
||||
[\#1954](https://github.com/vector-im/vector-web/pull/1954)
|
||||
* Wmwragg/room tag menu
|
||||
[\#1941](https://github.com/vector-im/vector-web/pull/1941)
|
||||
* Don't redirect to mobile app if verifying 3pid
|
||||
[\#1951](https://github.com/vector-im/vector-web/pull/1951)
|
||||
* Make sure that we clear localstorage before *all* tests
|
||||
[\#1950](https://github.com/vector-im/vector-web/pull/1950)
|
||||
* Basic CSS for multi-invite dialog
|
||||
[\#1942](https://github.com/vector-im/vector-web/pull/1942)
|
||||
* More tests for the loading process:
|
||||
[\#1947](https://github.com/vector-im/vector-web/pull/1947)
|
||||
* Support for refactored login token handling
|
||||
[\#1946](https://github.com/vector-im/vector-web/pull/1946)
|
||||
* Various fixes and improvements to emojification.
|
||||
[\#1935](https://github.com/vector-im/vector-web/pull/1935)
|
||||
* More app-loading tests
|
||||
[\#1938](https://github.com/vector-im/vector-web/pull/1938)
|
||||
* Some tests of the application load process
|
||||
[\#1936](https://github.com/vector-im/vector-web/pull/1936)
|
||||
* Add 'enable labs' setting to sample config
|
||||
[\#1930](https://github.com/vector-im/vector-web/pull/1930)
|
||||
* Matthew/scalar
|
||||
[\#1928](https://github.com/vector-im/vector-web/pull/1928)
|
||||
* Fix unit tests
|
||||
[\#1929](https://github.com/vector-im/vector-web/pull/1929)
|
||||
* Wmwragg/mute mention state fix
|
||||
[\#1926](https://github.com/vector-im/vector-web/pull/1926)
|
||||
* CSS for deactivate account dialog
|
||||
[\#1919](https://github.com/vector-im/vector-web/pull/1919)
|
||||
* Wmwragg/mention state menu
|
||||
[\#1900](https://github.com/vector-im/vector-web/pull/1900)
|
||||
* Fix UnknownBody styling for #1901
|
||||
[\#1913](https://github.com/vector-im/vector-web/pull/1913)
|
||||
* Exclude olm from the webpack
|
||||
[\#1914](https://github.com/vector-im/vector-web/pull/1914)
|
||||
* Wmwragg/button updates
|
||||
[\#1912](https://github.com/vector-im/vector-web/pull/1912)
|
||||
* Wmwragg/button updates
|
||||
[\#1828](https://github.com/vector-im/vector-web/pull/1828)
|
||||
* CSS for device management UI
|
||||
[\#1909](https://github.com/vector-im/vector-web/pull/1909)
|
||||
* Fix a warning from RoomSubList
|
||||
[\#1908](https://github.com/vector-im/vector-web/pull/1908)
|
||||
* Fix notifications warning layout
|
||||
[\#1907](https://github.com/vector-im/vector-web/pull/1907)
|
||||
* Remove relayoutOnUpdate prop on gemini-scrollbar
|
||||
[\#1883](https://github.com/vector-im/vector-web/pull/1883)
|
||||
* Bump dependency versions
|
||||
[\#1842](https://github.com/vector-im/vector-web/pull/1842)
|
||||
* Wmwragg/mention state indicator round 2
|
||||
[\#1835](https://github.com/vector-im/vector-web/pull/1835)
|
||||
* Wmwragg/spinner fix
|
||||
[\#1822](https://github.com/vector-im/vector-web/pull/1822)
|
||||
* Wmwragg/mention state indicator
|
||||
[\#1823](https://github.com/vector-im/vector-web/pull/1823)
|
||||
* Revert "Presentation for inline link"
|
||||
[\#1809](https://github.com/vector-im/vector-web/pull/1809)
|
||||
* Wmwragg/modal restyle
|
||||
[\#1806](https://github.com/vector-im/vector-web/pull/1806)
|
||||
* Presentation for inline link
|
||||
[\#1799](https://github.com/vector-im/vector-web/pull/1799)
|
||||
* CSS for offline user colours
|
||||
[\#1798](https://github.com/vector-im/vector-web/pull/1798)
|
||||
* Wmwragg/typography updates
|
||||
[\#1776](https://github.com/vector-im/vector-web/pull/1776)
|
||||
* webpack: always use the olm from vector-web
|
||||
[\#1766](https://github.com/vector-im/vector-web/pull/1766)
|
||||
* feat: large emoji support
|
||||
[\#1718](https://github.com/vector-im/vector-web/pull/1718)
|
||||
* Autocomplete
|
||||
[\#1717](https://github.com/vector-im/vector-web/pull/1717)
|
||||
* #1664 Set a maximum height for codeblocks
|
||||
[\#1670](https://github.com/vector-im/vector-web/pull/1670)
|
||||
* CSS for device blocking
|
||||
[\#1688](https://github.com/vector-im/vector-web/pull/1688)
|
||||
* Fix joining rooms by typing the alias
|
||||
[\#1685](https://github.com/vector-im/vector-web/pull/1685)
|
||||
* Add ability to delete an alias from room directory
|
||||
[\#1680](https://github.com/vector-im/vector-web/pull/1680)
|
||||
* package.json: add olm as optionalDependency
|
||||
[\#1678](https://github.com/vector-im/vector-web/pull/1678)
|
||||
* Another go at enabling olm on vector.im/develop
|
||||
[\#1675](https://github.com/vector-im/vector-web/pull/1675)
|
||||
* CSS for unverify button
|
||||
[\#1661](https://github.com/vector-im/vector-web/pull/1661)
|
||||
* CSS fix for rooms with crypto enabled
|
||||
[\#1660](https://github.com/vector-im/vector-web/pull/1660)
|
||||
* Karma: fix warning by ignoring olm
|
||||
[\#1652](https://github.com/vector-im/vector-web/pull/1652)
|
||||
* Update for react-sdk dbkr/fix_peeking branch
|
||||
[\#1639](https://github.com/vector-im/vector-web/pull/1639)
|
||||
* Update README.md
|
||||
[\#1641](https://github.com/vector-im/vector-web/pull/1641)
|
||||
* Fix karma tests
|
||||
[\#1643](https://github.com/vector-im/vector-web/pull/1643)
|
||||
* Rich Text Editor
|
||||
[\#1553](https://github.com/vector-im/vector-web/pull/1553)
|
||||
* Fix RoomDirectory to join by alias whenever possible.
|
||||
[\#1615](https://github.com/vector-im/vector-web/pull/1615)
|
||||
* Make the config optional
|
||||
[\#1612](https://github.com/vector-im/vector-web/pull/1612)
|
||||
* CSS support for device verification
|
||||
[\#1610](https://github.com/vector-im/vector-web/pull/1610)
|
||||
* Don't use SdkConfig
|
||||
[\#1609](https://github.com/vector-im/vector-web/pull/1609)
|
||||
* serve config.json statically instead of bundling it
|
||||
[\#1516](https://github.com/vector-im/vector-web/pull/1516)
|
||||
|
||||
Changes in [0.7.3](https://github.com/vector-im/vector-web/releases/tag/v0.7.3) (2016-06-03)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.2...v0.7.3)
|
||||
|
||||
* Update to react-sdk 0.6.3
|
||||
|
||||
Changes in [0.7.2](https://github.com/vector-im/vector-web/releases/tag/v0.7.2) (2016-06-02)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.1...v0.7.2)
|
||||
|
||||
* Correctly bump the dep on new matrix-js-sdk and matrix-react-sdk
|
||||
|
||||
Changes in [0.7.1](https://github.com/vector-im/vector-web/releases/tag/v0.7.1) (2016-06-02)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.7.0...v0.7.1)
|
||||
|
||||
* Fix accidentally committed local changes to the default config.json (doh!)
|
||||
|
||||
Changes in [0.7.0](https://github.com/vector-im/vector-web/releases/tag/v0.7.0) (2016-06-02)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.6.1...v0.7.0)
|
||||
|
||||
* Update to matrix-react-sdk 0.6.0 - see
|
||||
[changelog](https://github.com/matrix-org/matrix-react-sdk/blob/v0.6.0/CHANGELOG.md)
|
||||
* Style selection color.
|
||||
[\#1557](https://github.com/vector-im/vector-web/pull/1557)
|
||||
* Fix NPE when loading the Settings page which infini-spinnered
|
||||
[\#1518](https://github.com/vector-im/vector-web/pull/1518)
|
||||
* Add option to enable email notifications
|
||||
[\#1469](https://github.com/vector-im/vector-web/pull/1469)
|
||||
|
||||
Changes in [0.6.1](https://github.com/vector-im/vector-web/releases/tag/v0.6.1) (2016-04-22)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.6.0...v0.6.1)
|
||||
|
||||
* Update to matrix-react-sdk 0.5.2 - see
|
||||
[changelog](https://github.com/matrix-org/matrix-react-sdk/blob/v0.5.2/CHANGELOG.md)
|
||||
* Don't relayout scrollpanels every time something changes
|
||||
[\#1438](https://github.com/vector-im/vector-web/pull/1438)
|
||||
* Include react-addons-perf for non-production builds
|
||||
[\#1431](https://github.com/vector-im/vector-web/pull/1431)
|
||||
|
||||
Changes in [0.6.0](https://github.com/vector-im/vector-web/releases/tag/v0.6.0) (2016-04-19)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.5.0...v0.6.0)
|
||||
|
||||
* Matthew/design tweaks
|
||||
[\#1402](https://github.com/vector-im/vector-web/pull/1402)
|
||||
* Improve handling of notification rules we can't parse
|
||||
[\#1399](https://github.com/vector-im/vector-web/pull/1399)
|
||||
* Do less mangling of jenkins builds
|
||||
[\#1391](https://github.com/vector-im/vector-web/pull/1391)
|
||||
* Start Notifications component refactor
|
||||
[\#1386](https://github.com/vector-im/vector-web/pull/1386)
|
||||
* make the UI fadable to help with decluttering
|
||||
[\#1376](https://github.com/vector-im/vector-web/pull/1376)
|
||||
* Get and display a user's pushers in settings
|
||||
[\#1374](https://github.com/vector-im/vector-web/pull/1374)
|
||||
* URL previewing support
|
||||
[\#1343](https://github.com/vector-im/vector-web/pull/1343)
|
||||
* 😄 Emoji autocomplete and unicode emoji to image conversion using emojione.
|
||||
[\#1332](https://github.com/vector-im/vector-web/pull/1332)
|
||||
* Show full-size avatar on MemberInfo avatar click
|
||||
[\#1340](https://github.com/vector-im/vector-web/pull/1340)
|
||||
* Numerous other changes via [matrix-react-sdk 0.5.1](https://github.com/matrix-org/matrix-react-sdk/blob/v0.5.1/CHANGELOG.md)
|
||||
|
||||
Changes in [0.5.0](https://github.com/vector-im/vector-web/releases/tag/v0.5.0) (2016-03-30)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.4.1...v0.5.0)
|
||||
|
||||
* Prettier, animated placeholder :D
|
||||
[\#1292](https://github.com/vector-im/vector-web/pull/1292)
|
||||
(Disabled for now due to high CPU usage)
|
||||
* RoomDirectory: use SimpleRoomHeader instead of RoomHeader
|
||||
[\#1307](https://github.com/vector-im/vector-web/pull/1307)
|
||||
* Tell webpack not to parse the highlight.js languages
|
||||
[\#1277](https://github.com/vector-im/vector-web/pull/1277)
|
||||
* CSS for https://github.com/matrix-org/matrix-react-sdk/pull/247
|
||||
[\#1249](https://github.com/vector-im/vector-web/pull/1249)
|
||||
* URI-decode the hash-fragment
|
||||
[\#1254](https://github.com/vector-im/vector-web/pull/1254)
|
||||
|
||||
Changes in [0.4.1](https://github.com/vector-im/vector-web/releases/tag/v0.4.1) (2016-03-23)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.4.0...v0.4.1)
|
||||
* Update to matrix-react-sdk 0.3.1; see
|
||||
https://github.com/matrix-org/matrix-react-sdk/blob/v0.3.1/CHANGELOG.md
|
||||
(Disables debug logging)
|
||||
|
||||
Changes in [0.4.0](https://github.com/vector-im/vector-web/releases/tag/v0.4.0) (2016-03-23)
|
||||
============================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/vector-web/compare/v0.3.0...v0.4.0)
|
||||
|
||||
* Update to matrix-react-sdk 0.3.0; see
|
||||
https://github.com/matrix-org/matrix-react-sdk/blob/master/CHANGELOG.md
|
||||
|
||||
Other changes
|
||||
* permalink button
|
||||
[\#1232](https://github.com/vector-im/vector-web/pull/1232)
|
||||
* make senderprofiles clickable
|
||||
[\#1191](https://github.com/vector-im/vector-web/pull/1191)
|
||||
* fix notif spam when logging in from a guest session by correctly logging out
|
||||
first.
|
||||
[\#1180](https://github.com/vector-im/vector-web/pull/1180)
|
||||
* use new start_login_from_guest dispatch for cancellable logins from guest
|
||||
accounts
|
||||
[\#1165](https://github.com/vector-im/vector-web/pull/1165)
|
||||
* Use then() chaining rather than manual callbacks
|
||||
[\#1171](https://github.com/vector-im/vector-web/pull/1171)
|
||||
* Remove trailing whitespace
|
||||
[\#1163](https://github.com/vector-im/vector-web/pull/1163)
|
||||
* Update the actions of default rules instead of overriding.
|
||||
[\#1037](https://github.com/vector-im/vector-web/pull/1037)
|
||||
* Update README to include `npm install` in react-sdk
|
||||
[\#1137](https://github.com/vector-im/vector-web/pull/1137)
|
||||
|
||||
Changes in vector v0.3.0 (2016-03-11)
|
||||
======================================
|
||||
* Lots of new bug fixes and updates
|
||||
|
||||
Changes in vector v0.2.0 (2016-02-24)
|
||||
======================================
|
||||
* Refactor of matrix-react-sdk and vector to remove separation between views and
|
||||
controllers
|
||||
* Temporarily break the layering abstraction between vector and matrix-react-sdk
|
||||
for expedience in developing vector.
|
||||
* Vast numbers of new features, including read receipts, read-up-to markers,
|
||||
updated look and feel, search, new room and user settings, and email invites.
|
||||
|
||||
Changes in vector v0.1.2 (2015-10-28)
|
||||
======================================
|
||||
* Support Room Avatars
|
||||
* Fullscreen video calls
|
||||
* Mute mic in VoIP calls
|
||||
* Fix bug with multiple desktop notifications
|
||||
* Context menu on messages
|
||||
* Better hover-over on member list
|
||||
* Support CAS auth
|
||||
* Many other bug fixes
|
||||
|
||||
Changes in vector v0.1.1 (2015-08-10)
|
||||
======================================
|
||||
|
||||
* Support logging in with an email address
|
||||
* Use the Vector identity server
|
||||
* Fix a bug where the client was not stopped properly on logout
|
||||
* Fix bugs where field values would be forgotten if login or registration failed
|
||||
* Improve URL bar navigation
|
||||
* Add explanatory help text on advanced server options
|
||||
* Fix a bug which caused execptions on malformed VoIP invitations
|
||||
* Remove superfluous scrollbars on Firefox
|
||||
* Numerous CSS fixes
|
||||
* Improved accessibility
|
||||
* Support command-click / middle click to open image in a new tab
|
||||
* Improved room directory
|
||||
* Fix display of text with many combining unicode points
|
||||
|
||||
Changes in vector v0.1.0 (2015-08-10)
|
||||
======================================
|
||||
Initial release
|
||||
4
CONTRIBUTING.rst
Normal file
4
CONTRIBUTING.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
Contributing code to Vector
|
||||
===========================
|
||||
|
||||
Vector follows the same pattern as https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst
|
||||
321
README.md
321
README.md
@@ -1,148 +1,231 @@
|
||||
matrix-react-sdk
|
||||
================
|
||||
Vector/Web
|
||||
==========
|
||||
|
||||
This is a react-based SDK for inserting a Matrix chat/voip client into a web page.
|
||||
It provides reusable and customisable UI components backed by the matrix-js-sdk.
|
||||
Vector is a Matrix web client built using the Matrix React SDK (https://github.com/matrix-org/matrix-react-sdk).
|
||||
|
||||
Getting started with the trivial example
|
||||
========================================
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
The easiest way to test Vector is to just use the hosted copy at https://vector.im/beta.
|
||||
The develop branch is continuously deployed by Jenkins at https://vector.im/develop for
|
||||
those who like living dangerously.
|
||||
|
||||
To host your own copy of Vector, the quickest bet is to use a pre-built released version
|
||||
of Vector:
|
||||
|
||||
1. Download the latest version from https://vector.im/packages/
|
||||
1. Untar the tarball on your web server
|
||||
1. Move (or symlink) the vector-x.x.x directory to an appropriate name
|
||||
1. If desired, copy `config.sample.json` to `config.json` and edit it
|
||||
as desired. See below for details.
|
||||
1. Enter the URL into your browser and log into vector!
|
||||
|
||||
Building From Source
|
||||
====================
|
||||
|
||||
Vector is a modular webapp built with modern ES6 and requires a npm build system to build.
|
||||
|
||||
1. Install or update `node.js` so that your `npm` is at least at version `2.0.0`
|
||||
2. Clone the repo: `git clone https://github.com/matrix-org/matrix-react-sdk.git`
|
||||
3. Switch to the SDK directory: `cd matrix-react-sdk`
|
||||
4. Install the prerequisites: `npm install`
|
||||
5. Switch to the example directory: `cd examples/trivial`
|
||||
6. Install the example app prerequisites: `npm install`
|
||||
7. Build the example and start a server: `npm start`
|
||||
1. Clone the repo: `git clone https://github.com/vector-im/vector-web.git`
|
||||
1. Switch to the vector directory: `cd vector-web`
|
||||
1. Install the prerequisites: `npm install`
|
||||
1. If you are using the `develop` branch of vector, you will probably need to
|
||||
rebuild one of the dependencies, due to https://github.com/npm/npm/issues/3055:
|
||||
`(cd node_modules/matrix-react-sdk && npm install)`
|
||||
1. Configure the app by copying `config.sample.json` to `config.json` and modifying
|
||||
it (see below for details)
|
||||
1. `npm run package` to build a tarball to deploy. Untaring this file will give
|
||||
a version-specific directory containing all the files that need to go on your
|
||||
web server.
|
||||
|
||||
Now open http://127.0.0.1:8080/ in your browser to see your newly built
|
||||
Matrix client.
|
||||
Note that `npm run package` is not supported on Windows, so Windows users can run `npm
|
||||
run build`, which will build all the necessary files into the `vector`
|
||||
directory. The version of Vector will not appear in Settings without
|
||||
using the package script. You can then mount the vector directory on your
|
||||
webserver to actually serve up the app, which is entirely static content.
|
||||
|
||||
Using the example app for development
|
||||
=====================================
|
||||
config.json
|
||||
===========
|
||||
|
||||
To work on the CSS and Javascript and have the bundle files update as you
|
||||
change the source files, you'll need to do two extra things:
|
||||
You can configure the app by copying `vector/config.sample.json` to
|
||||
`vector/config.json` and customising it:
|
||||
|
||||
1. Link the react sdk package into the example:
|
||||
`cd matrix-react-sdk/examples/trivial; npm link ../../`
|
||||
2. Start a watcher for the CSS files:
|
||||
`cd matrix-react-sdk; npm run start:css`
|
||||
1. `default_hs_url` is the default home server url.
|
||||
1. `default_is_url` is the default identity server url (this is the server used
|
||||
for verifying third party identifiers like email addresses). If this is blank,
|
||||
registering with an email address, adding an email address to your account,
|
||||
or inviting users via email address will not work. Matrix identity servers are
|
||||
very simple web services which map third party identifiers (currently only email
|
||||
addresses) to matrix IDs: see http://matrix.org/docs/spec/identity_service/unstable.html
|
||||
for more details. Currently the only public matrix identity servers are https://matrix.org
|
||||
and https://vector.im. In future identity servers will be decentralised.
|
||||
|
||||
Note that you may need to restart the CSS builder if you add a new file. Note
|
||||
that `npm start` builds debug versions of the javascript and CSS, which are
|
||||
much larger than the production versions build by the `npm run build` commands.
|
||||
|
||||
IMPORTANT: If you customise components in your application (and hence require
|
||||
react from your app) you must be sure to:
|
||||
|
||||
1. Make your app depend on react directly
|
||||
2. If you `npm link` matrix-react-sdk, manually remove the 'react' directory
|
||||
from matrix-react-sdk's `node_modules` folder, otherwise browserify will
|
||||
pull in both copies of react which causes the app to break.
|
||||
|
||||
How to customise the SDK
|
||||
Running as a Desktop app
|
||||
========================
|
||||
|
||||
The matrix-react-sdk provides well-defined reusable UI components which may be
|
||||
customised/replaced by the developer to build into an app. A set of consistent
|
||||
UI components (View + CSS classes) is called a 'skin' - currently the SDK
|
||||
provides a very vanilla whitelabelled 'base skin'. In future the SDK could
|
||||
provide alternative skins (probably by extending the base skin) that provide more
|
||||
specific look and feels (e.g. "IRC-style", "Skype-style") etc. However, unlike
|
||||
Wordpress themes and similar, we don't normally expect app developers to define
|
||||
reusable skins. Instead you just go and incorporate your view customisations
|
||||
into your actual app.
|
||||
In future we'll do an official distribution of Vector as an desktop app. Meanwhile,
|
||||
there are a few options:
|
||||
|
||||
The SDK uses the 'atomic' design pattern as seen at http://patternlab.io to
|
||||
encourage a very modular and reusable architecture, making it easy to
|
||||
customise and use UI widgets independently of the rest of the SDK and your app.
|
||||
In practice this means:
|
||||
@asdf:matrix.org points out that you can use nativefier and it just works(tm):
|
||||
|
||||
* The UI of the app is strictly split up into a hierarchy of components.
|
||||
|
||||
* Each component has its own:
|
||||
* View object defined as a React javascript class containing embedded
|
||||
HTML expressed in React's JSX notation.
|
||||
* CSS file, which defines the styling specific to that component.
|
||||
|
||||
* Components are loosely grouped into the 5 levels outlined by atomic design:
|
||||
* atoms: fundamental building blocks (e.g. a timestamp tag)
|
||||
* molecules: "group of atoms which functions together as a unit"
|
||||
(e.g. a message in a chat timeline)
|
||||
* organisms: "groups of molecules (and atoms) which form a distinct section
|
||||
of a UI" (e.g. a view of a chat room)
|
||||
* templates: "a reusable configuration of organisms" - used to combine and
|
||||
style organisms into a well-defined global look and feel
|
||||
* pages: specific instances of templates.
|
||||
```
|
||||
sudo npm install nativefier -g
|
||||
nativefier https://vector.im/beta/
|
||||
```
|
||||
|
||||
Good separation between the components is maintained by adopting various best
|
||||
practices that anyone working with the SDK needs to be be aware of and uphold:
|
||||
krisa has a dedicated electron project at https://github.com/krisak/vector-electron-desktop
|
||||
(although you should swap out the 'vector' folder for the latest vector tarball you want to run)
|
||||
|
||||
* Views are named with upper camel case (e.g. molecules/MessageTile.js)
|
||||
There's also a (much) older electron distribution at https://github.com/stevenhammerton/vector-desktop
|
||||
|
||||
* The view's CSS file MUST have the same name (e.g. molecules/MessageTile.css)
|
||||
|
||||
* Per-view CSS is optional - it could choose to inherit all its styling from
|
||||
the context of the rest of the app, although this is unusual for any but
|
||||
the simplest atoms and molecules.
|
||||
Development
|
||||
===========
|
||||
|
||||
* The view MUST *only* refer to the CSS rules defined in its own CSS file.
|
||||
'Stealing' styling information from other components (including parents)
|
||||
is not cool, as it breaks the independence of the components.
|
||||
Before attempting to develop on Vector you **must** read the developer guide
|
||||
for `matrix-react-sdk` at https://github.com/matrix-org/matrix-react-sdk, which
|
||||
also defines the design, architecture and style for Vector too.
|
||||
|
||||
* CSS classes are named with an app-specific namespacing prefix to try to avoid
|
||||
CSS collisions. The base skin shipped by Matrix.org with the matrix-react-sdk
|
||||
uses the naming prefix "mx_". A company called Yoyodyne Inc might use a
|
||||
prefix like "yy_" for its app-specific classes.
|
||||
The idea of Vector is to be a relatively lightweight "skin" of customisations on
|
||||
top of the underlying `matrix-react-sdk`. `matrix-react-sdk` provides both the
|
||||
higher and lower level React components useful for building Matrix communication
|
||||
apps using React.
|
||||
|
||||
* CSS classes use upper camel case when they describe React components - e.g.
|
||||
.mx_MessageTile is the selector for the CSS applied to a MessageTile view.
|
||||
After creating a new component you must run `npm run reskindex` to regenerate
|
||||
the `component-index.js` for the app (used in future for skinning)
|
||||
|
||||
* CSS classes for DOM elements within a view which aren't components are named
|
||||
by appending a lower camel case identifier to the view's class name - e.g.
|
||||
.mx_MessageTile_randomDiv is how you'd name the class of an arbitrary div
|
||||
within the MessageTile view.
|
||||
**However, as of July 2016 this layering abstraction is broken due to rapid
|
||||
development on Vector forcing `matrix-react-sdk` to move fast at the expense of
|
||||
maintaining a clear abstraction between the two.** Hacking on Vector inevitably
|
||||
means hacking equally on `matrix-react-sdk`, and there are bits of
|
||||
`matrix-react-sdk` behaviour incorrectly residing in the `vector-web` project
|
||||
(e.g. matrix-react-sdk specific CSS), and a bunch of Vector specific behaviour
|
||||
in the `matrix-react-sdk` (grep for Vector). This separation problem will be
|
||||
solved asap once development on Vector (and thus matrix-react-sdk) has
|
||||
stabilised. Until then, the two projects should basically be considered as a
|
||||
single unit. In particular, `matrix-react-sdk` issues are currently filed
|
||||
against `vector-web` in github.
|
||||
|
||||
* We deliberately use vanilla CSS 3.0 to avoid adding any more magic
|
||||
dependencies into the mix than we already have. App developers are welcome
|
||||
to use whatever floats their boat however.
|
||||
Please note that Vector is intended to run correctly without access to the public
|
||||
internet. So please don't depend on resources (JS libs, CSS, images, fonts)
|
||||
hosted by external CDNs or servers but instead please package all dependencies
|
||||
into Vector itself.
|
||||
|
||||
* The CSS for a component can however override the rules for child components.
|
||||
For instance, .mx_RoomList .mx_RoomTile {} would be the selector to override
|
||||
styles of RoomTiles when viewed in the context of a RoomList view.
|
||||
Overrides *must* be scoped to the View's CSS class - i.e. don't just define
|
||||
.mx_RoomTile {} in RoomList.css - only RoomTile.css is allowed to define its
|
||||
own CSS. Instead, say .mx_RoomList .mx_RoomTile {} to scope the override
|
||||
only to the context of RoomList views. N.B. overrides should be relatively
|
||||
rare as in general CSS inheritence should be enough.
|
||||
Setting up a dev environment
|
||||
============================
|
||||
|
||||
* Components should render only within the bounding box of their outermost DOM
|
||||
element. Page-absolute positioning and negative CSS margins and similar are
|
||||
generally not cool and stop the component from being reused easily in
|
||||
different places.
|
||||
Much of the functionality in Vector is actually in the `matrix-react-sdk` and
|
||||
`matrix-js-sdk` modules. It is possible to set these up in a way that makes it
|
||||
easy to track the `develop` branches in git and to make local changes without
|
||||
having to manually rebuild each time.
|
||||
|
||||
* We don't use the atomify library itself, as React already provides most
|
||||
of the modularity requirements it brings to the table.
|
||||
First clone and build `matrix-js-sdk`:
|
||||
|
||||
With all this in mind, here's how you go about skinning the react SDK UI
|
||||
components to embed a Matrix client into your app:
|
||||
1. `git clone git@github.com:matrix-org/matrix-js-sdk.git`
|
||||
1. `pushd matrix-js-sdk`
|
||||
1. `git checkout develop`
|
||||
1. `npm install`
|
||||
1. `npm install source-map-loader` # because webpack is made of fail (https://github.com/webpack/webpack/issues/1472)
|
||||
1. `popd`
|
||||
|
||||
* Create a new NPM project. Be sure to directly depend on react, (otherwise
|
||||
you can end up with two copies of react).
|
||||
* Create an index.js file that sets up react. Add require statements for
|
||||
React, the ComponentBroker and matrix-react-sdk and a call to Render
|
||||
the root React element as in the examples.
|
||||
* Create React classes for any custom components you wish to add. These
|
||||
can be based off the files in `views` in the `matrix-react-sdk` package,
|
||||
modifying the require() statement appropriately.
|
||||
You only need to copy files you want to customise.
|
||||
* Add a ComponentBroker.set() call for each of your custom components. These
|
||||
must come *before* `require("matrix-react-sdk")`.
|
||||
* Add a way to build your project: we suggest copying the browserify calls
|
||||
from the example projects, but you could use grunt or gulp.
|
||||
* Create an index.html file pulling in your compiled index.js file, the
|
||||
CSS bundle from matrix-react-sdk.
|
||||
Then similarly with `matrix-react-sdk`:
|
||||
|
||||
For more specific detail on any of these steps, look at the `custom` example in
|
||||
matrix-react-sdk/examples.
|
||||
1. `git clone git@github.com:matrix-org/matrix-react-sdk.git`
|
||||
1. `pushd matrix-react-sdk`
|
||||
1. `git checkout develop`
|
||||
1. `npm install`
|
||||
1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/`
|
||||
1. `popd`
|
||||
|
||||
Finally, build and start vector itself:
|
||||
|
||||
1. `git clone git@github.com:vector-im/vector-web.git`
|
||||
1. `cd vector-web`
|
||||
1. `git checkout develop`
|
||||
1. `npm install`
|
||||
1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/`
|
||||
1. `rm -r node_modules/matrix-react-sdk; ln -s ../../matrix-react-sdk node_modules/`
|
||||
1. `npm start`
|
||||
1. Wait a few seconds for the initial build to finish; you should see something like:
|
||||
|
||||
```
|
||||
Hash: b0af76309dd56d7275c8
|
||||
Version: webpack 1.12.14
|
||||
Time: 14533ms
|
||||
Asset Size Chunks Chunk Names
|
||||
bundle.js 4.2 MB 0 [emitted] main
|
||||
bundle.css 91.5 kB 0 [emitted] main
|
||||
bundle.js.map 5.29 MB 0 [emitted] main
|
||||
bundle.css.map 116 kB 0 [emitted] main
|
||||
+ 1013 hidden modules
|
||||
```
|
||||
Remember, the command will not terminate since it runs the web server
|
||||
and rebuilds source files when they change. This development server also
|
||||
disables caching, so do NOT use it in production.
|
||||
1. Open http://127.0.0.1:8080/ in your browser to see your newly built Vector.
|
||||
|
||||
When you make changes to `matrix-react-sdk`, you will need to run `npm run
|
||||
build` in the relevant directory. You can do this automatically by instead
|
||||
running `npm start` in the directory, to start a development builder which
|
||||
will watch for changes to the files and rebuild automatically.
|
||||
|
||||
If you add or remove any components from the Vector skin, you will need to rebuild
|
||||
the skin's index by running, `npm run reskindex`.
|
||||
|
||||
Filing issues
|
||||
=============
|
||||
|
||||
All issues for Vector-web and Matrix-react-sdk should be filed at
|
||||
https://github.com/matrix-org/matrix-react-sdk/issues
|
||||
|
||||
Triaging issues
|
||||
===============
|
||||
|
||||
Issues will be triaged by the core team using the following primary set of tags:
|
||||
|
||||
priority:
|
||||
P1: top priority; typically blocks releases.
|
||||
P2: one below that
|
||||
P3: non-urgent
|
||||
P4/P5: bluesky some day, who knows.
|
||||
|
||||
bug or feature:
|
||||
bug severity:
|
||||
* cosmetic - feature works functionally but UI/UX is broken.
|
||||
* critical - whole app doesn't work
|
||||
* major - entire feature doesn't work
|
||||
* minor - partially broken feature (but still usable)
|
||||
|
||||
* release blocker
|
||||
|
||||
* ui/ux (think of this as cosmetic)
|
||||
|
||||
* network (specific to network conditions)
|
||||
* platform (platform specific)
|
||||
|
||||
Enabling encryption
|
||||
===================
|
||||
|
||||
End-to-end encryption in Vector and Matrix is not yet considered ready for
|
||||
day-to-day use; it is experimental and should be considered only as a
|
||||
proof-of-concept. See https://matrix.org/jira/browse/SPEC-162 for an overview
|
||||
of the current progress.
|
||||
|
||||
Vector is built with support for end-to-end encryption by default.
|
||||
|
||||
To enable encryption for a room, type
|
||||
|
||||
```
|
||||
/encrypt on
|
||||
```
|
||||
|
||||
in the message bar in that room. Vector will then generate a set of keys, and
|
||||
encrypt all outgoing messages in that room. (Note that other people in that
|
||||
room will send messages in the clear unless they also `/encrypt on`.)
|
||||
|
||||
Note that historical encrypted messages cannot currently be decoded - history
|
||||
is therefore lost when the page is reloaded.
|
||||
|
||||
There is currently no visual indication of whether encryption is enabled for a
|
||||
room.
|
||||
|
||||
170
deploy/redeploy.py
Executable file
170
deploy/redeploy.py
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
import json, requests, tarfile, argparse, os, errno
|
||||
from urlparse import urljoin
|
||||
from flask import Flask, jsonify, request, abort
|
||||
app = Flask(__name__)
|
||||
|
||||
arg_jenkins_url, arg_extract_path, arg_should_clean, arg_symlink = (
|
||||
None, None, None, None
|
||||
)
|
||||
|
||||
def download_file(url):
|
||||
local_filename = url.split('/')[-1]
|
||||
r = requests.get(url, stream=True)
|
||||
with open(local_filename, 'wb') as f:
|
||||
for chunk in r.iter_content(chunk_size=1024):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
f.write(chunk)
|
||||
return local_filename
|
||||
|
||||
def untar_to(tarball, dest):
|
||||
with tarfile.open(tarball) as tar:
|
||||
tar.extractall(dest)
|
||||
|
||||
def create_symlink(source, linkname):
|
||||
try:
|
||||
os.symlink(source, linkname)
|
||||
except OSError, e:
|
||||
if e.errno == errno.EEXIST:
|
||||
# atomic modification
|
||||
os.symlink(source, linkname + ".tmp")
|
||||
os.rename(linkname + ".tmp", linkname)
|
||||
else:
|
||||
raise e
|
||||
|
||||
@app.route("/", methods=["POST"])
|
||||
def on_receive_jenkins_poke():
|
||||
# {
|
||||
# "name": "VectorWebDevelop",
|
||||
# "build": {
|
||||
# "number": 8
|
||||
# }
|
||||
# }
|
||||
incoming_json = request.get_json()
|
||||
if not incoming_json:
|
||||
abort(400, "No JSON provided!")
|
||||
return
|
||||
print("Incoming JSON: %s" % (incoming_json,))
|
||||
|
||||
job_name = incoming_json.get("name")
|
||||
if not isinstance(job_name, basestring):
|
||||
abort(400, "Bad job name: %s" % (job_name,))
|
||||
return
|
||||
|
||||
build_num = incoming_json.get("build", {}).get("number", 0)
|
||||
if not build_num or build_num <= 0 or not isinstance(build_num, int):
|
||||
abort(400, "Missing or bad build number")
|
||||
return
|
||||
|
||||
artifact_url = urljoin(
|
||||
arg_jenkins_url, "job/%s/%s/api/json" % (job_name, build_num)
|
||||
)
|
||||
artifact_response = requests.get(artifact_url).json()
|
||||
|
||||
# {
|
||||
# "actions": [],
|
||||
# "artifacts": [
|
||||
# {
|
||||
# "displayPath": "vector-043f6991a4ed-react-20f77d1224ef-js-0a7efe3e8bd5.tar.gz",
|
||||
# "fileName": "vector-043f6991a4ed-react-20f77d1224ef-js-0a7efe3e8bd5.tar.gz",
|
||||
# "relativePath": "vector-043f6991a4ed-react-20f77d1224ef-js-0a7efe3e8bd5.tar.gz"
|
||||
# }
|
||||
# ],
|
||||
# "building": false,
|
||||
# "description": null,
|
||||
# "displayName": "#11",
|
||||
# "duration": 137976,
|
||||
# "estimatedDuration": 132008,
|
||||
# "executor": null,
|
||||
# "fullDisplayName": "VectorWebDevelop #11",
|
||||
# "id": "11",
|
||||
# "keepLog": false,
|
||||
# "number": 11,
|
||||
# "queueId": 12254,
|
||||
# "result": "SUCCESS",
|
||||
# "timestamp": 1454432640079,
|
||||
# "url": "http://matrix.org/jenkins/job/VectorWebDevelop/11/",
|
||||
# "builtOn": "",
|
||||
# "changeSet": {},
|
||||
# "culprits": []
|
||||
# }
|
||||
if artifact_response.get("result") != "SUCCESS":
|
||||
abort(404, "Not deploying. Build was not marked as SUCCESS.")
|
||||
return
|
||||
|
||||
if len(artifact_response.get("artifacts", [])) != 1:
|
||||
abort(404, "Not deploying. Build has an unexpected number of artifacts.")
|
||||
return
|
||||
|
||||
tar_gz_path = artifact_response["artifacts"][0]["relativePath"]
|
||||
if not tar_gz_path.endswith(".tar.gz"):
|
||||
abort(404, "Not deploying. Artifact is not a .tar.gz file")
|
||||
return
|
||||
|
||||
tar_gz_url = urljoin(
|
||||
arg_jenkins_url, "job/%s/%s/artifact/%s" % (job_name, build_num, tar_gz_path)
|
||||
)
|
||||
|
||||
print("Retrieving .tar.gz file: %s" % tar_gz_url)
|
||||
filename = download_file(tar_gz_url)
|
||||
print("Downloaded file: %s" % filename)
|
||||
name_str = filename.replace(".tar.gz", "")
|
||||
untar_location = os.path.join(arg_extract_path, name_str)
|
||||
untar_to(filename, untar_location)
|
||||
|
||||
if arg_should_clean:
|
||||
os.remove(filename)
|
||||
|
||||
# stamp the version somewhere JS can get to it
|
||||
with open(os.path.join(untar_location, "vector/version"), "w") as stamp_file:
|
||||
stamp_file.write(name_str)
|
||||
|
||||
create_symlink(source=os.path.join(untar_location, "vector"), linkname=arg_symlink)
|
||||
|
||||
return jsonify({})
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser("Runs a Vector redeployment server.")
|
||||
parser.add_argument(
|
||||
"-j", "--jenkins", dest="jenkins", default="http://matrix.org/jenkins/", help=(
|
||||
"The base URL of the Jenkins web server. This will be hit to get the\
|
||||
built artifacts (the .gz file) for redeploying."
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--port", dest="port", default=4000, type=int, help=(
|
||||
"The port to listen on for requests from Jenkins."
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e", "--extract", dest="extract", default="./extracted", help=(
|
||||
"The location to extract .tar.gz files to."
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--clean", dest="clean", action="store_true", default=False, help=(
|
||||
"Remove .tar.gz files after they have been downloaded and extracted."
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--symlink", dest="symlink", default="./latest", help=(
|
||||
"Write a symlink to this location pointing to the extracted tarball. \
|
||||
New builds will keep overwriting this symlink. The symlink will point \
|
||||
to the /vector directory INSIDE the tarball."
|
||||
)
|
||||
)
|
||||
args = parser.parse_args()
|
||||
if args.jenkins.endswith("/"): # important for urljoin
|
||||
arg_jenkins_url = args.jenkins
|
||||
else:
|
||||
arg_jenkins_url = args.jenkins + "/"
|
||||
arg_extract_path = args.extract
|
||||
arg_should_clean = args.clean
|
||||
arg_symlink = args.symlink
|
||||
print(
|
||||
"Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s" %
|
||||
(args.port, arg_extract_path,
|
||||
" (clean after)" if arg_should_clean else "", arg_symlink, arg_jenkins_url)
|
||||
)
|
||||
app.run(host="0.0.0.0", port=args.port, debug=True)
|
||||
52
docs/conferencing.md
Normal file
52
docs/conferencing.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# VoIP Conferencing
|
||||
|
||||
This is a draft proposal for a naive voice/video conferencing implementation for
|
||||
Matrix clients. There are many possible conferencing architectures possible for
|
||||
Matrix (Multipoint Conferencing Unit (MCU); Stream Forwarding Unit (SFU); Peer-
|
||||
to-Peer mesh (P2P), etc; events shared in the group room; events shared 1:1;
|
||||
possibly even out-of-band signalling).
|
||||
|
||||
This is a starting point for a naive MCU implementation which could provide one
|
||||
possible Matrix-wide solution in future, which retains backwards compatibility
|
||||
with standard 1:1 calling.
|
||||
|
||||
* A client chooses to initiate a conference for a given room by starting a
|
||||
voice or video call with a 'conference focus' user. This is a virtual user
|
||||
(typically Application Service) which implements a conferencing bridge. It
|
||||
isn't defined how the client discovers or selects this user.
|
||||
|
||||
* The conference focus user MUST join the room in which the client has
|
||||
initiated the conference - this may require the client to invite the
|
||||
conference focus user to the room, depending on the room's `join_rules`. The
|
||||
conference focus user needs to be in the room to let the bridge eject users
|
||||
from the conference who have left the room in which it was initiated, and aid
|
||||
discovery of the conference by other users in the room. The bridge
|
||||
identifies the room to join based on the user ID by which it was invited.
|
||||
The format of this identifier is implementation dependent for now.
|
||||
|
||||
* If a client leaves the group chat room, they MUST be ejected from the
|
||||
conference. If a client leaves the 1:1 room with the conference focus user,
|
||||
they SHOULD be ejected from the conference.
|
||||
|
||||
* For now, rooms can contain multiple conference focus users - it's left to
|
||||
user or client implementation to select which to converge on. In future this
|
||||
could be mediated using a state event (e.g. `im.vector.call.mcu`), but we
|
||||
can't do that right now as by default normal users can't set arbitrary state
|
||||
events on a room.
|
||||
|
||||
* To participate in the conference, other clients initiates a standard 1:1
|
||||
voice or video call to the conference focus user.
|
||||
|
||||
* For best UX, clients SHOULD show the ongoing voice/video call in the UI
|
||||
context of the group room rather than 1:1 with the focus user. If a client
|
||||
recognises a conference user present in the room, it MAY chose to highlight
|
||||
this in the UI (e.g. with a "conference ongoing" notification, to aid
|
||||
discovery). Clients MAY hide the 1:1 room with the focus user (although in
|
||||
future this room could be used for floor control or other direct
|
||||
communication with the conference focus)
|
||||
|
||||
* When all users have left the conference, the 'conference focus' user SHOULD
|
||||
leave the room.
|
||||
|
||||
* If a conference focus user joins a room but does not receive a 1:1 voice or
|
||||
video call, it SHOULD time out after a period of time and leave the room.
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MTextTileController = require("matrix-react-sdk/src/controllers/molecules/MTextTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MTextTile',
|
||||
mixins: [MTextTileController],
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
return (
|
||||
<span ref="content" className="mx_MTextTile mx_MessageTile_content" onClick={this.onClick}>
|
||||
{content.body}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
|
||||
onClick: function(ev) {
|
||||
global.alert(this.props.mxEvent.getContent().body);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
matrix-react-example
|
||||
====================
|
||||
|
||||
An example of how to use the Matrix React SDK to build a more customised app
|
||||
@@ -1,12 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" style="height: 100%; overflow: hidden">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Matrix React SDK Custom Example</title>
|
||||
</head>
|
||||
<body style="height: 100%; ">
|
||||
<section id="matrixchat" style="height: 100%; "></section>
|
||||
<script src="bundle.js"></script>
|
||||
<link rel="stylesheet" href="node_modules/matrix-react-sdk/bundle.css">
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// Remember to make your project depend on react directly as soon as
|
||||
// you add a require('react') to any file in your project. Do not rely
|
||||
// on react being pulled in via matrix-react-sdk: browserify breaks
|
||||
// horribly in this situation and can end up pulling in multiple copies
|
||||
// of react.
|
||||
var React = require("react");
|
||||
|
||||
// We pull in the component broker first, separately, as we need to replace
|
||||
// components before the SDK loads.
|
||||
var ComponentBroker = require("matrix-react-sdk/src/ComponentBroker");
|
||||
|
||||
var CustomMTextTile = require('./CustomMTextTile');
|
||||
|
||||
ComponentBroker.set('molecules/MTextTile', CustomMTextTile);
|
||||
|
||||
var MatrixReactSdk = require("matrix-react-sdk");
|
||||
//var MatrixReactSdk = require("../../src/index");
|
||||
|
||||
React.render(
|
||||
<MatrixReactSdk.MatrixChat />,
|
||||
document.getElementById('matrixchat')
|
||||
);
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "matrix-react-example",
|
||||
"version": "0.0.1",
|
||||
"description": "Example usage of matrix-react-sdk",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/matrix-org/matrix-react-sdk"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"browserify": "^10.2.3",
|
||||
"envify": "^3.4.0",
|
||||
"http-server": "^0.8.0",
|
||||
"matrix-react-sdk": "../../",
|
||||
"npm-css": "^0.2.3",
|
||||
"parallelshell": "^1.2.0",
|
||||
"reactify": "^1.1.1",
|
||||
"uglify-js": "^2.4.23",
|
||||
"watchify": "^3.2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "browserify -t [ envify --NODE_ENV production ] -g reactify index.js | uglifyjs -c -m -o bundle.js",
|
||||
"start": "parallelshell 'watchify -v -d -g reactify index.js -o bundle.js' 'http-server'"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^0.13.3"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
matrix-react-example
|
||||
====================
|
||||
|
||||
A simple example of how to use the Matrix React SDK
|
||||
@@ -1,12 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" style="height: 100%; overflow: hidden">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Matrix React SDK Example</title>
|
||||
</head>
|
||||
<body style="height: 100%;">
|
||||
<section id="matrixchat" style="height: 100%;"></section>
|
||||
<script src="bundle.js"></script>
|
||||
<link rel="stylesheet" href="node_modules/matrix-react-sdk/bundle.css">
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require("react");
|
||||
// In normal usage of the module:
|
||||
//var MatrixReactSdk = require("matrix-react-sdk");
|
||||
// Or to import the source directly from the file system:
|
||||
// (This is useful for debugging the SDK as it seems source
|
||||
// maps cannot pass through two stages).
|
||||
var MatrixReactSdk = require("../../src/index");
|
||||
|
||||
// Here, we do some crude URL analysis to allow
|
||||
// deep-linking. We only support registration
|
||||
// deep-links in this example.
|
||||
function routeUrl(location) {
|
||||
if (location.hash.indexOf('#/register') == 0) {
|
||||
var hashparts = location.hash.split('?');
|
||||
var params = {};
|
||||
if (hashparts.length == 2) {
|
||||
var pairs = hashparts[1].split('&');
|
||||
for (var i = 0; i < pairs.length; ++i) {
|
||||
var parts = pairs[i].split('=');
|
||||
if (parts.length != 2) continue;
|
||||
params[decodeURIComponent(parts[0])] = decodeURIComponent(parts[1]);
|
||||
}
|
||||
}
|
||||
window.matrixChat.showScreen('register', params);
|
||||
}
|
||||
}
|
||||
|
||||
var loaded = false;
|
||||
|
||||
window.onload = function() {
|
||||
routeUrl(window.location);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
// This will be called whenever the SDK changes screens,
|
||||
// so a web page can update the URL bar appropriately.
|
||||
var onNewScreen = function(screen) {
|
||||
if (!loaded) return;
|
||||
window.location.hash = '#/'+screen;
|
||||
}
|
||||
|
||||
// We use this to work out what URL the SDK should
|
||||
// pass through when registering to allow the user to
|
||||
// click back to the client having registered.
|
||||
// It's up to us to recognise if we're loaded with
|
||||
// this URL and tell MatrixClient to resume registration.
|
||||
var makeRegistrationUrl = function() {
|
||||
return window.location.protocol + '//' +
|
||||
window.location.host +
|
||||
window.location.pathname +
|
||||
'#/register';
|
||||
}
|
||||
|
||||
window.matrixChat = React.render(
|
||||
<MatrixReactSdk.MatrixChat onNewScreen={onNewScreen} registrationUrl={makeRegistrationUrl()} />,
|
||||
document.getElementById('matrixchat')
|
||||
);
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "matrix-react-example",
|
||||
"version": "0.0.1",
|
||||
"description": "Example usage of matrix-react-sdk",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/matrix-org/matrix-react-sdk"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"browserify": "^10.2.3",
|
||||
"envify": "^3.4.0",
|
||||
"http-server": "^0.8.0",
|
||||
"matrix-react-sdk": "../../",
|
||||
"parallelshell": "^1.2.0",
|
||||
"reactify": "^1.1.1",
|
||||
"uglify-js": "^2.4.23",
|
||||
"watchify": "^3.2.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "browserify -t [ envify --NODE_ENV production ] -t reactify index.js | uglifyjs -c -m -o bundle.js",
|
||||
"start": "parallelshell 'watchify -v -d -t reactify index.js -o bundle.js' 'http-server'"
|
||||
}
|
||||
}
|
||||
33
jenkins.sh
Executable file
33
jenkins.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export NVM_DIR="/home/jenkins/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||
nvm use 4
|
||||
|
||||
set -x
|
||||
|
||||
npm install
|
||||
|
||||
# we may be using a dev branch of react-sdk, in which case we need to build it
|
||||
(cd node_modules/matrix-react-sdk && npm run build)
|
||||
|
||||
# run the mocha tests
|
||||
npm run test
|
||||
|
||||
# build our artifacts; dumps them in ./vector
|
||||
npm run build:dev
|
||||
|
||||
# gzip up ./vector
|
||||
rm vector-*.tar.gz || true # rm previous artifacts without failing if it doesn't exist
|
||||
|
||||
# node_modules deps from 'npm install' don't have a .git dir so can't
|
||||
# rev-parse; but they do set the commit in package.json under 'gitHead' which
|
||||
# we're grabbing here.
|
||||
REACT_SHA=$(grep 'gitHead' node_modules/matrix-react-sdk/package.json | cut -d \" -f 4 | head -c 12)
|
||||
JSSDK_SHA=$(grep 'gitHead' node_modules/matrix-js-sdk/package.json | cut -d \" -f 4 | head -c 12)
|
||||
|
||||
VECTOR_SHA=$(git rev-parse --short=12 HEAD) # use the ACTUAL SHA rather than assume develop
|
||||
|
||||
tar -zcvhf vector-$VECTOR_SHA-react-$REACT_SHA-js-$JSSDK_SHA.tar.gz vector #g[z]ip, [c]reate archive, [v]erbose, [f]ilename, [h]ard-dereference (do not archive symlinks)
|
||||
142
karma.conf.js
Normal file
142
karma.conf.js
Normal file
@@ -0,0 +1,142 @@
|
||||
// karma.conf.js - the config file for karma, which runs our tests.
|
||||
|
||||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
|
||||
/*
|
||||
* We use webpack to build our tests. It's a pain to have to wait for webpack
|
||||
* to build everything; however it's the easiest way to load our dependencies
|
||||
* from node_modules.
|
||||
*
|
||||
* If you run karma in multi-run mode (with `npm run test:multi`), it will watch
|
||||
* the tests for changes, and webpack will rebuild using a cache. This is much quicker
|
||||
* than a clean rebuild.
|
||||
*/
|
||||
|
||||
// the name of the test file. By default, a special file which runs all tests.
|
||||
var testFile = process.env.KARMA_TEST_FILE || 'test/all-tests.js';
|
||||
|
||||
process.env.PHANTOMJS_BIN = 'node_modules/.bin/phantomjs';
|
||||
process.env.Q_DEBUG = 1;
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['mocha'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'node_modules/babel-polyfill/browser.js',
|
||||
testFile,
|
||||
{pattern: 'vector/img/*', watched: false, included: false, served: true, nocache: false},
|
||||
],
|
||||
|
||||
// redirect img links to the karma server
|
||||
proxies: {
|
||||
"/img/": "/base/vector/img/",
|
||||
},
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors:
|
||||
// https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
'test/**/*.js': ['webpack', 'sourcemap']
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress', 'junit'],
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR ||
|
||||
// config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file
|
||||
// changes
|
||||
autoWatch: true,
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers:
|
||||
// https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: [
|
||||
'Chrome',
|
||||
//'PhantomJS',
|
||||
],
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
// singleRun: false,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity,
|
||||
|
||||
junitReporter: {
|
||||
outputDir: 'karma-reports',
|
||||
},
|
||||
|
||||
webpack: {
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.json$/, loader: "json" },
|
||||
{
|
||||
test: /\.js$/, loader: "babel",
|
||||
include: [path.resolve('./src'),
|
||||
path.resolve('./test'),
|
||||
],
|
||||
query: {
|
||||
// we're using babel 5, for consistency with
|
||||
// the release build, which doesn't use the
|
||||
// presets.
|
||||
// presets: ['react', 'es2015'],
|
||||
},
|
||||
},
|
||||
],
|
||||
noParse: [
|
||||
// don't parse the languages within highlight.js. They
|
||||
// cause stack overflows
|
||||
// (https://github.com/webpack/webpack/issues/1721), and
|
||||
// there is no need for webpack to parse them - they can
|
||||
// just be included as-is.
|
||||
/highlight\.js\/lib\/languages/,
|
||||
|
||||
// also disable parsing for sinon, because it
|
||||
// tries to do voodoo with 'require' which upsets
|
||||
// webpack (https://github.com/webpack/webpack/issues/304)
|
||||
/sinon\/pkg\/sinon\.js$/,
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
// alias any requires to the react module to the one in our path, otherwise
|
||||
// we tend to get the react source included twice when using npm link.
|
||||
react: path.resolve('./node_modules/react'),
|
||||
|
||||
// same goes for js-sdk
|
||||
"matrix-js-sdk": path.resolve('./node_modules/matrix-js-sdk'),
|
||||
|
||||
sinon: 'sinon/pkg/sinon.js',
|
||||
},
|
||||
root: [
|
||||
path.resolve('./src'),
|
||||
path.resolve('./test'),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// olm may not be installed, so avoid webpack warnings by
|
||||
// ignoring it.
|
||||
new webpack.IgnorePlugin(/^olm$/),
|
||||
],
|
||||
devtool: 'inline-source-map',
|
||||
},
|
||||
});
|
||||
};
|
||||
96
package.json
96
package.json
@@ -1,40 +1,92 @@
|
||||
{
|
||||
"name": "matrix-react-sdk",
|
||||
"version": "0.0.1",
|
||||
"description": "SDK for matrix.org using React",
|
||||
"name": "vector-web",
|
||||
"version": "0.7.4",
|
||||
"description": "Vector webapp",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/matrix-org/matrix-react-sdk"
|
||||
"url": "https://github.com/vector-im/vector-web"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"main": "src/index.js",
|
||||
"style": "bundle.css",
|
||||
"matrix-react-parent": "matrix-react-sdk",
|
||||
"scripts": {
|
||||
"build:skins": "jsx skins build/skins",
|
||||
"build:logic": "jsx src build/src",
|
||||
"build:js": "npm run build:skins && npm run build:logic",
|
||||
"start:js": "jsx -w skins/base/views/ build --source-map-inline",
|
||||
"build:css": "catw 'skins/base/css/**/*.css' -o bundle.css -c uglifycss --no-watch",
|
||||
"start:css": "catw 'skins/base/css/**/*.css' -o bundle.css -v",
|
||||
"build": "npm run build:js && npm run build:css",
|
||||
"start": "parallelshell 'npm run start:js' 'npm run start:css'",
|
||||
"prepublish": "npm run build"
|
||||
"reskindex": "reskindex -h src/header",
|
||||
"build:emojione": "cpx \"node_modules/emojione/assets/svg/*\" vector/emojione/svg/",
|
||||
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
|
||||
"build:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css --no-watch",
|
||||
"build:compile": "babel --source-maps -d lib src",
|
||||
"build:bundle": "NODE_ENV=production webpack -p lib/vector/index.js vector/bundle.js",
|
||||
"build:bundle:dev": "NODE_ENV=production webpack --optimize-occurence-order lib/vector/index.js vector/bundle.js",
|
||||
"build:staticfiles": "scripts/staticfiles.js",
|
||||
"build": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle",
|
||||
"build:dev": "npm run build:staticfiles && npm run build:emojione && npm run build:css && npm run build:compile && npm run build:bundle:dev",
|
||||
"package": "scripts/package.sh",
|
||||
"start:emojione": "cpx \"node_modules/emojione/assets/svg/*\" vector/emojione/svg/ -w",
|
||||
"start:js": "webpack -w src/vector/index.js vector/bundle.js",
|
||||
"start:js:prod": "NODE_ENV=production webpack -w src/vector/index.js vector/bundle.js",
|
||||
"start:skins:css": "catw \"src/skins/vector/css/**/*.css\" -o vector/components.css",
|
||||
"//cache": "Note the -c 1 below due to https://code.google.com/p/chromium/issues/detail?id=508270",
|
||||
"start": "parallelshell \"npm run build:staticfiles\" \"npm run start:emojione\" \"npm run start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
|
||||
"start:prod": "parallelshell \"npm run build:staticfiles\" \"npm run start:emojione\" \"npm run start:js:prod\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
|
||||
"clean": "rimraf lib vector/olm.js vector/bundle.css vector/bundle.js vector/bundle.js.map vector/webpack.css* vector/emojione",
|
||||
"prepublish": "npm run build:css && npm run build:compile",
|
||||
"test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false",
|
||||
"test:multi": "karma start"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.5.0",
|
||||
"browser-request": "^0.3.3",
|
||||
"classnames": "^2.1.2",
|
||||
"draft-js": "^0.7.0",
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"filesize": "^3.1.2",
|
||||
"flux": "^2.0.3",
|
||||
"matrix-js-sdk": "0.1.1",
|
||||
"flux": "~2.0.3",
|
||||
"gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279",
|
||||
"gfm.css": "^1.1.1",
|
||||
"highlight.js": "^9.0.0",
|
||||
"linkifyjs": "^2.0.0-beta.4",
|
||||
"matrix-js-sdk": "0.5.5",
|
||||
"matrix-react-sdk": "0.6.4",
|
||||
"modernizr": "^3.1.0",
|
||||
"q": "^1.4.1",
|
||||
"react": "^0.13.3",
|
||||
"react-loader": "^1.4.0",
|
||||
"linkifyjs": "^2.0.0-beta.4"
|
||||
"react": "^15.2.1",
|
||||
"react-dnd": "^2.1.4",
|
||||
"react-dnd-html5-backend": "^2.1.2",
|
||||
"react-dom": "^15.2.1",
|
||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
||||
"sanitize-html": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel": "^5.8.23",
|
||||
"babel-core": "^5.8.25",
|
||||
"babel-loader": "^5.3.2",
|
||||
"catw": "^1.0.1",
|
||||
"parallelshell": "^1.1.1",
|
||||
"react-tools": "^0.13.3",
|
||||
"uglifycss": "0.0.15"
|
||||
"cpx": "^1.3.2",
|
||||
"css-raw-loader": "^0.1.1",
|
||||
"emojione": "^2.2.3",
|
||||
"expect": "^1.16.0",
|
||||
"fs-extra": "^0.30.0",
|
||||
"http-server": "^0.8.4",
|
||||
"json-loader": "^0.5.3",
|
||||
"karma": "^0.13.22",
|
||||
"karma-chrome-launcher": "^0.2.3",
|
||||
"karma-cli": "^0.1.2",
|
||||
"karma-junit-reporter": "^0.4.1",
|
||||
"karma-mocha": "^0.2.2",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^1.7.0",
|
||||
"mocha": "^2.4.5",
|
||||
"parallelshell": "^1.2.0",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"react-addons-perf": "^15.0",
|
||||
"react-addons-test-utils": "^15.0.1",
|
||||
"rimraf": "^2.4.3",
|
||||
"source-map-loader": "^0.1.5",
|
||||
"webpack": "^1.12.14"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"olm": "https://matrix.org/packages/npm/olm/olm-1.0.0.tgz"
|
||||
}
|
||||
}
|
||||
|
||||
12
release.sh
Executable file
12
release.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Script to perform a release of vector-web.
|
||||
#
|
||||
# Requires github-changelog-generator; to install, do
|
||||
# pip install git+https://github.com/matrix-org/github-changelog-generator.git
|
||||
|
||||
set -e
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
exec ./node_modules/matrix-js-sdk/release.sh -z "$@"
|
||||
124
scripts/issues-burndown.pl
Normal file
124
scripts/issues-burndown.pl
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use Net::GitHub;
|
||||
use DateTime;
|
||||
use DateTime::Format::ISO8601;
|
||||
|
||||
my $gh = Net::GitHub->new(
|
||||
login => 'ara4n', pass => 'secret'
|
||||
);
|
||||
|
||||
$gh->set_default_user_repo('vector-im', 'vector-web');
|
||||
|
||||
my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
|
||||
while ($gh->issue->has_next_page) {
|
||||
push @issues, $gh->issue->next_page;
|
||||
}
|
||||
|
||||
# we want:
|
||||
# day by day:
|
||||
# split by { open, closed }
|
||||
# split by { bug, feature, neither }
|
||||
# each split by { p1, p2, p3, p4, p5, unprioritised } <- priority
|
||||
# each split by { minor, major, critical, cosmetic, network, no-severity } <- severity
|
||||
# then split (with overlap between the groups) as { total, tag1, tag2, ... }?
|
||||
|
||||
# ...and then all over again split by milestone.
|
||||
|
||||
my $days = {};
|
||||
my $schema = {};
|
||||
my $now = DateTime->now();
|
||||
|
||||
foreach my $issue (@issues) {
|
||||
next if ($issue->{pull_request});
|
||||
|
||||
# use Data::Dumper;
|
||||
# print STDERR Dumper($issue);
|
||||
|
||||
my @label_list = map { $_->{name} } @{$issue->{labels}};
|
||||
my $labels = {};
|
||||
$labels->{$_} = 1 foreach (@label_list);
|
||||
$labels->{bug}++ if ($labels->{cosmetic} && !$labels->{bug} && !$labels->{feature});
|
||||
|
||||
my $extract_labels = sub {
|
||||
my $label = undef;
|
||||
foreach (@_) {
|
||||
$label ||= $_ if (delete $labels->{$_});
|
||||
}
|
||||
return $label;
|
||||
};
|
||||
|
||||
my $state = $issue->{state};
|
||||
my $type = &$extract_labels(qw(bug feature)) || "neither";
|
||||
my $priority = &$extract_labels(qw(p1 p2 p3 p4 p5)) || "unprioritised";
|
||||
my $severity = &$extract_labels(qw(minor major critical cosmetic network)) || "no-severity";
|
||||
|
||||
my $start = DateTime::Format::ISO8601->parse_datetime($issue->{created_at});
|
||||
|
||||
do {
|
||||
my $ymd = $start->ymd();
|
||||
|
||||
$days->{ $ymd }->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
$schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
foreach (keys %$labels) {
|
||||
$days->{ $ymd }->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
$schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
}
|
||||
|
||||
$start = $start->add(days => 1);
|
||||
} while (DateTime->compare($start, $now) < 0);
|
||||
|
||||
if ($state eq 'closed') {
|
||||
my $end = DateTime::Format::ISO8601->parse_datetime($issue->{closed_at});
|
||||
do {
|
||||
my $ymd = $end->ymd();
|
||||
|
||||
$days->{ $ymd }->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
$schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
foreach (keys %$labels) {
|
||||
$days->{ $ymd }->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
$schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
}
|
||||
|
||||
$end = $end->add(days => 1);
|
||||
} while (DateTime->compare($end, $now) < 0);
|
||||
}
|
||||
}
|
||||
|
||||
print "day,";
|
||||
foreach my $state (sort keys %{$schema}) {
|
||||
foreach my $type (grep { /^(bug|feature)$/ } sort keys %{$schema->{$state}}) {
|
||||
foreach my $priority (grep { /^(p1|p2)$/ } sort keys %{$schema->{$state}->{$type}}) {
|
||||
foreach my $severity (sort keys %{$schema->{$state}->{$type}->{$priority}}) {
|
||||
# foreach my $tag (sort keys %{$schema->{$state}->{$type}->{$priority}->{$severity}}) {
|
||||
# print "\"$type\n$priority\n$severity\n$tag\",";
|
||||
# }
|
||||
print "\"$state\n$type\n$priority\n$severity\",";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
|
||||
foreach my $day (sort keys %$days) {
|
||||
print "$day,";
|
||||
foreach my $state (sort keys %{$schema}) {
|
||||
foreach my $type (grep { /^(bug|feature)$/ } sort keys %{$schema->{$state}}) {
|
||||
foreach my $priority (grep { /^(p1|p2)$/ } sort keys %{$schema->{$state}->{$type}}) {
|
||||
foreach my $severity (sort keys %{$schema->{$state}->{$type}->{$priority}}) {
|
||||
# foreach my $tag (sort keys %{$schema->{$state}->{$type}->{$priority}->{$severity}}) {
|
||||
# print $days->{$day}->{$state}->{$type}->{$priority}->{$severity}->{$tag} || 0;
|
||||
# print ",";
|
||||
# }
|
||||
print $days->{$day}->{$state}->{$type}->{$priority}->{$severity}->{total} || 0;
|
||||
print ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
104
scripts/issues-no-state.pl
Executable file
104
scripts/issues-no-state.pl
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use Net::GitHub;
|
||||
use DateTime;
|
||||
use DateTime::Format::ISO8601;
|
||||
|
||||
my $gh = Net::GitHub->new(
|
||||
login => 'ara4n', pass => 'secret'
|
||||
);
|
||||
|
||||
$gh->set_default_user_repo('vector-im', 'vector-web');
|
||||
|
||||
my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
|
||||
while ($gh->issue->has_next_page) {
|
||||
push @issues, $gh->issue->next_page;
|
||||
}
|
||||
|
||||
# we want:
|
||||
# day by day:
|
||||
# split by { open, closed }
|
||||
# split by { bug, feature, neither }
|
||||
# each split by { p1, p2, p3, p4, p5, unprioritised } <- priority
|
||||
# each split by { minor, major, critical, cosmetic, network, no-severity } <- severity
|
||||
# then split (with overlap between the groups) as { total, tag1, tag2, ... }?
|
||||
|
||||
# ...and then all over again split by milestone.
|
||||
|
||||
my $days = {};
|
||||
my $schema = {};
|
||||
my $now = DateTime->now();
|
||||
|
||||
foreach my $issue (@issues) {
|
||||
next if ($issue->{pull_request});
|
||||
|
||||
use Data::Dumper;
|
||||
print STDERR Dumper($issue);
|
||||
|
||||
my @label_list = map { $_->{name} } @{$issue->{labels}};
|
||||
my $labels = {};
|
||||
$labels->{$_} = 1 foreach (@label_list);
|
||||
$labels->{bug}++ if ($labels->{cosmetic} && !$labels->{bug} && !$labels->{feature});
|
||||
|
||||
my $extract_labels = sub {
|
||||
my $label = undef;
|
||||
foreach (@_) {
|
||||
$label ||= $_ if (delete $labels->{$_});
|
||||
}
|
||||
return $label;
|
||||
};
|
||||
|
||||
my $type = &$extract_labels(qw(bug feature)) || "neither";
|
||||
my $priority = &$extract_labels(qw(p1 p2 p3 p4 p5)) || "unprioritised";
|
||||
my $severity = &$extract_labels(qw(minor major critical cosmetic network)) || "no-severity";
|
||||
|
||||
my $start = DateTime::Format::ISO8601->parse_datetime($issue->{created_at});
|
||||
my $end = $issue->{closed_at} ? DateTime::Format::ISO8601->parse_datetime($issue->{closed_at}) : $now;
|
||||
|
||||
do {
|
||||
my $ymd = $start->ymd();
|
||||
|
||||
$days->{ $ymd }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
$schema->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
foreach (keys %$labels) {
|
||||
$days->{ $ymd }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
$schema->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
}
|
||||
|
||||
$start = $start->add(days => 1);
|
||||
} while (DateTime->compare($start, $end) < 0);
|
||||
}
|
||||
|
||||
print "day,";
|
||||
foreach my $type (sort keys %{$schema}) {
|
||||
foreach my $priority (sort keys %{$schema->{$type}}) {
|
||||
foreach my $severity (sort keys %{$schema->{$type}->{$priority}}) {
|
||||
# foreach my $tag (sort keys %{$schema->{$type}->{$priority}->{$severity}}) {
|
||||
# print "\"$type\n$priority\n$severity\n$tag\",";
|
||||
# }
|
||||
print "\"$type\n$priority\n$severity\",";
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
|
||||
foreach my $day (sort keys %$days) {
|
||||
print "$day,";
|
||||
foreach my $type (sort keys %{$schema}) {
|
||||
foreach my $priority (sort keys %{$schema->{$type}}) {
|
||||
foreach my $severity (sort keys %{$schema->{$type}->{$priority}}) {
|
||||
# foreach my $tag (sort keys %{$schema->{$type}->{$priority}->{$severity}}) {
|
||||
# print $days->{$day}->{$type}->{$priority}->{$severity}->{$tag} || 0;
|
||||
# print ",";
|
||||
# }
|
||||
print $days->{$day}->{$type}->{$priority}->{$severity}->{total} || 0;
|
||||
print ",";
|
||||
}
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
15
scripts/package.sh
Executable file
15
scripts/package.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
version=`git describe --dirty --tags || echo unknown`
|
||||
|
||||
npm run build
|
||||
mkdir -p packages
|
||||
cp -r vector vector-$version
|
||||
echo $version > vector-$version/version
|
||||
tar chvzf packages/vector-$version.tar.gz vector-$version
|
||||
rm -r vector-$version
|
||||
|
||||
echo
|
||||
echo "Packaged packages/vector-$version.tar.gz"
|
||||
21
scripts/staticfiles.js
Executable file
21
scripts/staticfiles.js
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// copy static files from node_modules to the vector directory
|
||||
//
|
||||
|
||||
var fs = require('fs-extra');
|
||||
|
||||
function exists(f) {
|
||||
try {
|
||||
fs.statSync(f);
|
||||
return true;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const olm = 'node_modules/olm/olm.js';
|
||||
if (exists(olm)) {
|
||||
console.log("copy", olm, "-> vector");
|
||||
fs.copySync(olm, 'vector/olm.js');
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
.mx_RoomView {
|
||||
word-wrap: break-word;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mx_RoomView .mx_RoomHeader {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.mx_RoomView_roomWrapper {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 32px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.mx_RoomView_messagePanel {
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-moz-box-ordinal-group: 1;
|
||||
-ms-flex-order: 1;
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* background-color: #ff0; */
|
||||
}
|
||||
|
||||
.mx_RoomView_messageListWrapper {
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.mx_RoomView_MessageList {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.mx_RoomView_invitePrompt {
|
||||
}
|
||||
|
||||
.mx_RoomView .mx_MemberList {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-moz-box-ordinal-group: 2;
|
||||
-ms-flex-order: 2;
|
||||
-webkit-order: 2;
|
||||
order: 2;
|
||||
|
||||
/* background-color: #0f0; */
|
||||
width: 250px;
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mx_RoomView .mx_MemberList ul {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.mx_RoomView .mx_MessageComposer {
|
||||
width: 100%;
|
||||
bottom: 0px;
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
.mx_MatrixChat {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_chatWrapper {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: 0px;
|
||||
bottom: 42px;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_leftPanel {
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-moz-box-ordinal-group: 1;
|
||||
-ms-flex-order: 1;
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
|
||||
/* background-color: #f00; */
|
||||
width: 250px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_leftPanel .mx_MatrixToolbar {
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-moz-box-ordinal-group: 1;
|
||||
-ms-flex-order: 1;
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.mx_MatrixChat_leftPanel .mx_RoomList {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-moz-box-ordinal-group: 2;
|
||||
-ms-flex-order: 2;
|
||||
-webkit-order: 2;
|
||||
order: 2;
|
||||
|
||||
/* background-color: #0ff; */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.mx_MatrixChat .mx_RoomView {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-moz-box-ordinal-group: 2;
|
||||
-ms-flex-order: 2;
|
||||
-webkit-order: 2;
|
||||
order: 2;
|
||||
|
||||
/* background-color: #00f; */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var EditableTextController = require("../../../../src/controllers/atoms/EditableText");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'EditableText',
|
||||
mixins: [EditableTextController],
|
||||
|
||||
onKeyUp: function(ev) {
|
||||
if (ev.key == "Enter") {
|
||||
this.onFinish(ev);
|
||||
} else if (ev.key == "Escape") {
|
||||
this.cancelEdit();
|
||||
}
|
||||
},
|
||||
|
||||
onClickDiv: function() {
|
||||
this.setState({
|
||||
phase: this.Phases.Edit,
|
||||
})
|
||||
},
|
||||
|
||||
onFocus: function(ev) {
|
||||
ev.target.setSelectionRange(0, ev.target.value.length);
|
||||
},
|
||||
|
||||
onFinish: function(ev) {
|
||||
this.setValue(ev.target.value);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var editable_el;
|
||||
|
||||
if (this.state.phase == this.Phases.Display) {
|
||||
editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.state.value}</div>;
|
||||
} else if (this.state.phase == this.Phases.Edit) {
|
||||
editable_el = (
|
||||
<div>
|
||||
<input type="text" defaultValue={this.state.value} onKeyUp={this.onKeyUp} onFocus={this.onFocus} onBlur={this.onFinish} autoFocus/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_EditableText">
|
||||
{editable_el}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var LogoutButtonController = require("../../../../src/controllers/atoms/LogoutButton");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'LogoutButton',
|
||||
mixins: [LogoutButtonController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<button className="mx_LogoutButton" onClick={this.onClick}>Sign out</button>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var CreateRoomButtonController = require("../../../../../src/controllers/atoms/create_room/CreateRoomButton");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CreateRoomButton',
|
||||
mixins: [CreateRoomButtonController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<button className="mx_CreateRoomButton" onClick={this.onClick}>Create Room</button>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var PresetsController = require("../../../../../src/controllers/atoms/create_room/Presets");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CreateRoomPresets',
|
||||
mixins: [PresetsController],
|
||||
|
||||
onValueChanged: function(ev) {
|
||||
this.setState({preset: ev.target.value})
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<select className="mx_Presets" onChange={this.onValueChanged} defaultValue={this.state.preset}>
|
||||
<option value="private_chat">Private Chat</option>
|
||||
<option value="public_chat">Public Chat</option>
|
||||
</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var RoomNameTextboxController = require("../../../../../src/controllers/atoms/create_room/RoomNameTextbox");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomNameTextbox',
|
||||
mixins: [RoomNameTextboxController],
|
||||
|
||||
onValueChanged: function(ev) {
|
||||
this.setState({room_name: ev.target.value})
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<input type="text" className="mx_RoomNameTextbox" placeholder="ex. MyNewRoom" onChange={this.onValueChanged}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MEmoteTileController = require("../../../../src/controllers/molecules/MEmoteTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MEmoteTile',
|
||||
mixins: [MEmoteTileController],
|
||||
|
||||
render: function() {
|
||||
var mxEvent = this.props.mxEvent;
|
||||
var content = mxEvent.getContent();
|
||||
var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
|
||||
return (
|
||||
<span className="mx_MEmoteTile mx_MessageTile_content">
|
||||
* {name} {content.body}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MFileTileController = require("../../../../src/controllers/molecules/MFileTile");
|
||||
|
||||
var MatrixClientPeg = require('../../../../src/MatrixClientPeg');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MFileTile',
|
||||
mixins: [MFileTileController],
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
return (
|
||||
<span className="mx_MFileTile">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
{this.presentableTextForFile(content)}
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MImageTileController = require("../../../../src/controllers/molecules/MImageTile");
|
||||
|
||||
var MatrixClientPeg = require('../../../../src/MatrixClientPeg');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MImageTile',
|
||||
mixins: [MImageTileController],
|
||||
|
||||
thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
||||
if (!fullWidth || !fullHeight) {
|
||||
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
||||
// log this because it's spammy
|
||||
return undefined;
|
||||
}
|
||||
if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
|
||||
// no scaling needs to be applied
|
||||
return fullHeight;
|
||||
}
|
||||
var widthMulti = thumbWidth / fullWidth;
|
||||
var heightMulti = thumbHeight / fullHeight;
|
||||
if (widthMulti < heightMulti) {
|
||||
// width is the dominant dimension so scaling will be fixed on that
|
||||
return Math.floor(widthMulti * fullHeight);
|
||||
}
|
||||
else {
|
||||
// height is the dominant dimension so scaling will be fixed on that
|
||||
return Math.floor(heightMulti * fullHeight);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
var thumbHeight = null;
|
||||
if (content.info) thumbHeight = this.thumbHeight(content.info.w, content.info.h, 320, 240);
|
||||
|
||||
var imgStyle = {};
|
||||
if (thumbHeight) imgStyle['height'] = thumbHeight;
|
||||
|
||||
return (
|
||||
<span className="mx_MImageTile">
|
||||
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||
<img src={cli.mxcUrlToHttp(content.url, 320, 240)} alt={content.body} style={imgStyle} />
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MNoticeTileController = require("../../../../src/controllers/molecules/MNoticeTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MNoticeTile',
|
||||
mixins: [MNoticeTileController],
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
return (
|
||||
<span ref="content" className="mx_MNoticeTile mx_MessageTile_content">
|
||||
{content.body}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MRoomMemberTileController = require("../../../../src/controllers/molecules/MRoomMemberTile");
|
||||
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MRoomMemberTile',
|
||||
mixins: [MRoomMemberTileController],
|
||||
|
||||
getMemberEventText: function() {
|
||||
var ev = this.props.mxEvent;
|
||||
// XXX: SYJS-16
|
||||
var senderName = ev.sender ? ev.sender.name : "Someone";
|
||||
switch (ev.getContent().membership) {
|
||||
case 'invite':
|
||||
return senderName + " invited " + ev.target.name + ".";
|
||||
case 'join':
|
||||
return senderName + " joined the room.";
|
||||
case 'leave':
|
||||
return senderName + " left the room.";
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX: for now, just cheekily borrow the css from message tile...
|
||||
return (
|
||||
<div className="mx_MessageTile">
|
||||
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
<span className="mx_SenderProfile"></span>
|
||||
<span className="mx_MessageTile_content">
|
||||
{this.getMemberEventText()}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MTextTileController = require("../../../../src/controllers/molecules/MTextTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MTextTile',
|
||||
mixins: [MTextTileController],
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
return (
|
||||
<span ref="content" className="mx_MTextTile mx_MessageTile_content">
|
||||
{content.body}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var classNames = require("classnames");
|
||||
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
|
||||
var SenderProfile = ComponentBroker.get('molecules/SenderProfile');
|
||||
|
||||
var UnknownMessageTile = ComponentBroker.get('molecules/UnknownMessageTile');
|
||||
|
||||
var tileTypes = {
|
||||
'm.text': ComponentBroker.get('molecules/MTextTile'),
|
||||
'm.notice': ComponentBroker.get('molecules/MNoticeTile'),
|
||||
'm.emote': ComponentBroker.get('molecules/MEmoteTile'),
|
||||
'm.image': ComponentBroker.get('molecules/MImageTile'),
|
||||
'm.file': ComponentBroker.get('molecules/MFileTile')
|
||||
};
|
||||
|
||||
var MessageTileController = require("../../../../src/controllers/molecules/MessageTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MessageTile',
|
||||
mixins: [MessageTileController],
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
var msgtype = content.msgtype;
|
||||
var TileType = UnknownMessageTile;
|
||||
if (msgtype && tileTypes[msgtype]) {
|
||||
TileType = tileTypes[msgtype];
|
||||
}
|
||||
var classes = classNames({
|
||||
mx_MessageTile: true,
|
||||
mx_MessageTile_sending: this.props.mxEvent.status == 'sending',
|
||||
mx_MessageTile_notSent: this.props.mxEvent.status == 'not_sent',
|
||||
mx_MessageTile_highlight: this.shouldHighlight()
|
||||
});
|
||||
return (
|
||||
<div className={classes}>
|
||||
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
<SenderProfile mxEvent={this.props.mxEvent} />
|
||||
<TileType mxEvent={this.props.mxEvent} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var ProgressBarController = require("../../../../src/controllers/molecules/ProgressBar");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ProgressBar',
|
||||
mixins: [ProgressBarController],
|
||||
|
||||
render: function() {
|
||||
// Would use an HTML5 progress tag but if that doesn't animate if you
|
||||
// use the HTML attributes rather than styles
|
||||
var progressStyle = {
|
||||
width: ((this.props.value / this.props.max) * 100)+"%"
|
||||
};
|
||||
return (
|
||||
<div className="mx_ProgressBar"><div className="mx_ProgressBar_fill" style={progressStyle}></div></div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var classNames = require('classnames');
|
||||
|
||||
var RoomTileController = require("../../../../src/controllers/molecules/RoomTile");
|
||||
|
||||
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTile',
|
||||
mixins: [RoomTileController],
|
||||
render: function() {
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
var classes = classNames({
|
||||
'mx_RoomTile': true,
|
||||
'selected': this.props.selected,
|
||||
'unread': this.props.unread,
|
||||
'highlight': this.props.highlight,
|
||||
'invited': this.props.room.currentState.members[myUserId].membership == 'invite'
|
||||
});
|
||||
return (
|
||||
<div className={classes} onClick={this.onClick}>
|
||||
<div className="mx_RoomTile_name">{this.props.room.name}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var SenderProfileController = require("../../../../src/controllers/molecules/SenderProfile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'SenderProfile',
|
||||
mixins: [SenderProfileController],
|
||||
|
||||
render: function() {
|
||||
var mxEvent = this.props.mxEvent;
|
||||
var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
|
||||
|
||||
var msgtype = mxEvent.getContent().msgtype;
|
||||
if (msgtype && msgtype == 'm.emote') {
|
||||
name = ''; // emote message must include the name so don't duplicate it
|
||||
}
|
||||
return (
|
||||
<span className="mx_SenderProfile">
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var ServerConfigController = require("../../../../src/controllers/molecules/ServerConfig");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ServerConfig',
|
||||
mixins: [ServerConfigController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="HomeServerTextBox">
|
||||
<table className="serverConfig">
|
||||
<tr>
|
||||
<td>Home Server URL</td>
|
||||
<td><input type="text" value={this.state.hs_url} onChange={this.hsChanged} /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Identity Server URL</td>
|
||||
<td><input type="text" value={this.state.is_url} onChange={this.isChanged} /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var UnknownMessageTileController = require("../../../../src/controllers/molecules/UnknownMessageTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'UnknownMessageTile',
|
||||
mixins: [UnknownMessageTileController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<span className="mx_UnknownMessageTile">
|
||||
?
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var UserSelectorController = require("../../../../src/controllers/molecules/UserSelector");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'UserSelector',
|
||||
mixins: [UserSelectorController],
|
||||
|
||||
onAddUserId: function() {
|
||||
this.addUser(this.refs.user_id_input.getDOMNode().value);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<ul className="mx_UserSelector_UserIdList" ref="list">
|
||||
{this.state.selected_users.map(function(user_id, i) {
|
||||
return <li key={user_id}>{user_id}</li>
|
||||
})}
|
||||
</ul>
|
||||
<input type="text" ref="user_id_input" className="mx_UserSelector_userIdInput" placeholder="ex. @bob:example.com"/>
|
||||
<button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">Add User</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var CreateRoomController = require("../../../../src/controllers/organisms/CreateRoom");
|
||||
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var CreateRoomButton = ComponentBroker.get("atoms/create_room/CreateRoomButton");
|
||||
var RoomNameTextbox = ComponentBroker.get("atoms/create_room/RoomNameTextbox");
|
||||
var Presets = ComponentBroker.get("atoms/create_room/Presets");
|
||||
var UserSelector = ComponentBroker.get("molecules/UserSelector");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CreateRoom',
|
||||
mixins: [CreateRoomController],
|
||||
|
||||
getPreset: function() {
|
||||
return this.refs.presets.getPreset();
|
||||
},
|
||||
|
||||
getName: function() {
|
||||
return this.refs.name_textbox.getName();
|
||||
},
|
||||
|
||||
getInvitedUsers: function() {
|
||||
return this.refs.user_selector.getUserIds();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var curr_phase = this.state.phase;
|
||||
if (curr_phase == this.phases.CREATING) {
|
||||
return (
|
||||
<div>Creating...</div>
|
||||
);
|
||||
} else {
|
||||
var error_box = "";
|
||||
if (curr_phase == this.phases.ERROR) {
|
||||
error_box = (
|
||||
<div className="mx_Error">
|
||||
An error occured: {this.state.error_string}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="mx_CreateRoom">
|
||||
<label>Room Name <RoomNameTextbox ref="name_textbox" /></label>
|
||||
<Presets ref="presets"/>
|
||||
<UserSelector ref="user_selector"/>
|
||||
<CreateRoomButton onCreateRoom={this.onCreateRoom} />
|
||||
{error_box}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MemberListController = require("../../../../src/controllers/organisms/MemberList");
|
||||
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var MemberTile = ComponentBroker.get("molecules/MemberTile");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MemberList',
|
||||
mixins: [MemberListController],
|
||||
|
||||
makeMemberTiles: function() {
|
||||
var that = this;
|
||||
return Object.keys(that.state.memberDict).map(function(userId) {
|
||||
var m = that.state.memberDict[userId];
|
||||
return (
|
||||
<li key={userId}>
|
||||
<MemberTile
|
||||
member={m}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_MemberList">
|
||||
<ul>
|
||||
{this.makeMemberTiles()}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var NotifierController = require("../../../../src/controllers/organisms/Notifier");
|
||||
|
||||
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
var extend = require("../../../../src/extend");
|
||||
var dis = require("../../../../src/dispatcher");
|
||||
|
||||
|
||||
var NotifierView = {
|
||||
notificationMessageForEvent: function(ev) {
|
||||
var senderDisplayName = ev.sender ? ev.sender.name : '';
|
||||
var message = null;
|
||||
|
||||
if (ev.event.type === "m.room.message") {
|
||||
message = ev.getContent().body;
|
||||
if (ev.getContent().msgtype === "m.emote") {
|
||||
message = "* " + senderDisplayName + " " + message;
|
||||
} else if (ev.getContent().msgtype === "m.image") {
|
||||
message = senderDisplayName + " sent an image.";
|
||||
}
|
||||
} else if (ev.event.type == "m.room.member") {
|
||||
if (ev.event.state_key !== MatrixClientPeg.get().credentials.userId && "join" === ev.getContent().membership) {
|
||||
// Notify when another user joins
|
||||
message = senderDisplayName + " joined";
|
||||
} else if (ev.event.state_key === MatrixClientPeg.get().credentials.userId && "invite" === ev.getContent().membership) {
|
||||
// notify when you are invited
|
||||
message = senderDisplayName + " invited you to a room";
|
||||
}
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
displayNotification: function(ev, room) {
|
||||
if (!global.Notification || global.Notification.permission != 'granted') {
|
||||
return;
|
||||
}
|
||||
if (global.document.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = this.notificationMessageForEvent(ev);
|
||||
if (!msg) return;
|
||||
|
||||
var title;
|
||||
if (!ev.sender || room.name == ev.sender.name) {
|
||||
title = room.name;
|
||||
} else if (ev.sender) {
|
||||
title = ev.sender.name + " (" + room.name + ")";
|
||||
}
|
||||
|
||||
var notification = new global.Notification(
|
||||
title,
|
||||
{
|
||||
"body": msg,
|
||||
"icon": MatrixClientPeg.get().getAvatarUrlForMember(ev.sender)
|
||||
}
|
||||
);
|
||||
|
||||
notification.onclick = function() {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.roomId
|
||||
});
|
||||
global.focus();
|
||||
};
|
||||
|
||||
/*var audioClip;
|
||||
|
||||
if (audioNotification) {
|
||||
audioClip = playAudio(audioNotification);
|
||||
}*/
|
||||
|
||||
global.setTimeout(function() {
|
||||
notification.close();
|
||||
}, 5 * 1000);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
var NotifierClass = function() {};
|
||||
extend(NotifierClass.prototype, NotifierController);
|
||||
extend(NotifierClass.prototype, NotifierView);
|
||||
|
||||
module.exports = new NotifierClass();
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
var classNames = require("classnames");
|
||||
|
||||
var MessageTile = ComponentBroker.get('molecules/MessageTile');
|
||||
var RoomHeader = ComponentBroker.get('molecules/RoomHeader');
|
||||
var MemberList = ComponentBroker.get('organisms/MemberList');
|
||||
var MessageComposer = ComponentBroker.get('molecules/MessageComposer');
|
||||
|
||||
var RoomViewController = require("../../../../src/controllers/organisms/RoomView");
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomView',
|
||||
mixins: [RoomViewController],
|
||||
|
||||
render: function() {
|
||||
if (!this.state.room) {
|
||||
return (
|
||||
<div />
|
||||
);
|
||||
}
|
||||
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
if (this.state.room.currentState.members[myUserId].membership == 'invite') {
|
||||
if (this.state.joining) {
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event;
|
||||
// XXX: Leaving this intentionally basic for now because invites are about to change totally
|
||||
var joinErrorText = this.state.joinError ? "Failed to join room!" : "";
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
<div className="mx_RoomView_invitePrompt">
|
||||
<div>{inviteEvent.user_id} has invited you to a room</div>
|
||||
<button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button>
|
||||
<div className="error">{joinErrorText}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
var scrollheader_classes = classNames({
|
||||
mx_RoomView_scrollheader: true,
|
||||
loading: this.state.paginating
|
||||
});
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
<RoomHeader room={this.state.room} />
|
||||
<div className="mx_RoomView_roomWrapper">
|
||||
<div className="mx_RoomView_messagePanel">
|
||||
<div ref="messageWrapper" className="mx_RoomView_messageListWrapper" onScroll={this.onMessageListScroll}>
|
||||
<div className="mx_RoomView_MessageList" aria-live="polite">
|
||||
<div className={scrollheader_classes}>
|
||||
</div>
|
||||
{this.getEventTiles()}
|
||||
</div>
|
||||
</div>
|
||||
<MessageComposer roomId={this.props.roomId} />
|
||||
</div>
|
||||
<MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var RoomList = ComponentBroker.get('organisms/RoomList');
|
||||
var RoomView = ComponentBroker.get('organisms/RoomView');
|
||||
var MatrixToolbar = ComponentBroker.get('molecules/MatrixToolbar');
|
||||
var Login = ComponentBroker.get('templates/Login');
|
||||
var Register = ComponentBroker.get('templates/Register');
|
||||
|
||||
var MatrixChatController = require("../../../../src/controllers/pages/MatrixChat");
|
||||
|
||||
// should be atomised
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MatrixChat',
|
||||
mixins: [MatrixChatController],
|
||||
|
||||
render: function() {
|
||||
if (this.state.logged_in && this.state.ready) {
|
||||
return (
|
||||
<div className="mx_MatrixChat">
|
||||
<div className="mx_MatrixChat_chatWrapper">
|
||||
<div className="mx_MatrixChat_leftPanel">
|
||||
<RoomList selectedRoom={this.state.currentRoom} />
|
||||
<MatrixToolbar />
|
||||
</div>
|
||||
<RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.logged_in) {
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
} else if (this.state.screen == 'register') {
|
||||
return (
|
||||
<Register onLoggedIn={this.onLoggedIn} clientSecret={this.state.register_client_secret}
|
||||
sessionId={this.state.register_session_id} idSid={this.state.register_id_sid}
|
||||
hsUrl={this.state.register_hs_url} isUrl={this.state.register_is_url}
|
||||
registrationUrl={this.props.registrationUrl}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Login onLoggedIn={this.onLoggedIn} />
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var ComponentBroker = require("../../../../src/ComponentBroker");
|
||||
|
||||
var ProgressBar = ComponentBroker.get("molecules/ProgressBar");
|
||||
var Loader = require("react-loader");
|
||||
|
||||
var LoginController = require("../../../../src/controllers/templates/Login");
|
||||
|
||||
var ServerConfig = ComponentBroker.get("molecules/ServerConfig");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Login',
|
||||
mixins: [LoginController],
|
||||
|
||||
getHsUrl: function() {
|
||||
return this.refs.serverConfig.getHsUrl();
|
||||
},
|
||||
|
||||
getIsUrl: function() {
|
||||
return this.refs.serverConfig.getIsUrl();
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the form field values for the current login stage
|
||||
*/
|
||||
getFormVals: function() {
|
||||
return {
|
||||
'username': this.refs.user.getDOMNode().value,
|
||||
'password': this.refs.pass.getDOMNode().value
|
||||
};
|
||||
},
|
||||
|
||||
componentForStep: function(step) {
|
||||
switch (step) {
|
||||
case 'choose_hs':
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onHSChosen}>
|
||||
<ServerConfig ref="serverConfig" />
|
||||
<input type="submit" value="Continue" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
// XXX: clearly these should be separate organisms
|
||||
case 'stage_m.login.password':
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onUserPassEntered}>
|
||||
<input ref="user" type="text" placeholder="username" /><br />
|
||||
<input ref="pass" type="password" placeholder="password" /><br />
|
||||
<input type="submit" value="Log in" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
loginContent: function() {
|
||||
if (this.state.busy) {
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<h1>Please log in:</h1>
|
||||
{this.componentForStep(this.state.step)}
|
||||
<div className="error">{this.state.errorText}</div>
|
||||
<a onClick={this.showRegister} href="#">Create a new account</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_Login">
|
||||
<ProgressBar value={this.state.currentStep} max={this.state.totalSteps} />
|
||||
{this.loginContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var ComponentBroker = require("../../../../src/ComponentBroker");
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
var RegisterController = require("../../../../src/controllers/templates/Register");
|
||||
|
||||
var ServerConfig = ComponentBroker.get("molecules/ServerConfig");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Register',
|
||||
mixins: [RegisterController],
|
||||
|
||||
getRegFormVals: function() {
|
||||
return {
|
||||
email: this.refs.email.getDOMNode().value,
|
||||
username: this.refs.username.getDOMNode().value,
|
||||
password: this.refs.password.getDOMNode().value,
|
||||
confirmPassword: this.refs.confirmPassword.getDOMNode().value
|
||||
};
|
||||
},
|
||||
|
||||
getHsUrl: function() {
|
||||
return this.refs.serverConfig.getHsUrl();
|
||||
},
|
||||
|
||||
getIsUrl: function() {
|
||||
return this.refs.serverConfig.getIsUrl();
|
||||
},
|
||||
|
||||
componentForStep: function(step) {
|
||||
switch (step) {
|
||||
case 'initial':
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onInitialStageSubmit}>
|
||||
Email: <input type="text" ref="email" defaultValue={this.savedParams.email} /><br />
|
||||
Username: <input type="text" ref="username" defaultValue={this.savedParams.username} /><br />
|
||||
Password: <input type="password" ref="password" defaultValue={this.savedParams.password} /><br />
|
||||
Confirm Password: <input type="password" ref="confirmPassword" defaultValue={this.savedParams.confirmPassword} /><br />
|
||||
<ServerConfig ref="serverConfig" />
|
||||
<input type="submit" value="Continue" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
// XXX: clearly these should be separate organisms
|
||||
case 'stage_m.login.email.identity':
|
||||
return (
|
||||
<div>
|
||||
Please check your email to continue registration.
|
||||
</div>
|
||||
);
|
||||
case 'stage_m.login.recaptcha':
|
||||
return (
|
||||
<div ref="recaptchaContainer">
|
||||
This Home Server would like to make sure you're not a robot
|
||||
<div id="mx_recaptcha"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
registerContent: function() {
|
||||
if (this.state.busy) {
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<h1>Create a new account:</h1>
|
||||
{this.componentForStep(this.state.step)}
|
||||
<div className="error">{this.state.errorText}</div>
|
||||
<a onClick={this.showLogin} href="#">Sign in with existing account</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
onBadFields: function(bad) {
|
||||
var keys = Object.keys(bad);
|
||||
var strings = [];
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
switch (bad[keys[i]]) {
|
||||
case this.FieldErrors.PasswordMismatch:
|
||||
strings.push("Passwords don't match");
|
||||
break;
|
||||
case this.FieldErrors.Missing:
|
||||
strings.push("Missing "+keys[i]);
|
||||
break;
|
||||
case this.FieldErrors.TooShort:
|
||||
strings.push(keys[i]+" is too short");
|
||||
break;
|
||||
case this.FieldErrors.InUse:
|
||||
strings.push(keys[i]+" is already taken");
|
||||
break;
|
||||
}
|
||||
}
|
||||
var errtxt = strings.join(', ');
|
||||
this.setState({
|
||||
errorText: errtxt
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_Register">
|
||||
{this.registerContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
function load(name) {
|
||||
var module = require("../skins/base/views/"+name);
|
||||
return module;
|
||||
};
|
||||
|
||||
var ComponentBroker = function() {
|
||||
this.components = {};
|
||||
};
|
||||
|
||||
ComponentBroker.prototype = {
|
||||
get: function(name) {
|
||||
if (this.components[name]) {
|
||||
return this.components[name];
|
||||
}
|
||||
|
||||
this.components[name] = load(name);
|
||||
return this.components[name];
|
||||
},
|
||||
|
||||
set: function(name, module) {
|
||||
this.components[name] = module;
|
||||
}
|
||||
};
|
||||
|
||||
// We define one Component Broker globally, because the intention is
|
||||
// very much that it is a singleton. Relying on there only being one
|
||||
// copy of the module can be dicey and not work as browserify's
|
||||
// behaviour with multiple copies of files etc. is erratic at best.
|
||||
// XXX: We can still end up with the same file twice in the resulting
|
||||
// JS bundle which is nonideal.
|
||||
if (global.componentBroker === undefined) {
|
||||
global.componentBroker = new ComponentBroker();
|
||||
}
|
||||
module.exports = global.componentBroker;
|
||||
|
||||
// We need to tell browserify to include all the components
|
||||
// by direct require syntax in here, but we don't want them
|
||||
// to be evaluated in this file because then we wouldn't be
|
||||
// able to override them. if (0) does this.
|
||||
// Must be in this file (because the require is file-specific) and
|
||||
// must be at the end because the components include this file.
|
||||
if (0) {
|
||||
require('../skins/base/views/atoms/LogoutButton');
|
||||
require('../skins/base/views/atoms/EnableNotificationsButton');
|
||||
require('../skins/base/views/atoms/MessageTimestamp');
|
||||
require('../skins/base/views/atoms/create_room/CreateRoomButton');
|
||||
require('../skins/base/views/atoms/create_room/RoomNameTextbox');
|
||||
require('../skins/base/views/atoms/create_room/Presets');
|
||||
require('../skins/base/views/atoms/EditableText');
|
||||
require('../skins/base/views/molecules/MatrixToolbar');
|
||||
require('../skins/base/views/molecules/RoomTile');
|
||||
require('../skins/base/views/molecules/MessageTile');
|
||||
require('../skins/base/views/molecules/SenderProfile');
|
||||
require('../skins/base/views/molecules/UnknownMessageTile');
|
||||
require('../skins/base/views/molecules/MTextTile');
|
||||
require('../skins/base/views/molecules/MNoticeTile');
|
||||
require('../skins/base/views/molecules/MEmoteTile');
|
||||
require('../skins/base/views/molecules/MImageTile');
|
||||
require('../skins/base/views/molecules/MFileTile');
|
||||
require('../skins/base/views/molecules/MRoomMemberTile');
|
||||
require('../skins/base/views/molecules/RoomHeader');
|
||||
require('../skins/base/views/molecules/MessageComposer');
|
||||
require('../skins/base/views/molecules/ProgressBar');
|
||||
require('../skins/base/views/molecules/ServerConfig');
|
||||
require('../skins/base/views/organisms/MemberList');
|
||||
require('../skins/base/views/molecules/MemberTile');
|
||||
require('../skins/base/views/organisms/RoomList');
|
||||
require('../skins/base/views/organisms/RoomView');
|
||||
require('../skins/base/views/templates/Login');
|
||||
require('../skins/base/views/templates/Register');
|
||||
require('../skins/base/views/organisms/Notifier');
|
||||
require('../skins/base/views/organisms/CreateRoom');
|
||||
require('../skins/base/views/molecules/UserSelector');
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var q = require('q');
|
||||
var extend = require('./extend');
|
||||
|
||||
function infoForImageFile(imageFile) {
|
||||
var deferred = q.defer();
|
||||
|
||||
// Load the file into an html element
|
||||
var img = document.createElement("img");
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
img.src = e.target.result;
|
||||
|
||||
// Once ready, returns its size
|
||||
img.onload = function() {
|
||||
deferred.resolve({
|
||||
w: img.width,
|
||||
h: img.height
|
||||
});
|
||||
};
|
||||
img.onerror = function(e) {
|
||||
deferred.reject(e);
|
||||
};
|
||||
};
|
||||
reader.onerror = function(e) {
|
||||
deferred.reject(e);
|
||||
};
|
||||
reader.readAsDataURL(imageFile);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function sendContentToRoom(file, roomId, matrixClient) {
|
||||
var content = {
|
||||
body: file.name,
|
||||
info: {
|
||||
size: file.size,
|
||||
mimetype: file.type
|
||||
}
|
||||
};
|
||||
|
||||
var def = q.defer();
|
||||
if (file.type.indexOf('image/') == 0) {
|
||||
content.msgtype = 'm.image';
|
||||
infoForImageFile(file).then(function(imageInfo) {
|
||||
extend(content.info, imageInfo);
|
||||
def.resolve();
|
||||
});
|
||||
} else {
|
||||
content.msgtype = 'm.file';
|
||||
def.resolve();
|
||||
}
|
||||
|
||||
return def.promise.then(function() {
|
||||
return matrixClient.uploadContent(file);
|
||||
}).then(function(url) {
|
||||
content.url = url;
|
||||
return matrixClient.sendMessage(roomId, content);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendContentToRoom: sendContentToRoom
|
||||
};
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// A thing that holds your Matrix Client
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
|
||||
var matrixClient = null;
|
||||
|
||||
var localStorage = window.localStorage;
|
||||
|
||||
function createClient(hs_url, is_url, user_id, access_token) {
|
||||
var opts = {
|
||||
baseUrl: hs_url,
|
||||
idBaseUrl: is_url,
|
||||
accessToken: access_token,
|
||||
userId: user_id
|
||||
};
|
||||
|
||||
matrixClient = Matrix.createClient(opts);
|
||||
}
|
||||
|
||||
if (localStorage) {
|
||||
var hs_url = localStorage.getItem("mx_hs_url");
|
||||
var is_url = localStorage.getItem("mx_is_url") || 'https://matrix.org';
|
||||
var access_token = localStorage.getItem("mx_access_token");
|
||||
var user_id = localStorage.getItem("mx_user_id");
|
||||
if (access_token && user_id && hs_url) {
|
||||
createClient(hs_url, is_url, user_id, access_token);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
get: function() {
|
||||
return matrixClient;
|
||||
},
|
||||
|
||||
replaceUsingUrls: function(hs_url, is_url) {
|
||||
matrixClient = Matrix.createClient({
|
||||
baseUrl: hs_url,
|
||||
idBaseUrl: is_url
|
||||
});
|
||||
},
|
||||
|
||||
replaceUsingAccessToken: function(hs_url, is_url, user_id, access_token) {
|
||||
createClient(hs_url, is_url, user_id, access_token);
|
||||
if (localStorage) {
|
||||
try {
|
||||
localStorage.clear();
|
||||
localStorage.setItem("mx_hs_url", hs_url);
|
||||
localStorage.setItem("mx_is_url", is_url);
|
||||
localStorage.setItem("mx_user_id", user_id);
|
||||
localStorage.setItem("mx_access_token", access_token);
|
||||
} catch (e) {
|
||||
console.warn("Error using local storage: can't persist session!");
|
||||
}
|
||||
} else {
|
||||
console.warn("No local storage available: can't persist session!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
135
src/VectorConferenceHandler.js
Normal file
135
src/VectorConferenceHandler.js
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var q = require("q");
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var Room = Matrix.Room;
|
||||
var CallHandler = require('matrix-react-sdk/lib/CallHandler');
|
||||
|
||||
// FIXME: This currently forces Vector to try to hit the matrix.org AS for conferencing.
|
||||
// This is bad because it prevents people running their own ASes from being used.
|
||||
// This isn't permanent and will be customisable in the future: see the proposal
|
||||
// at docs/conferencing.md for more info.
|
||||
var USER_PREFIX = "fs_";
|
||||
var DOMAIN = "matrix.org";
|
||||
|
||||
function ConferenceCall(matrixClient, groupChatRoomId) {
|
||||
this.client = matrixClient;
|
||||
this.groupRoomId = groupChatRoomId;
|
||||
this.confUserId = module.exports.getConferenceUserIdForRoom(this.groupRoomId);
|
||||
}
|
||||
|
||||
ConferenceCall.prototype.setup = function() {
|
||||
var self = this;
|
||||
return this._joinConferenceUser().then(function() {
|
||||
return self._getConferenceUserRoom();
|
||||
}).then(function(room) {
|
||||
// return a call for *this* room to be placed. We also tack on
|
||||
// confUserId to speed up lookups (else we'd need to loop every room
|
||||
// looking for a 1:1 room with this conf user ID!)
|
||||
var call = Matrix.createNewMatrixCall(self.client, room.roomId);
|
||||
call.confUserId = self.confUserId;
|
||||
call.groupRoomId = self.groupRoomId;
|
||||
return call;
|
||||
});
|
||||
};
|
||||
|
||||
ConferenceCall.prototype._joinConferenceUser = function() {
|
||||
// Make sure the conference user is in the group chat room
|
||||
var groupRoom = this.client.getRoom(this.groupRoomId);
|
||||
if (!groupRoom) {
|
||||
return q.reject("Bad group room ID");
|
||||
}
|
||||
var member = groupRoom.getMember(this.confUserId);
|
||||
if (member && member.membership === "join") {
|
||||
return q();
|
||||
}
|
||||
return this.client.invite(this.groupRoomId, this.confUserId);
|
||||
};
|
||||
|
||||
ConferenceCall.prototype._getConferenceUserRoom = function() {
|
||||
// Use an existing 1:1 with the conference user; else make one
|
||||
var rooms = this.client.getRooms();
|
||||
var confRoom = null;
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var confUser = rooms[i].getMember(this.confUserId);
|
||||
if (confUser && confUser.membership === "join" &&
|
||||
rooms[i].getJoinedMembers().length === 2) {
|
||||
confRoom = rooms[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (confRoom) {
|
||||
return q(confRoom);
|
||||
}
|
||||
return this.client.createRoom({
|
||||
preset: "private_chat",
|
||||
invite: [this.confUserId]
|
||||
}).then(function(res) {
|
||||
return new Room(res.room_id);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this user ID is in fact a conference bot.
|
||||
* @param {string} userId The user ID to check.
|
||||
* @return {boolean} True if it is a conference bot.
|
||||
*/
|
||||
module.exports.isConferenceUser = function(userId) {
|
||||
if (userId.indexOf("@" + USER_PREFIX) !== 0) {
|
||||
return false;
|
||||
}
|
||||
var base64part = userId.split(":")[0].substring(1 + USER_PREFIX.length);
|
||||
if (base64part) {
|
||||
var decoded = new Buffer(base64part, "base64").toString();
|
||||
// ! $STUFF : $STUFF
|
||||
return /^!.+:.+/.test(decoded);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
module.exports.getConferenceUserIdForRoom = function(roomId) {
|
||||
// abuse browserify's core node Buffer support (strip padding ='s)
|
||||
var base64RoomId = new Buffer(roomId).toString("base64").replace(/=/g, "");
|
||||
return "@" + USER_PREFIX + base64RoomId + ":" + DOMAIN;
|
||||
};
|
||||
|
||||
module.exports.createNewMatrixCall = function(client, roomId) {
|
||||
var confCall = new ConferenceCall(
|
||||
client, roomId
|
||||
);
|
||||
return confCall.setup();
|
||||
};
|
||||
|
||||
module.exports.getConferenceCallForRoom = function(roomId) {
|
||||
// search for a conference 1:1 call for this group chat room ID
|
||||
var activeCall = CallHandler.getAnyActiveCall();
|
||||
if (activeCall && activeCall.confUserId) {
|
||||
var thisRoomConfUserId = module.exports.getConferenceUserIdForRoom(
|
||||
roomId
|
||||
);
|
||||
if (thisRoomConfUserId === activeCall.confUserId) {
|
||||
return activeCall;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports.ConferenceCall = ConferenceCall;
|
||||
|
||||
module.exports.slot = 'conference';
|
||||
56
src/component-index.js
Normal file
56
src/component-index.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* THIS FILE IS AUTO-GENERATED
|
||||
* You can edit it you like, but your changes will be overwritten,
|
||||
* so you'd just be trying to swim upstream like a salmon.
|
||||
* You are not a salmon.
|
||||
*
|
||||
* To update it, run:
|
||||
* ./reskindex.js -h header
|
||||
*/
|
||||
|
||||
module.exports.components = require('matrix-react-sdk/lib/component-index').components;
|
||||
|
||||
module.exports.components['structures.BottomLeftMenu'] = require('./components/structures/BottomLeftMenu');
|
||||
module.exports.components['structures.CompatibilityPage'] = require('./components/structures/CompatibilityPage');
|
||||
module.exports.components['structures.LeftPanel'] = require('./components/structures/LeftPanel');
|
||||
module.exports.components['structures.RightPanel'] = require('./components/structures/RightPanel');
|
||||
module.exports.components['structures.RoomDirectory'] = require('./components/structures/RoomDirectory');
|
||||
module.exports.components['structures.RoomSubList'] = require('./components/structures/RoomSubList');
|
||||
module.exports.components['structures.SearchBox'] = require('./components/structures/SearchBox');
|
||||
module.exports.components['structures.ViewSource'] = require('./components/structures/ViewSource');
|
||||
module.exports.components['views.context_menus.MessageContextMenu'] = require('./components/views/context_menus/MessageContextMenu');
|
||||
module.exports.components['views.context_menus.NotificationStateContextMenu'] = require('./components/views/context_menus/NotificationStateContextMenu');
|
||||
module.exports.components['views.context_menus.RoomTagContextMenu'] = require('./components/views/context_menus/RoomTagContextMenu');
|
||||
module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView');
|
||||
module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner');
|
||||
module.exports.components['views.globals.GuestWarningBar'] = require('./components/views/globals/GuestWarningBar');
|
||||
module.exports.components['views.globals.MatrixToolbar'] = require('./components/views/globals/MatrixToolbar');
|
||||
module.exports.components['views.globals.NewVersionBar'] = require('./components/views/globals/NewVersionBar');
|
||||
module.exports.components['views.login.VectorCustomServerDialog'] = require('./components/views/login/VectorCustomServerDialog');
|
||||
module.exports.components['views.login.VectorLoginFooter'] = require('./components/views/login/VectorLoginFooter');
|
||||
module.exports.components['views.login.VectorLoginHeader'] = require('./components/views/login/VectorLoginHeader');
|
||||
module.exports.components['views.messages.DateSeparator'] = require('./components/views/messages/DateSeparator');
|
||||
module.exports.components['views.messages.MessageTimestamp'] = require('./components/views/messages/MessageTimestamp');
|
||||
module.exports.components['views.rooms.BottomLeftMenuTile'] = require('./components/views/rooms/BottomLeftMenuTile');
|
||||
module.exports.components['views.rooms.RoomDNDView'] = require('./components/views/rooms/RoomDNDView');
|
||||
module.exports.components['views.rooms.RoomDropTarget'] = require('./components/views/rooms/RoomDropTarget');
|
||||
module.exports.components['views.rooms.RoomTooltip'] = require('./components/views/rooms/RoomTooltip');
|
||||
module.exports.components['views.rooms.SearchBar'] = require('./components/views/rooms/SearchBar');
|
||||
module.exports.components['views.settings.IntegrationsManager'] = require('./components/views/settings/IntegrationsManager');
|
||||
module.exports.components['views.settings.Notifications'] = require('./components/views/settings/Notifications');
|
||||
67
src/components/structures/BottomLeftMenu.js
Normal file
67
src/components/structures/BottomLeftMenu.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'BottomLeftMenu',
|
||||
|
||||
onSettingsClick: function() {
|
||||
dis.dispatch({action: 'view_user_settings'});
|
||||
},
|
||||
|
||||
onRoomDirectoryClick: function() {
|
||||
dis.dispatch({action: 'view_room_directory'});
|
||||
},
|
||||
|
||||
onCreateRoomClick: function() {
|
||||
dis.dispatch({action: 'view_create_room'});
|
||||
},
|
||||
|
||||
getLabel: function(name) {
|
||||
if (!this.props.collapsed) {
|
||||
return <div className="mx_RoomTile_name">{name}</div>
|
||||
}
|
||||
else if (this.state.hover) {
|
||||
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||
return <RoomTooltip name={name}/>;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var BottomLeftMenuTile = sdk.getComponent('rooms.BottomLeftMenuTile');
|
||||
var TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
return (
|
||||
<div className="mx_BottomLeftMenu">
|
||||
<div className="mx_BottomLeftMenu_options">
|
||||
<div className="mx_BottomLeftMenu_createRoom" title="Start chat" onClick={ this.onCreateRoomClick }>
|
||||
<TintableSvg src="img/icons-create-room.svg" width="25" height="25"/>
|
||||
</div>
|
||||
<div className="mx_BottomLeftMenu_directory" title="Room directory" onClick={ this.onRoomDirectoryClick }>
|
||||
<TintableSvg src="img/icons-directory.svg" width="25" height="25"/>
|
||||
</div>
|
||||
<div className="mx_BottomLeftMenu_settings" title="Settings" onClick={ this.onSettingsClick }>
|
||||
<TintableSvg src="img/icons-settings.svg" width="25" height="25"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
66
src/components/structures/CompatibilityPage.js
Normal file
66
src/components/structures/CompatibilityPage.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CompatibilityPage',
|
||||
propTypes: {
|
||||
onAccept: React.PropTypes.func
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onAccept: function() {} // NOP
|
||||
};
|
||||
},
|
||||
|
||||
onAccept: function() {
|
||||
this.props.onAccept();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
return (
|
||||
<div className="mx_CompatibilityPage">
|
||||
<div className="mx_CompatibilityPage_box">
|
||||
<p>Sorry, your browser is <b>not</b> able to run Vector.</p>
|
||||
<p>
|
||||
Vector uses many advanced browser features, some of which are not
|
||||
available or experimental in your current browser.
|
||||
</p>
|
||||
<p>
|
||||
Please install <a href="https://www.google.com/chrome">Chrome</a> or
|
||||
<a href="https://getfirefox.com">Firefox</a> for the best experience.
|
||||
<a href="http://apple.com/safari">Safari</a> and
|
||||
<a href="http://opera.com">Opera</a> work too.
|
||||
</p>
|
||||
<p>
|
||||
With your current browser, the look and feel of the application may
|
||||
be completely incorrect, and some or all features may not function.
|
||||
If you want to try it anyway you can continue, but you are on your own
|
||||
in terms of any issues you may encounter!
|
||||
</p>
|
||||
<button onClick={this.onAccept}>
|
||||
I understand the risks and wish to continue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
133
src/components/structures/LeftPanel.js
Normal file
133
src/components/structures/LeftPanel.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var DragDropContext = require('react-dnd').DragDropContext;
|
||||
var HTML5Backend = require('react-dnd-html5-backend');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
var VectorConferenceHandler = require('../../VectorConferenceHandler');
|
||||
var CallHandler = require("matrix-react-sdk/lib/CallHandler");
|
||||
|
||||
var LeftPanel = React.createClass({
|
||||
displayName: 'LeftPanel',
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
showCallElement: null,
|
||||
searchFilter: '',
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
this._recheckCallElement(newProps.selectedRoom);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
switch (payload.action) {
|
||||
// listen for call state changes to prod the render method, which
|
||||
// may hide the global CallView if the call it is tracking is dead
|
||||
case 'call_state':
|
||||
this._recheckCallElement(this.props.selectedRoom);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_recheckCallElement: function(selectedRoomId) {
|
||||
// if we aren't viewing a room with an ongoing call, but there is an
|
||||
// active call, show the call element - we need to do this to make
|
||||
// audio/video not crap out
|
||||
var activeCall = CallHandler.getAnyActiveCall();
|
||||
var callForRoom = CallHandler.getCallForRoom(selectedRoomId);
|
||||
var showCall = (activeCall && activeCall.call_state === 'connected' && !callForRoom);
|
||||
this.setState({
|
||||
showCallElement: showCall
|
||||
});
|
||||
},
|
||||
|
||||
onHideClick: function() {
|
||||
dis.dispatch({
|
||||
action: 'hide_left_panel',
|
||||
});
|
||||
},
|
||||
|
||||
onCallViewClick: function() {
|
||||
var call = CallHandler.getAnyActiveCall();
|
||||
if (call) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: call.groupRoomId || call.roomId,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onSearch: function(term) {
|
||||
this.setState({ searchFilter: term });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var RoomList = sdk.getComponent('rooms.RoomList');
|
||||
var BottomLeftMenu = sdk.getComponent('structures.BottomLeftMenu');
|
||||
var SearchBox = sdk.getComponent('structures.SearchBox');
|
||||
|
||||
var collapseButton;
|
||||
var classes = "mx_LeftPanel mx_fadable";
|
||||
if (this.props.collapsed) {
|
||||
classes += " collapsed";
|
||||
}
|
||||
else {
|
||||
// Hide the collapse button until we work out how to display it in the new skin
|
||||
// collapseButton = <img className="mx_LeftPanel_hideButton" onClick={ this.onHideClick } src="img/hide.png" width="12" height="20" alt="<"/>
|
||||
}
|
||||
|
||||
var callPreview;
|
||||
if (this.state.showCallElement && !this.props.collapsed) {
|
||||
var CallView = sdk.getComponent('voip.CallView');
|
||||
callPreview = (
|
||||
<CallView
|
||||
className="mx_LeftPanel_callView" onClick={this.onCallViewClick}
|
||||
ConferenceHandler={VectorConferenceHandler} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className={classes} style={{ opacity: this.props.opacity }}>
|
||||
<SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />
|
||||
{ collapseButton }
|
||||
{ callPreview }
|
||||
<RoomList
|
||||
selectedRoom={this.props.selectedRoom}
|
||||
collapsed={this.props.collapsed}
|
||||
searchFilter={this.state.searchFilter}
|
||||
ConferenceHandler={VectorConferenceHandler} />
|
||||
<BottomLeftMenu collapsed={this.props.collapsed}/>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = DragDropContext(HTML5Backend)(LeftPanel);
|
||||
181
src/components/structures/RightPanel.js
Normal file
181
src/components/structures/RightPanel.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
|
||||
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RightPanel',
|
||||
|
||||
Phase : {
|
||||
MemberList: 'MemberList',
|
||||
FileList: 'FileList',
|
||||
MemberInfo: 'MemberInfo',
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
var cli = MatrixClientPeg.get();
|
||||
cli.on("RoomState.members", this.onRoomStateMember);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
if (MatrixClientPeg.get()) {
|
||||
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
phase : this.Phase.MemberList
|
||||
}
|
||||
},
|
||||
|
||||
onMemberListButtonClick: function() {
|
||||
if (this.props.collapsed) {
|
||||
this.setState({ phase: this.Phase.MemberList });
|
||||
dis.dispatch({
|
||||
action: 'show_right_panel',
|
||||
});
|
||||
}
|
||||
else {
|
||||
dis.dispatch({
|
||||
action: 'hide_right_panel',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onRoomStateMember: function(ev, state, member) {
|
||||
// redraw the badge on the membership list
|
||||
if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) {
|
||||
this._delayedUpdate();
|
||||
}
|
||||
else if (this.state.phase === this.Phase.MemberInfo && member.roomId === this.props.roomId &&
|
||||
member.userId === this.state.member.userId) {
|
||||
// refresh the member info (e.g. new power level)
|
||||
this._delayedUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
_delayedUpdate: new rate_limited_func(function() {
|
||||
this.forceUpdate();
|
||||
}, 500),
|
||||
|
||||
onAction: function(payload) {
|
||||
if (payload.action === "view_user") {
|
||||
dis.dispatch({
|
||||
action: 'show_right_panel',
|
||||
});
|
||||
if (payload.member) {
|
||||
this.setState({
|
||||
phase: this.Phase.MemberInfo,
|
||||
member: payload.member,
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.setState({
|
||||
phase: this.Phase.MemberList
|
||||
});
|
||||
}
|
||||
}
|
||||
if (payload.action === "view_room") {
|
||||
if (this.state.phase === this.Phase.MemberInfo) {
|
||||
this.setState({
|
||||
phase: this.Phase.MemberList
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var MemberList = sdk.getComponent('rooms.MemberList');
|
||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
var buttonGroup;
|
||||
var panel;
|
||||
|
||||
var filesHighlight;
|
||||
var membersHighlight;
|
||||
if (!this.props.collapsed) {
|
||||
if (this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) {
|
||||
membersHighlight = <div className="mx_RightPanel_headerButton_highlight"></div>;
|
||||
}
|
||||
else if (this.state.phase == this.Phase.FileList) {
|
||||
filesHighlight = <div className="mx_RightPanel_headerButton_highlight"></div>;
|
||||
}
|
||||
}
|
||||
|
||||
var membersBadge;
|
||||
if ((this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) && this.props.roomId) {
|
||||
var cli = MatrixClientPeg.get();
|
||||
var room = cli.getRoom(this.props.roomId);
|
||||
if (room) {
|
||||
membersBadge = <div className="mx_RightPanel_headerButton_badge">{ room.getJoinedMembers().length }</div>;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.roomId) {
|
||||
buttonGroup =
|
||||
<div className="mx_RightPanel_headerButtonGroup">
|
||||
<div className="mx_RightPanel_headerButton" title="Members" onClick={ this.onMemberListButtonClick }>
|
||||
{ membersBadge }
|
||||
<TintableSvg src="img/icons-people.svg" width="25" height="25"/>
|
||||
{ membersHighlight }
|
||||
</div>
|
||||
<div className="mx_RightPanel_headerButton mx_RightPanel_filebutton" title="Files">
|
||||
<TintableSvg src="img/files.svg" width="17" height="22"/>
|
||||
{ filesHighlight }
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
if (!this.props.collapsed) {
|
||||
if(this.state.phase == this.Phase.MemberList) {
|
||||
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
||||
}
|
||||
else if(this.state.phase == this.Phase.MemberInfo) {
|
||||
var MemberInfo = sdk.getComponent('rooms.MemberInfo');
|
||||
panel = <MemberInfo roomId={this.props.roomId} member={this.state.member} key={this.props.roomId} />
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!panel) {
|
||||
panel = <div className="mx_RightPanel_blank"></div>;
|
||||
}
|
||||
|
||||
var classes = "mx_RightPanel mx_fadable";
|
||||
if (this.props.collapsed) {
|
||||
classes += " collapsed";
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className={classes} style={{ opacity: this.props.opacity }}>
|
||||
<div className="mx_RightPanel_header">
|
||||
{ buttonGroup }
|
||||
</div>
|
||||
{ panel }
|
||||
<div className="mx_RightPanel_footer">
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
302
src/components/structures/RoomDirectory.js
Normal file
302
src/components/structures/RoomDirectory.js
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var ContentRepo = require("matrix-js-sdk").ContentRepo;
|
||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
|
||||
var linkify = require('linkifyjs');
|
||||
var linkifyString = require('linkifyjs/string');
|
||||
var linkifyMatrix = require('matrix-react-sdk/lib/linkify-matrix');
|
||||
var sanitizeHtml = require('sanitize-html');
|
||||
|
||||
linkifyMatrix(linkify);
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomDirectory',
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
publicRooms: [],
|
||||
roomAlias: '',
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// dis.dispatch({
|
||||
// action: 'ui_opacity',
|
||||
// sideOpacity: 0.3,
|
||||
// middleOpacity: 0.3,
|
||||
// });
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.getPublicRooms();
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
// dis.dispatch({
|
||||
// action: 'ui_opacity',
|
||||
// sideOpacity: 1.0,
|
||||
// middleOpacity: 1.0,
|
||||
// });
|
||||
},
|
||||
|
||||
getPublicRooms: function() {
|
||||
var self = this;
|
||||
MatrixClientPeg.get().publicRooms(function (err, data) {
|
||||
if (err) {
|
||||
self.setState({ loading: false });
|
||||
console.error("Failed to get publicRooms: %s", JSON.stringify(err));
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to get public room list",
|
||||
description: err.message
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.setState({
|
||||
publicRooms: data.chunk,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* A limited interface for removing rooms from the directory.
|
||||
* Will set the room to not be publicly visible and delete the
|
||||
* default alias. In the long term, it would be better to allow
|
||||
* HS admins to do this through the RoomSettings interface, but
|
||||
* this needs SPEC-417.
|
||||
*/
|
||||
removeFromDirectory: function(room) {
|
||||
var alias = get_display_alias_for_room(room);
|
||||
var name = room.name || alias || "Unnamed room";
|
||||
|
||||
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
|
||||
var desc;
|
||||
if (alias) {
|
||||
desc = `Delete the room alias '${alias}' and remove '${name}' from the directory?`;
|
||||
} else {
|
||||
desc = `Remove '${name}' from the directory?`;
|
||||
}
|
||||
|
||||
Modal.createDialog(QuestionDialog, {
|
||||
title: "Remove from Directory",
|
||||
description: desc,
|
||||
onFinished: (should_delete) => {
|
||||
if (!should_delete) return;
|
||||
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
var modal = Modal.createDialog(Loader);
|
||||
var step = `remove '${name}' from the directory.`;
|
||||
|
||||
MatrixClientPeg.get().setRoomDirectoryVisibility(room.room_id, 'private').then(() => {
|
||||
if (!alias) return;
|
||||
step = 'delete the alias.';
|
||||
return MatrixClientPeg.get().deleteAlias(alias);
|
||||
}).done(() => {
|
||||
modal.close();
|
||||
this.getPublicRooms();
|
||||
}, function(err) {
|
||||
modal.close();
|
||||
this.getPublicRooms();
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to "+step,
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onRoomClicked: function(room, ev) {
|
||||
if (ev.shiftKey) {
|
||||
ev.preventDefault();
|
||||
this.removeFromDirectory(room);
|
||||
} else {
|
||||
this.showRoom(room);
|
||||
}
|
||||
},
|
||||
|
||||
showRoomAlias: function(alias) {
|
||||
this.showRoom(null, alias);
|
||||
},
|
||||
|
||||
showRoom: function(room, room_alias) {
|
||||
var payload = {action: 'view_room'};
|
||||
if (room) {
|
||||
// Don't let the user view a room they won't be able to either
|
||||
// peek or join: fail earlier so they don't have to click back
|
||||
// to the directory.
|
||||
if (MatrixClientPeg.get().isGuest()) {
|
||||
if (!room.world_readable && !room.guest_can_join) {
|
||||
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
||||
Modal.createDialog(NeedToRegisterDialog, {
|
||||
title: "Failed to join the room",
|
||||
description: "This room is inaccessible to guests. You may be able to join if you register."
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!room_alias) {
|
||||
room_alias = get_display_alias_for_room(room);
|
||||
}
|
||||
|
||||
payload.oob_data = {
|
||||
avatarUrl: room.avatar_url,
|
||||
// XXX: This logic is duplicated from the JS SDK which
|
||||
// would normally decide what the name is.
|
||||
name: room.name || room_alias || "Unnamed room",
|
||||
};
|
||||
}
|
||||
// It's not really possible to join Matrix rooms by ID because the HS has no way to know
|
||||
// which servers to start querying. However, there's no other way to join rooms in
|
||||
// this list without aliases at present, so if roomAlias isn't set here we have no
|
||||
// choice but to supply the ID.
|
||||
if (room_alias) {
|
||||
payload.room_alias = room_alias;
|
||||
} else {
|
||||
payload.room_id = room.room_id;
|
||||
}
|
||||
dis.dispatch(payload);
|
||||
},
|
||||
|
||||
getRows: function(filter) {
|
||||
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||
|
||||
if (!this.state.publicRooms) return [];
|
||||
|
||||
var rooms = this.state.publicRooms.filter(function(a) {
|
||||
// FIXME: if incrementally typing, keep narrowing down the search set
|
||||
// incrementally rather than starting over each time.
|
||||
return (((a.name && a.name.toLowerCase().search(filter.toLowerCase()) >= 0) ||
|
||||
(a.aliases && a.aliases[0].toLowerCase().search(filter.toLowerCase()) >= 0)) &&
|
||||
a.num_joined_members > 0);
|
||||
}).sort(function(a,b) {
|
||||
return a.num_joined_members - b.num_joined_members;
|
||||
});
|
||||
var rows = [];
|
||||
var self = this;
|
||||
var guestRead, guestJoin, perms;
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var name = rooms[i].name || get_display_alias_for_room(rooms[i]) || "Unnamed room";
|
||||
guestRead = null;
|
||||
guestJoin = null;
|
||||
|
||||
if (rooms[i].world_readable) {
|
||||
guestRead = (
|
||||
<div className="mx_RoomDirectory_perm">World readable</div>
|
||||
);
|
||||
}
|
||||
if (rooms[i].guest_can_join) {
|
||||
guestJoin = (
|
||||
<div className="mx_RoomDirectory_perm">Guests can join</div>
|
||||
);
|
||||
}
|
||||
|
||||
perms = null;
|
||||
if (guestRead || guestJoin) {
|
||||
perms = <div className="mx_RoomDirectory_perms">{guestRead} {guestJoin}</div>;
|
||||
}
|
||||
|
||||
var topic = rooms[i].topic || '';
|
||||
topic = linkifyString(sanitizeHtml(topic));
|
||||
|
||||
rows.unshift(
|
||||
<tr key={ rooms[i].room_id }
|
||||
onClick={self.onRoomClicked.bind(self, rooms[i])}
|
||||
// cancel onMouseDown otherwise shift-clicking highlights text
|
||||
onMouseDown={(ev) => {ev.preventDefault();}}
|
||||
>
|
||||
<td className="mx_RoomDirectory_roomAvatar">
|
||||
<BaseAvatar width={24} height={24} resizeMethod='crop'
|
||||
name={ name } idName={ name }
|
||||
url={ ContentRepo.getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
rooms[i].avatar_url, 24, 24, "crop") } />
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomDescription">
|
||||
<div className="mx_RoomDirectory_name">{ name }</div>
|
||||
{ perms }
|
||||
<div className="mx_RoomDirectory_topic"
|
||||
onClick={ function(e) { e.stopPropagation() } }
|
||||
dangerouslySetInnerHTML={{ __html: topic }}/>
|
||||
<div className="mx_RoomDirectory_alias">{ get_display_alias_for_room(rooms[i]) }</div>
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomMemberCount">
|
||||
{ rooms[i].num_joined_members }
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
|
||||
onKeyUp: function(ev) {
|
||||
this.forceUpdate();
|
||||
this.setState({ roomAlias : this.refs.roomAlias.value })
|
||||
if (ev.key == "Enter") {
|
||||
this.showRoomAlias(this.refs.roomAlias.value);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.loading) {
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
var SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<SimpleRoomHeader title="Directory" />
|
||||
<div className="mx_RoomDirectory_list">
|
||||
<input ref="roomAlias" placeholder="Join a room (e.g. #foo:domain.com)" className="mx_RoomDirectory_input" size="64" onKeyUp={ this.onKeyUp }/>
|
||||
<GeminiScrollbar className="mx_RoomDirectory_tableWrapper">
|
||||
<table ref="directory_table" className="mx_RoomDirectory_table">
|
||||
<tbody>
|
||||
{ this.getRows(this.state.roomAlias) }
|
||||
</tbody>
|
||||
</table>
|
||||
</GeminiScrollbar>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
|
||||
// but works with the objects we get from the public room list
|
||||
function get_display_alias_for_room(room) {
|
||||
return room.canonical_alias || (room.aliases ? room.aliases[0] : "");
|
||||
}
|
||||
429
src/components/structures/RoomSubList.js
Normal file
429
src/components/structures/RoomSubList.js
Normal file
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var DropTarget = require('react-dnd').DropTarget;
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var Unread = require('matrix-react-sdk/lib/Unread');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
|
||||
// turn this on for drop & drag console debugging galore
|
||||
var debug = false;
|
||||
|
||||
var roomListTarget = {
|
||||
canDrop: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
drop: function(props, monitor, component) {
|
||||
if (debug) console.log("dropped on sublist")
|
||||
},
|
||||
|
||||
hover: function(props, monitor, component) {
|
||||
var item = monitor.getItem();
|
||||
|
||||
if (component.state.sortedList.length == 0 && props.editable) {
|
||||
if (debug) console.log("hovering on sublist " + props.label + ", isOver=" + monitor.isOver());
|
||||
|
||||
if (item.targetList !== component) {
|
||||
item.targetList.removeRoomTile(item.room);
|
||||
item.targetList = component;
|
||||
}
|
||||
|
||||
component.moveRoomTile(item.room, 0);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var RoomSubList = React.createClass({
|
||||
displayName: 'RoomSubList',
|
||||
|
||||
debug: debug,
|
||||
|
||||
propTypes: {
|
||||
list: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
|
||||
label: React.PropTypes.string.isRequired,
|
||||
tagName: React.PropTypes.string,
|
||||
editable: React.PropTypes.bool,
|
||||
|
||||
order: React.PropTypes.string.isRequired,
|
||||
|
||||
// undefined if no room is selected (eg we are showing settings)
|
||||
selectedRoom: React.PropTypes.string,
|
||||
|
||||
startAsHidden: React.PropTypes.bool,
|
||||
showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded
|
||||
collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
|
||||
onHeaderClick: React.PropTypes.func,
|
||||
alwaysShowHeader: React.PropTypes.bool,
|
||||
incomingCall: React.PropTypes.object,
|
||||
onShowMoreRooms: React.PropTypes.func,
|
||||
searchFilter: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
hidden: this.props.startAsHidden || false,
|
||||
truncateAt: 20,
|
||||
sortedList: [],
|
||||
};
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onHeaderClick: function() {}, // NOP
|
||||
onShowMoreRooms: function() {} // NOP
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
// order the room list appropriately before we re-render
|
||||
//if (debug) console.log("received new props, list = " + newProps.list);
|
||||
this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order);
|
||||
},
|
||||
|
||||
applySearchFilter: function(list, filter) {
|
||||
if (filter === "") return list;
|
||||
return list.filter((room) => {
|
||||
return room.name && room.name.toLowerCase().indexOf(filter.toLowerCase()) >= 0
|
||||
});
|
||||
},
|
||||
|
||||
onClick: function(ev) {
|
||||
var isHidden = !this.state.hidden;
|
||||
this.setState({ hidden : isHidden });
|
||||
|
||||
if (isHidden) {
|
||||
// as good a way as any to reset the truncate state
|
||||
this.setState({ truncateAt : 20 });
|
||||
this.props.onShowMoreRooms();
|
||||
}
|
||||
|
||||
this.props.onHeaderClick(isHidden);
|
||||
},
|
||||
|
||||
tsOfNewestEvent: function(room) {
|
||||
for (var i = room.timeline.length - 1; i >= 0; --i) {
|
||||
var ev = room.timeline[i];
|
||||
if (Unread.eventTriggersUnreadCount(ev) ||
|
||||
(ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId))
|
||||
{
|
||||
return ev.getTs();
|
||||
}
|
||||
}
|
||||
|
||||
// we might only have events that don't trigger the unread indicator,
|
||||
// in which case use the oldest event even if normally it wouldn't count.
|
||||
// This is better than just assuming the last event was forever ago.
|
||||
if (room.timeline.length) {
|
||||
return room.timeline[0].getTs();
|
||||
} else {
|
||||
return Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: factor the comparators back out into a generic comparator
|
||||
// so that view_prev_room and view_next_room can do the right thing
|
||||
|
||||
recentsComparator: function(roomA, roomB) {
|
||||
return this.tsOfNewestEvent(roomB) - this.tsOfNewestEvent(roomA);
|
||||
},
|
||||
|
||||
lexicographicalComparator: function(roomA, roomB) {
|
||||
return roomA.name > roomB.name ? 1 : -1;
|
||||
},
|
||||
|
||||
// Generates the manual comparator using the given list
|
||||
manualComparator: function(roomA, roomB) {
|
||||
if (!roomA.tags[this.props.tagName] || !roomB.tags[this.props.tagName]) return 0;
|
||||
|
||||
// Make sure the room tag has an order element, if not set it to be the bottom
|
||||
var a = roomA.tags[this.props.tagName].order;
|
||||
var b = roomB.tags[this.props.tagName].order;
|
||||
|
||||
// Order undefined room tag orders to the bottom
|
||||
if (a === undefined && b !== undefined) {
|
||||
return 1;
|
||||
} else if (a !== undefined && b === undefined) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return a == b ? this.lexicographicalComparator(roomA, roomB) : ( a > b ? 1 : -1);
|
||||
},
|
||||
|
||||
sortList: function(list, order) {
|
||||
if (list === undefined) list = this.state.sortedList;
|
||||
if (order === undefined) order = this.props.order;
|
||||
var comparator;
|
||||
list = list || [];
|
||||
if (order === "manual") comparator = this.manualComparator;
|
||||
if (order === "recent") comparator = this.recentsComparator;
|
||||
|
||||
// Fix undefined orders here, and make sure the backend gets updated as well
|
||||
this._fixUndefinedOrder(list);
|
||||
|
||||
//if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list);
|
||||
this.setState({ sortedList: list.sort(comparator) });
|
||||
},
|
||||
|
||||
moveRoomTile: function(room, atIndex) {
|
||||
if (debug) console.log("moveRoomTile: id " + room.roomId + ", atIndex " + atIndex);
|
||||
//console.log("moveRoomTile before: " + JSON.stringify(this.state.rooms));
|
||||
var found = this.findRoomTile(room);
|
||||
var rooms = this.state.sortedList;
|
||||
if (found.room) {
|
||||
if (debug) console.log("removing at index " + found.index + " and adding at index " + atIndex);
|
||||
rooms.splice(found.index, 1);
|
||||
rooms.splice(atIndex, 0, found.room);
|
||||
}
|
||||
else {
|
||||
if (debug) console.log("Adding at index " + atIndex);
|
||||
rooms.splice(atIndex, 0, room);
|
||||
}
|
||||
this.setState({ sortedList: rooms });
|
||||
// console.log("moveRoomTile after: " + JSON.stringify(this.state.rooms));
|
||||
},
|
||||
|
||||
// XXX: this isn't invoked via a property method but indirectly via
|
||||
// the roomList property method. Unsure how evil this is.
|
||||
removeRoomTile: function(room) {
|
||||
if (debug) console.log("remove room " + room.roomId);
|
||||
var found = this.findRoomTile(room);
|
||||
var rooms = this.state.sortedList;
|
||||
if (found.room) {
|
||||
rooms.splice(found.index, 1);
|
||||
}
|
||||
else {
|
||||
console.warn("Can't remove room " + room.roomId + " - can't find it");
|
||||
}
|
||||
this.setState({ sortedList: rooms });
|
||||
},
|
||||
|
||||
findRoomTile: function(room) {
|
||||
var index = this.state.sortedList.indexOf(room);
|
||||
if (index >= 0) {
|
||||
// console.log("found: room: " + room.roomId + " with index " + index);
|
||||
}
|
||||
else {
|
||||
if (debug) console.log("didn't find room");
|
||||
room = null;
|
||||
}
|
||||
return ({
|
||||
room: room,
|
||||
index: index,
|
||||
});
|
||||
},
|
||||
|
||||
calcManualOrderTagData: function(room) {
|
||||
var index = this.state.sortedList.indexOf(room);
|
||||
|
||||
// we sort rooms by the lexicographic ordering of the 'order' metadata on their tags.
|
||||
// for convenience, we calculate this for now a floating point number between 0.0 and 1.0.
|
||||
|
||||
var orderA = 0.0; // by default we're next to the beginning of the list
|
||||
if (index > 0) {
|
||||
var prevTag = this.state.sortedList[index - 1].tags[this.props.tagName];
|
||||
if (!prevTag) {
|
||||
console.error("Previous room in sublist is not tagged to be in this list. This should never happen.")
|
||||
}
|
||||
else if (prevTag.order === undefined) {
|
||||
console.error("Previous room in sublist has no ordering metadata. This should never happen.");
|
||||
}
|
||||
else {
|
||||
orderA = prevTag.order;
|
||||
}
|
||||
}
|
||||
|
||||
var orderB = 1.0; // by default we're next to the end of the list too
|
||||
if (index < this.state.sortedList.length - 1) {
|
||||
var nextTag = this.state.sortedList[index + 1].tags[this.props.tagName];
|
||||
if (!nextTag) {
|
||||
console.error("Next room in sublist is not tagged to be in this list. This should never happen.")
|
||||
}
|
||||
else if (nextTag.order === undefined) {
|
||||
console.error("Next room in sublist has no ordering metadata. This should never happen.");
|
||||
}
|
||||
else {
|
||||
orderB = nextTag.order;
|
||||
}
|
||||
}
|
||||
|
||||
var order = (orderA + orderB) / 2.0;
|
||||
if (order === orderA || order === orderB) {
|
||||
console.error("Cannot describe new list position. This should be incredibly unlikely.");
|
||||
// TODO: renumber the list
|
||||
}
|
||||
|
||||
return order;
|
||||
},
|
||||
|
||||
makeRoomTiles: function() {
|
||||
var self = this;
|
||||
var RoomTile = sdk.getComponent("rooms.RoomTile");
|
||||
return this.state.sortedList.map(function(room) {
|
||||
var selected = room.roomId == self.props.selectedRoom;
|
||||
// XXX: is it evil to pass in self as a prop to RoomTile?
|
||||
return (
|
||||
<RoomTile
|
||||
room={ room }
|
||||
roomSubList={ self }
|
||||
key={ room.roomId }
|
||||
collapsed={ self.props.collapsed || false}
|
||||
selected={ selected }
|
||||
unread={ Unread.doesRoomHaveUnreadMessages(room) }
|
||||
highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites' }
|
||||
isInvite={ self.props.label === 'Invites' }
|
||||
incomingCall={ self.props.incomingCall && (self.props.incomingCall.roomId === room.roomId) ? self.props.incomingCall : null } />
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
_getHeaderJsx: function() {
|
||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
return (
|
||||
<h2 onClick={ this.onClick } className="mx_RoomSubList_label">
|
||||
{ this.props.collapsed ? '' : this.props.label }
|
||||
<img className="mx_RoomSubList_chevron"
|
||||
src={ this.state.hidden ? "img/list-close.svg" : "img/list-open.svg" }
|
||||
width="10" height="10" />
|
||||
</h2>
|
||||
);
|
||||
},
|
||||
|
||||
_createOverflowTile: function(overflowCount, totalCount) {
|
||||
var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||
// XXX: this is duplicated from RoomTile - factor it out
|
||||
return (
|
||||
<div className="mx_RoomTile mx_RoomTile_ellipsis" onClick={this._showFullMemberList}>
|
||||
<div className="mx_RoomTile_avatar">
|
||||
<BaseAvatar url="img/ellipsis.svg" name="..." width={24} height={24} />
|
||||
</div>
|
||||
<div className="mx_RoomTile_name">and { overflowCount } others...</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
_showFullMemberList: function() {
|
||||
this.setState({
|
||||
truncateAt: -1
|
||||
});
|
||||
this.props.onShowMoreRooms();
|
||||
},
|
||||
|
||||
// Fix any undefined order elements of a room in a manual ordered list
|
||||
// room.tag[tagname].order
|
||||
_fixUndefinedOrder: function(list) {
|
||||
if (this.props.order === "manual") {
|
||||
var order = 0.0;
|
||||
var self = this;
|
||||
|
||||
// Find the highest (lowest position) order of a room in a manual ordered list
|
||||
list.forEach(function(room) {
|
||||
if (room.tags.hasOwnProperty(self.props.tagName)) {
|
||||
if (order < room.tags[self.props.tagName].order) {
|
||||
order = room.tags[self.props.tagName].order;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Fix any undefined order elements of a room in a manual ordered list
|
||||
// Do this one at a time, as each time a rooms tag data is updated the RoomList
|
||||
// gets triggered and another list is passed in. Doing it one at a time means that
|
||||
// we always correctly calculate the highest order for the list - stops multiple
|
||||
// rooms getting the same order. This is only really relevant for the first time this
|
||||
// is run with historical room tag data, after that there should only be undefined
|
||||
// in the list at a time anyway.
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].tags[self.props.tagName].order === undefined) {
|
||||
MatrixClientPeg.get().setRoomTag(list[i].roomId, self.props.tagName, {order: (order + 1.0) / 2.0}).finally(function() {
|
||||
// Do any final stuff here
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to add tag " + self.props.tagName + " to room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
break;
|
||||
};
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var connectDropTarget = this.props.connectDropTarget;
|
||||
var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
|
||||
var TruncatedList = sdk.getComponent('elements.TruncatedList');
|
||||
|
||||
var label = this.props.collapsed ? null : this.props.label;
|
||||
|
||||
//console.log("render: " + JSON.stringify(this.state.sortedList));
|
||||
|
||||
var target;
|
||||
if (this.state.sortedList.length == 0 && this.props.editable) {
|
||||
target = <RoomDropTarget label={ 'Drop here to ' + this.props.verb }/>;
|
||||
}
|
||||
|
||||
if (this.state.sortedList.length > 0 || this.props.editable) {
|
||||
var subList;
|
||||
var classes = "mx_RoomSubList";
|
||||
|
||||
if (!this.state.hidden) {
|
||||
subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt}
|
||||
createOverflowElement={this._createOverflowTile} >
|
||||
{ target }
|
||||
{ this.makeRoomTiles() }
|
||||
</TruncatedList>;
|
||||
}
|
||||
else {
|
||||
subList = <TruncatedList className={ classes }>
|
||||
</TruncatedList>;
|
||||
}
|
||||
|
||||
return connectDropTarget(
|
||||
<div>
|
||||
{ this._getHeaderJsx() }
|
||||
{ subList }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
return (
|
||||
<div className="mx_RoomSubList">
|
||||
{ this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined }
|
||||
{ (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Export the wrapped version, inlining the 'collect' functions
|
||||
// to more closely resemble the ES7
|
||||
module.exports =
|
||||
DropTarget('RoomTile', roomListTarget, function(connect) {
|
||||
return {
|
||||
connectDropTarget: connect.dropTarget(),
|
||||
}
|
||||
})(RoomSubList);
|
||||
119
src/components/structures/SearchBox.js
Normal file
119
src/components/structures/SearchBox.js
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'SearchBox',
|
||||
|
||||
propTypes: {
|
||||
collapsed: React.PropTypes.bool,
|
||||
onSearch: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
searchTerm: "",
|
||||
};
|
||||
},
|
||||
|
||||
onChange: function() {
|
||||
if (!this.refs.search) return;
|
||||
this.setState({ searchTerm: this.refs.search.value });
|
||||
this.onSearch();
|
||||
},
|
||||
|
||||
onSearch: new rate_limited_func(
|
||||
function() {
|
||||
this.props.onSearch(this.refs.search.value);
|
||||
},
|
||||
100
|
||||
),
|
||||
|
||||
onToggleCollapse: function(show) {
|
||||
if (show) {
|
||||
dis.dispatch({
|
||||
action: 'show_left_panel',
|
||||
});
|
||||
}
|
||||
else {
|
||||
dis.dispatch({
|
||||
action: 'hide_left_panel',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
|
||||
var toggleCollapse;
|
||||
if (this.props.collapsed) {
|
||||
toggleCollapse =
|
||||
<div className="mx_SearchBox_maximise" onClick={ this.onToggleCollapse.bind(this, true) }>
|
||||
<TintableSvg src="img/maximise.svg" width="10" height="16" alt="<"/>
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
toggleCollapse =
|
||||
<div className="mx_SearchBox_minimise" onClick={ this.onToggleCollapse.bind(this, false) }>
|
||||
<TintableSvg src="img/minimise.svg" width="10" height="16" alt="<"/>
|
||||
</div>
|
||||
}
|
||||
|
||||
var searchControls;
|
||||
if (!this.props.collapsed) {
|
||||
searchControls = [
|
||||
this.state.searchTerm.length > 0 ?
|
||||
<div key="button"
|
||||
className="mx_SearchBox_closeButton"
|
||||
onClick={ ()=>{ this.refs.search.value = ""; this.onChange(); } }>
|
||||
<TintableSvg
|
||||
className="mx_SearchBox_searchButton"
|
||||
src="img/icons-close.svg" width="24" height="24"
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
<TintableSvg
|
||||
key="button"
|
||||
className="mx_SearchBox_searchButton"
|
||||
src="img/icons-search-copy.svg" width="13" height="13"
|
||||
/>,
|
||||
<input
|
||||
key="searchfield"
|
||||
type="text"
|
||||
ref="search"
|
||||
className="mx_SearchBox_search"
|
||||
value={ this.state.searchTerm }
|
||||
onChange={ this.onChange }
|
||||
placeholder="Filter room names"
|
||||
/>
|
||||
];
|
||||
}
|
||||
|
||||
var self = this;
|
||||
return (
|
||||
<div className="mx_SearchBox">
|
||||
{ searchControls }
|
||||
{ toggleCollapse }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
54
src/components/structures/ViewSource.js
Normal file
54
src/components/structures/ViewSource.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ViewSource',
|
||||
|
||||
propTypes: {
|
||||
onFinished: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
document.addEventListener("keydown", this.onKeyDown);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
},
|
||||
|
||||
onKeyDown: function(ev) {
|
||||
if (ev.keyCode == 27) { // escape
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.props.onFinished();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_ViewSource">
|
||||
<pre>
|
||||
{JSON.stringify(this.props.mxEvent.event, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
156
src/components/views/context_menus/MessageContextMenu.js
Normal file
156
src/components/views/context_menus/MessageContextMenu.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||
var Resend = require("matrix-react-sdk/lib/Resend");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MessageContextMenu',
|
||||
|
||||
propTypes: {
|
||||
/* the MatrixEvent associated with the context menu */
|
||||
mxEvent: React.PropTypes.object.isRequired,
|
||||
|
||||
/* an optional EventTileOps implementation that can be used to unhide preview widgets */
|
||||
eventTileOps: React.PropTypes.object,
|
||||
|
||||
/* callback called when the menu is dismissed */
|
||||
onFinished: React.PropTypes.func,
|
||||
},
|
||||
|
||||
onResendClick: function() {
|
||||
Resend.resend(this.props.mxEvent);
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
onViewSourceClick: function() {
|
||||
var ViewSource = sdk.getComponent('structures.ViewSource');
|
||||
Modal.createDialog(ViewSource, {
|
||||
mxEvent: this.props.mxEvent
|
||||
}, 'mx_Dialog_viewsource');
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
onRedactClick: function() {
|
||||
MatrixClientPeg.get().redactEvent(
|
||||
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
|
||||
).done(function() {
|
||||
// message should disappear by itself
|
||||
}, function(e) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
// display error message stating you couldn't delete this.
|
||||
var code = e.errcode || e.statusCode;
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "You cannot delete this message. (" + code + ")"
|
||||
});
|
||||
});
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
onCancelSendClick: function() {
|
||||
Resend.removeFromQueue(this.props.mxEvent);
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
onPermalinkClick: function() {
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
onUnhidePreviewClick: function() {
|
||||
if (this.props.eventTileOps) {
|
||||
this.props.eventTileOps.unhideWidget();
|
||||
}
|
||||
if (this.props.onFinished) this.props.onFinished();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var eventStatus = this.props.mxEvent.status;
|
||||
var resendButton;
|
||||
var viewSourceButton;
|
||||
var redactButton;
|
||||
var cancelButton;
|
||||
var permalinkButton;
|
||||
var unhidePreviewButton;
|
||||
|
||||
if (eventStatus === 'not_sent') {
|
||||
resendButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
||||
Resend
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!eventStatus) { // sent
|
||||
redactButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onRedactClick}>
|
||||
Redact
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (eventStatus === "queued" || eventStatus === "not_sent") {
|
||||
cancelButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
||||
Cancel Sending
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
viewSourceButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onViewSourceClick}>
|
||||
View Source
|
||||
</div>
|
||||
);
|
||||
|
||||
if (this.props.eventTileOps) {
|
||||
if (this.props.eventTileOps.isWidgetHidden()) {
|
||||
unhidePreviewButton = (
|
||||
<div className="mx_MessageContextMenu_field" onClick={this.onUnhidePreviewClick}>
|
||||
Unhide Preview
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: this should be https://matrix.to.
|
||||
// XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID)
|
||||
permalinkButton = (
|
||||
<div className="mx_MessageContextMenu_field">
|
||||
<a href={ "#/room/" + this.props.mxEvent.getRoomId() +"/"+ this.props.mxEvent.getId() }
|
||||
onClick={ this.onPermalinkClick }>Permalink</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{resendButton}
|
||||
{redactButton}
|
||||
{cancelButton}
|
||||
{viewSourceButton}
|
||||
{unhidePreviewButton}
|
||||
{permalinkButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var q = require("q");
|
||||
var React = require('react');
|
||||
var classNames = require('classnames');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'NotificationStateContextMenu',
|
||||
|
||||
propTypes: {
|
||||
room: React.PropTypes.object.isRequired,
|
||||
/* callback called when the menu is dismissed */
|
||||
onFinished: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
var areNotifsMuted = false;
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (!cli.isGuest()) {
|
||||
var roomPushRule = cli.getRoomPushRule("global", this.props.room.roomId);
|
||||
if (roomPushRule) {
|
||||
if (0 <= roomPushRule.actions.indexOf("dont_notify")) {
|
||||
areNotifsMuted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
areNotifsMuted: areNotifsMuted,
|
||||
};
|
||||
},
|
||||
|
||||
_save: function( areNotifsMuted ) {
|
||||
var self = this;
|
||||
const roomId = this.props.room.roomId;
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
if (!cli.isGuest()) {
|
||||
// Wrapping this in a q promise, as setRoomMutePushRule can return
|
||||
// a promise or a value
|
||||
q(cli.setRoomMutePushRule("global", roomId, areNotifsMuted))
|
||||
.then(function() {
|
||||
self.setState({areNotifsMuted: areNotifsMuted});
|
||||
|
||||
// delay slightly so that the user can see their state change
|
||||
// before closing the menu
|
||||
return q.delay(500).then(function() {
|
||||
// tell everyone that wants to know of the change in
|
||||
// notification state
|
||||
dis.dispatch({
|
||||
action: 'notification_change',
|
||||
roomId: self.props.room.roomId,
|
||||
areNotifsMuted: areNotifsMuted,
|
||||
});
|
||||
|
||||
// Close the context menu
|
||||
if (self.props.onFinished) {
|
||||
self.props.onFinished();
|
||||
};
|
||||
});
|
||||
}).fail(function(error) {
|
||||
// TODO: some form of error notification to the user
|
||||
// to inform them that their state change failed.
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onClickAlertMe: function() {
|
||||
// Placeholder
|
||||
},
|
||||
|
||||
_onClickAllNotifs: function() {
|
||||
this._save(false);
|
||||
},
|
||||
|
||||
_onClickMentions: function() {
|
||||
this._save(true);
|
||||
},
|
||||
|
||||
_onClickMute: function() {
|
||||
// Placeholder
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
var alertMeClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldDisabled': true,
|
||||
});
|
||||
|
||||
var allNotifsClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldSet': !this.state.areNotifsMuted,
|
||||
});
|
||||
|
||||
var mentionsClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldSet': this.state.areNotifsMuted,
|
||||
});
|
||||
|
||||
var muteNotifsClasses = classNames({
|
||||
'mx_NotificationStateContextMenu_field': true,
|
||||
'mx_NotificationStateContextMenu_fieldDisabled': true,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mx_NotificationStateContextMenu_picker" >
|
||||
<img src="img/notif-slider.svg" width="20" height="107" />
|
||||
</div>
|
||||
<div className={ alertMeClasses } onClick={this._onClickAlertMe} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-off-copy.svg" width="16" height="12" />
|
||||
All messages (loud)
|
||||
</div>
|
||||
<div className={ allNotifsClasses } onClick={this._onClickAllNotifs} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-off.svg" width="16" height="12" />
|
||||
All messages
|
||||
</div>
|
||||
<div className={ mentionsClasses } onClick={this._onClickMentions} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute-mentions.svg" width="16" height="12" />
|
||||
Mentions only
|
||||
</div>
|
||||
<div className={ muteNotifsClasses } onClick={this._onClickMute} >
|
||||
<img className="mx_NotificationStateContextMenu_activeIcon" src="img/notif-active.svg" width="12" height="12" />
|
||||
<img className="mx_NotificationStateContextMenu_icon" src="img/icon-context-mute.svg" width="16" height="12" />
|
||||
Mute
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
171
src/components/views/context_menus/RoomTagContextMenu.js
Normal file
171
src/components/views/context_menus/RoomTagContextMenu.js
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var q = require("q");
|
||||
var React = require('react');
|
||||
var classNames = require('classnames');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTagContextMenu',
|
||||
|
||||
propTypes: {
|
||||
room: React.PropTypes.object.isRequired,
|
||||
/* callback called when the menu is dismissed */
|
||||
onFinished: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
isFavourite: this.props.room.tags.hasOwnProperty("m.favourite"),
|
||||
isLowPriority: this.props.room.tags.hasOwnProperty("m.lowpriority"),
|
||||
};
|
||||
},
|
||||
|
||||
_toggleTag: function(tagNameOn, tagNameOff) {
|
||||
var self = this;
|
||||
const roomId = this.props.room.roomId;
|
||||
var cli = MatrixClientPeg.get();
|
||||
if (!cli.isGuest()) {
|
||||
q.delay(500).then(function() {
|
||||
if (tagNameOff !== null && tagNameOff !== undefined) {
|
||||
cli.deleteRoomTag(roomId, tagNameOff).finally(function() {
|
||||
// Close the context menu
|
||||
if (self.props.onFinished) {
|
||||
self.props.onFinished();
|
||||
};
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to remove tag " + tagNameOff + " from room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (tagNameOn !== null && tagNameOn !== undefined) {
|
||||
// If the tag ordering meta data is required, it is added by
|
||||
// the RoomSubList when it sorts its rooms
|
||||
cli.setRoomTag(roomId, tagNameOn, {}).finally(function() {
|
||||
// Close the context menu
|
||||
if (self.props.onFinished) {
|
||||
self.props.onFinished();
|
||||
};
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to add tag " + tagNameOn + " to room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_onClickFavourite: function() {
|
||||
// Tag room as 'Favourite'
|
||||
if (!this.state.isFavourite && this.state.isLowPriority) {
|
||||
this.setState({
|
||||
isFavourite: true,
|
||||
isLowPriority: false,
|
||||
});
|
||||
this._toggleTag("m.favourite", "m.lowpriority");
|
||||
} else if (this.state.isFavourite) {
|
||||
this.setState({isFavourite: false});
|
||||
this._toggleTag(null, "m.favourite");
|
||||
} else if (!this.state.isFavourite) {
|
||||
this.setState({isFavourite: true});
|
||||
this._toggleTag("m.favourite");
|
||||
}
|
||||
},
|
||||
|
||||
_onClickLowPriority: function() {
|
||||
// Tag room as 'Low Priority'
|
||||
if (!this.state.isLowPriority && this.state.isFavourite) {
|
||||
this.setState({
|
||||
isFavourite: false,
|
||||
isLowPriority: true,
|
||||
});
|
||||
this._toggleTag("m.lowpriority", "m.favourite");
|
||||
} else if (this.state.isLowPriority) {
|
||||
this.setState({isLowPriority: false});
|
||||
this._toggleTag(null, "m.lowpriority");
|
||||
} else if (!this.state.isLowPriority) {
|
||||
this.setState({isLowPriority: true});
|
||||
this._toggleTag("m.lowpriority");
|
||||
}
|
||||
},
|
||||
|
||||
_onClickLeave: function() {
|
||||
// Leave room
|
||||
dis.dispatch({
|
||||
action: 'leave_room',
|
||||
room_id: this.props.room.roomId,
|
||||
});
|
||||
|
||||
// Close the context menu
|
||||
if (this.props.onFinished) {
|
||||
this.props.onFinished();
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
var myMember = this.props.room.getMember(myUserId);
|
||||
|
||||
var favouriteClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': this.state.isFavourite,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
var lowPriorityClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': this.state.isLowPriority,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
var leaveClasses = classNames({
|
||||
'mx_RoomTagContextMenu_field': true,
|
||||
'mx_RoomTagContextMenu_fieldSet': false,
|
||||
'mx_RoomTagContextMenu_fieldDisabled': false,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ favouriteClasses } onClick={this._onClickFavourite} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_fave.svg" width="15" height="15" />
|
||||
<img className="mx_RoomTagContextMenu_icon_set" src="img/icon_context_fave_on.svg" width="15" height="15" />
|
||||
Favourite
|
||||
</div>
|
||||
<div className={ lowPriorityClasses } onClick={this._onClickLowPriority} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_low.svg" width="15" height="15" />
|
||||
<img className="mx_RoomTagContextMenu_icon_set" src="img/icon_context_low_on.svg" width="15" height="15" />
|
||||
Low Priority
|
||||
</div>
|
||||
<hr className="mx_RoomTagContextMenu_separator" />
|
||||
<div className={ leaveClasses } onClick={(myMember && myMember.membership === "join") ? this._onClickLeave : null} >
|
||||
<img className="mx_RoomTagContextMenu_icon" src="img/icon_context_delete.svg" width="15" height="15" />
|
||||
Leave
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
189
src/components/views/elements/ImageView.js
Normal file
189
src/components/views/elements/ImageView.js
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
|
||||
var DateUtils = require('matrix-react-sdk/lib/DateUtils');
|
||||
var filesize = require('filesize');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ImageView',
|
||||
|
||||
propTypes: {
|
||||
src: React.PropTypes.string.isRequired, // the source of the image being displayed
|
||||
name: React.PropTypes.string, // the main title ('name') for the image
|
||||
link: React.PropTypes.string, // the link (if any) applied to the name of the image
|
||||
width: React.PropTypes.number, // width of the image src in pixels
|
||||
height: React.PropTypes.number, // height of the image src in pixels
|
||||
fileSize: React.PropTypes.number, // size of the image src in bytes
|
||||
onFinished: React.PropTypes.func.isRequired, // callback when the lightbox is dismissed
|
||||
|
||||
// the event (if any) that the Image is displaying. Used for event-specific stuff like
|
||||
// redactions, senders, timestamps etc. Other descriptors are taken from the explicit
|
||||
// properties above, which let us use lightboxes to display images which aren't associated
|
||||
// with events.
|
||||
mxEvent: React.PropTypes.object,
|
||||
},
|
||||
|
||||
// XXX: keyboard shortcuts for managing dialogs should be done by the modal
|
||||
// dialog base class somehow, surely...
|
||||
componentDidMount: function() {
|
||||
document.addEventListener("keydown", this.onKeyDown);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
},
|
||||
|
||||
onKeyDown: function(ev) {
|
||||
if (ev.keyCode == 27) { // escape
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.props.onFinished();
|
||||
}
|
||||
},
|
||||
|
||||
onRedactClick: function() {
|
||||
var self = this;
|
||||
MatrixClientPeg.get().redactEvent(
|
||||
this.props.mxEvent.getRoomId(), this.props.mxEvent.getId()
|
||||
).done(function() {
|
||||
if (self.props.onFinished) self.props.onFinished();
|
||||
}, function(e) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
// display error message stating you couldn't delete this.
|
||||
var code = e.errcode || e.statusCode;
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "You cannot delete this image. (" + code + ")"
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getName: function () {
|
||||
var name = this.props.name;
|
||||
if (name && this.props.link) {
|
||||
name = <a href={ this.props.link } target="_blank">{ name }</a>;
|
||||
}
|
||||
return name;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
/*
|
||||
// In theory max-width: 80%, max-height: 80% on the CSS should work
|
||||
// but in practice, it doesn't, so do it manually:
|
||||
|
||||
var width = this.props.width || 500;
|
||||
var height = this.props.height || 500;
|
||||
|
||||
var maxWidth = document.documentElement.clientWidth * 0.8;
|
||||
var maxHeight = document.documentElement.clientHeight * 0.8;
|
||||
|
||||
var widthFrac = width / maxWidth;
|
||||
var heightFrac = height / maxHeight;
|
||||
|
||||
var displayWidth;
|
||||
var displayHeight;
|
||||
if (widthFrac > heightFrac) {
|
||||
displayWidth = Math.min(width, maxWidth);
|
||||
displayHeight = (displayWidth / width) * height;
|
||||
} else {
|
||||
displayHeight = Math.min(height, maxHeight);
|
||||
displayWidth = (displayHeight / height) * width;
|
||||
}
|
||||
|
||||
var style = {
|
||||
width: displayWidth,
|
||||
height: displayHeight
|
||||
};
|
||||
*/
|
||||
var style, res;
|
||||
|
||||
if (this.props.width && this.props.height) {
|
||||
style = {
|
||||
width: this.props.width,
|
||||
height: this.props.height,
|
||||
};
|
||||
res = style.width + "x" + style.height + "px";
|
||||
}
|
||||
|
||||
var size;
|
||||
if (this.props.fileSize) {
|
||||
size = filesize(this.props.fileSize);
|
||||
}
|
||||
|
||||
var size_res;
|
||||
if (size && res) {
|
||||
size_res = size + ", " + res;
|
||||
}
|
||||
else {
|
||||
size_res = size || res;
|
||||
}
|
||||
|
||||
var showEventMeta = !!this.props.mxEvent;
|
||||
|
||||
var eventMeta;
|
||||
if(showEventMeta) {
|
||||
eventMeta = (<div className="mx_ImageView_metadata">
|
||||
Uploaded on { DateUtils.formatDate(new Date(this.props.mxEvent.getTs())) } by { this.props.mxEvent.getSender() }
|
||||
</div>);
|
||||
}
|
||||
|
||||
var eventRedact;
|
||||
if(showEventMeta) {
|
||||
eventRedact = (<div className="mx_ImageView_button" onClick={this.onRedactClick}>
|
||||
Redact
|
||||
</div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_ImageView">
|
||||
<div className="mx_ImageView_lhs">
|
||||
</div>
|
||||
<div className="mx_ImageView_content">
|
||||
<img src={this.props.src} style={style}/>
|
||||
<div className="mx_ImageView_labelWrapper">
|
||||
<div className="mx_ImageView_label">
|
||||
<img className="mx_ImageView_cancel" src="img/cancel-white.svg" width="18" height="18" alt="Close" onClick={ this.props.onFinished }/>
|
||||
<div className="mx_ImageView_shim">
|
||||
</div>
|
||||
<div className="mx_ImageView_name">
|
||||
{ this.getName() }
|
||||
</div>
|
||||
{ eventMeta }
|
||||
<a className="mx_ImageView_link" href={ this.props.src } target="_blank">
|
||||
<div className="mx_ImageView_download">
|
||||
Download this file<br/>
|
||||
<span className="mx_ImageView_size">{ size_res }</span>
|
||||
</div>
|
||||
</a>
|
||||
{ eventRedact }
|
||||
<div className="mx_ImageView_shim">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_ImageView_rhs">
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,15 +18,16 @@ limitations under the License.
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MemberTileController = require("../../../../src/controllers/molecules/MemberTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MemberTile',
|
||||
mixins: [MemberTileController],
|
||||
displayName: 'Spinner',
|
||||
|
||||
render: function() {
|
||||
var w = this.props.w || 32;
|
||||
var h = this.props.h || 32;
|
||||
var imgClass = this.props.imgClassName || "";
|
||||
return (
|
||||
<div className="mx_MemberTile">
|
||||
<div className="mx_MemberTile_name">{this.props.member.name}</div>
|
||||
<div className="mx_Spinner">
|
||||
<img src="img/spinner.gif" width={w} height={h} className={imgClass}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
src/components/views/globals/GuestWarningBar.js
Normal file
43
src/components/views/globals/GuestWarningBar.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher')
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'GuestWarningBar',
|
||||
|
||||
onRegisterClicked: function() {
|
||||
dis.dispatch({'action': 'start_upgrade_registration'});
|
||||
},
|
||||
|
||||
onLoginClicked: function() {
|
||||
dis.dispatch({'action': 'logout'});
|
||||
dis.dispatch({'action': 'start_login'});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_GuestWarningBar">
|
||||
<img className="mx_GuestWarningBar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
|
||||
<div>
|
||||
You are using Vector as a guest. <a onClick={this.onRegisterClicked}>Register</a> or <a onClick={this.onLoginClicked}>log in</a> to access more rooms and features.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,25 +17,29 @@ limitations under the License.
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var LogoutButton = ComponentBroker.get("atoms/LogoutButton");
|
||||
var EnableNotificationsButton = ComponentBroker.get("atoms/EnableNotificationsButton");
|
||||
|
||||
var MatrixToolbarController = require("../../../../src/controllers/molecules/MatrixToolbar");
|
||||
var Notifier = require("matrix-react-sdk/lib/Notifier");
|
||||
var sdk = require('matrix-react-sdk')
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MatrixToolbar',
|
||||
mixins: [MatrixToolbarController],
|
||||
|
||||
hideToolbar: function() {
|
||||
Notifier.setToolbarHidden(true);
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
Notifier.setEnabled(true);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_MatrixToolbar">
|
||||
<LogoutButton />
|
||||
<EnableNotificationsButton />
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
|
||||
<div className="mx_MatrixToolbar_content">
|
||||
You are not receiving desktop notifications. <a className="mx_MatrixToolbar_link" onClick={ this.onClick }>Enable them now</a>
|
||||
</div>
|
||||
<div className="mx_MatrixToolbar_close"><img src="img/cancel.svg" width="18" height="18" onClick={ this.hideToolbar } /></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,19 +17,20 @@ limitations under the License.
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var RoomHeaderController = require("../../../../src/controllers/molecules/RoomHeader");
|
||||
var sdk = require('matrix-react-sdk')
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomHeader',
|
||||
mixins: [RoomHeaderController],
|
||||
displayName: 'NewVersionBar',
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_RoomHeader">
|
||||
{this.props.room.name}
|
||||
<div className="mx_MatrixToolbar">
|
||||
<img className="mx_MatrixToolbar_warning" src="img/warning.svg" width="24" height="23" alt="/!\"/>
|
||||
<div>
|
||||
A new version of Vector is available. Refresh your browser.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
52
src/components/views/login/VectorCustomServerDialog.js
Normal file
52
src/components/views/login/VectorCustomServerDialog.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
var React = require("react");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'VectorCustomServerDialog',
|
||||
statics: {
|
||||
replaces: 'CustomServerDialog',
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_ErrorDialog">
|
||||
<div className="mx_Dialog_title">
|
||||
Custom Server Options
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
<span>
|
||||
You can use the custom server options to log into other Matrix
|
||||
servers by specifying a different Home server URL.
|
||||
<br/>
|
||||
This allows you to use Vector with an existing Matrix account on
|
||||
a different home server.
|
||||
<br/>
|
||||
<br/>
|
||||
You can also set a custom identity server but you won't be able to
|
||||
invite users by email address, or be invited by email address yourself.
|
||||
</span>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.props.onFinished} autoFocus={true}>
|
||||
Dismiss
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,18 +18,20 @@ limitations under the License.
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MessageComposerController = require("../../../../src/controllers/molecules/MessageComposer");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MessageComposer',
|
||||
mixins: [MessageComposerController],
|
||||
displayName: 'VectorLoginFooter',
|
||||
statics: {
|
||||
replaces: 'LoginFooter',
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_MessageComposer">
|
||||
<textarea ref="textarea" onKeyDown={this.onKeyDown} />
|
||||
<div className="mx_Login_links">
|
||||
<a href="https://medium.com/@Vector">blog</a> ·
|
||||
<a href="https://twitter.com/@VectorCo">twitter</a> ·
|
||||
<a href="https://github.com/vector-im/vector-web">github</a> ·
|
||||
<a href="https://matrix.org">powered by Matrix</a>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,19 +18,17 @@ limitations under the License.
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var RoomListController = require("../../../../src/controllers/organisms/RoomList");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomList',
|
||||
mixins: [RoomListController],
|
||||
displayName: 'VectorLoginHeader',
|
||||
statics: {
|
||||
replaces: 'LoginHeader',
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_RoomList">
|
||||
{this.makeRoomTiles()}
|
||||
<div className="mx_Login_logo">
|
||||
<img src="img/logo.png" width="249" height="78" alt="vector"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
56
src/components/views/messages/DateSeparator.js
Normal file
56
src/components/views/messages/DateSeparator.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var days = [
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday"
|
||||
];
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'DateSeparator',
|
||||
render: function() {
|
||||
var date = new Date(this.props.ts);
|
||||
var today = new Date();
|
||||
var yesterday = new Date();
|
||||
yesterday.setDate(today.getDate() - 1);
|
||||
var label;
|
||||
if (date.toDateString() === today.toDateString()) {
|
||||
label = "Today";
|
||||
}
|
||||
else if (date.toDateString() === yesterday.toDateString()) {
|
||||
label = "Yesterday";
|
||||
}
|
||||
else if (today.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) {
|
||||
label = days[date.getDay()];
|
||||
}
|
||||
else {
|
||||
label = date.toDateString();
|
||||
}
|
||||
|
||||
return (
|
||||
<h2>{ label }</h2>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,18 +17,16 @@ limitations under the License.
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var MessageTimestampController = require("../../../../src/controllers/atoms/MessageTimestamp");
|
||||
var DateUtils = require('matrix-react-sdk/lib/DateUtils');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MessageTimestamp',
|
||||
mixins: [MessageTimestampController],
|
||||
|
||||
render: function() {
|
||||
var date = new Date(this.props.ts);
|
||||
return (
|
||||
<span className="mx_MessageTimestamp">
|
||||
{date.toLocaleTimeString()}
|
||||
{ DateUtils.formatDate(date) }
|
||||
</span>
|
||||
);
|
||||
},
|
||||
57
src/components/views/rooms/BottomLeftMenuTile.js
Normal file
57
src/components/views/rooms/BottomLeftMenuTile.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var sdk = require('matrix-react-sdk')
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'BottomLeftMenuTile',
|
||||
|
||||
getInitialState: function() {
|
||||
return( { hover : false });
|
||||
},
|
||||
|
||||
onMouseEnter: function() {
|
||||
this.setState( { hover : true });
|
||||
},
|
||||
|
||||
onMouseLeave: function() {
|
||||
this.setState( { hover : false });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var label;
|
||||
if (!this.props.collapsed) {
|
||||
label = <div className="mx_RoomTile_name">{ this.props.label }</div>;
|
||||
}
|
||||
else if (this.state.hover) {
|
||||
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||
label = <RoomTooltip bottom={ true } label={ this.props.label }/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomTile" onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} onClick={this.props.onClick}>
|
||||
<div className="mx_RoomTile_avatar">
|
||||
<img src={ this.props.img } width="26" height="26"/>
|
||||
</div>
|
||||
{ label }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
205
src/components/views/rooms/RoomDNDView.js
Normal file
205
src/components/views/rooms/RoomDNDView.js
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var DragSource = require('react-dnd').DragSource;
|
||||
var DropTarget = require('react-dnd').DropTarget;
|
||||
|
||||
var dis = require("matrix-react-sdk/lib/dispatcher");
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var RoomTile = require('matrix-react-sdk/lib/components/views/rooms/RoomTile');
|
||||
|
||||
/**
|
||||
* Specifies the drag source contract.
|
||||
* Only `beginDrag` function is required.
|
||||
*/
|
||||
var roomTileSource = {
|
||||
canDrag: function(props, monitor) {
|
||||
return props.roomSubList.props.editable;
|
||||
},
|
||||
|
||||
beginDrag: function (props) {
|
||||
// Return the data describing the dragged item
|
||||
var item = {
|
||||
room: props.room,
|
||||
originalList: props.roomSubList,
|
||||
originalIndex: props.roomSubList.findRoomTile(props.room).index,
|
||||
targetList: props.roomSubList, // at first target is same as original
|
||||
// lastTargetRoom: null,
|
||||
// lastYOffset: null,
|
||||
// lastYDelta: null,
|
||||
};
|
||||
|
||||
if (props.roomSubList.debug) console.log("roomTile beginDrag for " + item.room.roomId);
|
||||
|
||||
// doing this 'correctly' with state causes react-dnd to break seemingly due to the state transitions
|
||||
props.room._dragging = true;
|
||||
|
||||
return item;
|
||||
},
|
||||
|
||||
endDrag: function (props, monitor, component) {
|
||||
var item = monitor.getItem();
|
||||
|
||||
if (props.roomSubList.debug) console.log("roomTile endDrag for " + item.room.roomId + " with didDrop=" + monitor.didDrop());
|
||||
|
||||
props.room._dragging = false;
|
||||
if (monitor.didDrop()) {
|
||||
if (props.roomSubList.debug) console.log("force updating component " + item.targetList.props.label);
|
||||
item.targetList.forceUpdate(); // as we're not using state
|
||||
}
|
||||
|
||||
if (monitor.didDrop() && item.targetList.props.editable) {
|
||||
// if we moved lists, remove the old tag
|
||||
if (item.targetList !== item.originalList && item.originalList.props.tagName) {
|
||||
// commented out attempts to set a spinner on our target component as component is actually
|
||||
// the original source component being dragged, not our target. To fix we just need to
|
||||
// move all of this to endDrop in the target instead. FIXME later.
|
||||
|
||||
//component.state.set({ spinner: component.state.spinner ? component.state.spinner++ : 1 });
|
||||
MatrixClientPeg.get().deleteRoomTag(item.room.roomId, item.originalList.props.tagName).finally(function() {
|
||||
//component.state.set({ spinner: component.state.spinner-- });
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to remove tag " + item.originalList.props.tagName + " from room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var newOrder= {};
|
||||
if (item.targetList.props.order === 'manual') {
|
||||
newOrder['order'] = item.targetList.calcManualOrderTagData(item.room);
|
||||
}
|
||||
|
||||
// if we moved lists or the ordering changed, add the new tag
|
||||
if (item.targetList.props.tagName && (item.targetList !== item.originalList || newOrder)) {
|
||||
//component.state.set({ spinner: component.state.spinner ? component.state.spinner++ : 1 });
|
||||
MatrixClientPeg.get().setRoomTag(item.room.roomId, item.targetList.props.tagName, newOrder).finally(function() {
|
||||
//component.state.set({ spinner: component.state.spinner-- });
|
||||
}).fail(function(err) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to add tag " + item.targetList.props.tagName + " to room",
|
||||
description: err.toString()
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
// cancel the drop and reset our original position
|
||||
if (props.roomSubList.debug) console.log("cancelling drop & drag");
|
||||
props.roomSubList.moveRoomTile(item.room, item.originalIndex);
|
||||
if (item.targetList && item.targetList !== item.originalList) {
|
||||
item.targetList.removeRoomTile(item.room);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var roomTileTarget = {
|
||||
canDrop: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
hover: function(props, monitor) {
|
||||
var item = monitor.getItem();
|
||||
//var off = monitor.getClientOffset();
|
||||
// console.log("hovering on room " + props.room.roomId + ", isOver=" + monitor.isOver());
|
||||
|
||||
//console.log("item.targetList=" + item.targetList + ", roomSubList=" + props.roomSubList);
|
||||
|
||||
var switchedTarget = false;
|
||||
if (item.targetList !== props.roomSubList) {
|
||||
// we've switched target, so remove the tile from the previous target.
|
||||
// n.b. the previous target might actually be the source list.
|
||||
if (props.roomSubList.debug) console.log("switched target sublist");
|
||||
switchedTarget = true;
|
||||
item.targetList.removeRoomTile(item.room);
|
||||
item.targetList = props.roomSubList;
|
||||
}
|
||||
|
||||
if (!item.targetList.props.editable) return;
|
||||
|
||||
if (item.targetList.props.order === 'manual') {
|
||||
if (item.room.roomId !== props.room.roomId && props.room !== item.lastTargetRoom) {
|
||||
// find the offset of the target tile in the list.
|
||||
var roomTile = props.roomSubList.findRoomTile(props.room);
|
||||
// shuffle the list to add our tile to that position.
|
||||
props.roomSubList.moveRoomTile(item.room, roomTile.index);
|
||||
}
|
||||
|
||||
// stop us from flickering between our droptarget and the previous room.
|
||||
// whenever the cursor changes direction we have to reset the flicker-damping.
|
||||
/*
|
||||
var yDelta = off.y - item.lastYOffset;
|
||||
|
||||
if ((yDelta > 0 && item.lastYDelta < 0) ||
|
||||
(yDelta < 0 && item.lastYDelta > 0))
|
||||
{
|
||||
// the cursor changed direction - forget our previous room
|
||||
item.lastTargetRoom = null;
|
||||
}
|
||||
else {
|
||||
// track the last room we were hovering over so we can stop
|
||||
// bouncing back and forth if the droptarget is narrower than
|
||||
// the other list items. The other way to do this would be
|
||||
// to reduce the size of the hittarget on the list items, but
|
||||
// can't see an easy way to do that.
|
||||
item.lastTargetRoom = props.room;
|
||||
}
|
||||
|
||||
if (yDelta) item.lastYDelta = yDelta;
|
||||
item.lastYOffset = off.y;
|
||||
*/
|
||||
}
|
||||
else if (switchedTarget) {
|
||||
if (!props.roomSubList.findRoomTile(item.room).room) {
|
||||
// add to the list in the right place
|
||||
props.roomSubList.moveRoomTile(item.room, 0);
|
||||
}
|
||||
// we have to sort the list whatever to recalculate it
|
||||
props.roomSubList.sortList();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Export the wrapped version, inlining the 'collect' functions
|
||||
// to more closely resemble the ES7
|
||||
module.exports =
|
||||
DropTarget('RoomTile', roomTileTarget, function(connect, monitor) {
|
||||
return {
|
||||
// Call this function inside render()
|
||||
// to let React DnD handle the drag events:
|
||||
connectDropTarget: connect.dropTarget(),
|
||||
isOver: monitor.isOver(),
|
||||
}
|
||||
})(
|
||||
DragSource('RoomTile', roomTileSource, function(connect, monitor) {
|
||||
return {
|
||||
// Call this function inside render()
|
||||
// to let React DnD handle the drag events:
|
||||
connectDragSource: connect.dragSource(),
|
||||
// You can ask the monitor about the current drag state:
|
||||
isDragging: monitor.isDragging()
|
||||
};
|
||||
})(RoomTile));
|
||||
|
||||
module.exports.replaces = 'RoomTile';
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,20 +18,24 @@ limitations under the License.
|
||||
|
||||
var React = require('react');
|
||||
|
||||
var EnableNotificationsButtonController = require("../../../../src/controllers/atoms/EnableNotificationsButton");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'EnableNotificationsButton',
|
||||
mixins: [EnableNotificationsButtonController],
|
||||
displayName: 'RoomDropTarget',
|
||||
|
||||
render: function() {
|
||||
if (this.enabled()) {
|
||||
if (this.props.placeholder) {
|
||||
return (
|
||||
<button className="mx_EnableNotificationsButton" onClick={this.onClick}>Disable Notifications</button>
|
||||
<div className="mx_RoomDropTarget mx_RoomDropTarget_placeholder">
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<button className="mx_EnableNotificationsButton" onClick={this.onClick}>Enable Notifications</button>
|
||||
<div className="mx_RoomDropTarget">
|
||||
<div className="mx_RoomDropTarget_avatar"></div>
|
||||
<div className="mx_RoomDropTarget_label">
|
||||
{ this.props.label }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
60
src/components/views/rooms/RoomTooltip.js
Normal file
60
src/components/views/rooms/RoomTooltip.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomTooltip',
|
||||
|
||||
componentDidMount: function() {
|
||||
var tooltip = ReactDOM.findDOMNode(this);
|
||||
if (!this.props.bottom) {
|
||||
// tell the roomlist about us so it can position us
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
tooltip: tooltip,
|
||||
});
|
||||
}
|
||||
else {
|
||||
tooltip.style.top = (70 + tooltip.parentElement.getBoundingClientRect().top) + "px";
|
||||
tooltip.style.display = "block";
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
if (!this.props.bottom) {
|
||||
dis.dispatch({
|
||||
action: 'view_tooltip',
|
||||
tooltip: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var label = this.props.room ? this.props.room.name : this.props.label;
|
||||
return (
|
||||
<div className="mx_RoomTooltip">
|
||||
<img className="mx_RoomTooltip_chevron" src="img/chevron-left.png" width="9" height="16"/>
|
||||
{ label }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
69
src/components/views/rooms/SearchBar.js
Normal file
69
src/components/views/rooms/SearchBar.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var classNames = require('classnames');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'SearchBar',
|
||||
|
||||
getInitialState: function() {
|
||||
return ({
|
||||
scope: 'Room'
|
||||
});
|
||||
},
|
||||
|
||||
onThisRoomClick: function() {
|
||||
this.setState({ scope: 'Room' });
|
||||
},
|
||||
|
||||
onAllRoomsClick: function() {
|
||||
this.setState({ scope: 'All' });
|
||||
},
|
||||
|
||||
onSearchChange: function(e) {
|
||||
if (e.keyCode === 13) { // on enter...
|
||||
this.onSearch();
|
||||
}
|
||||
if (e.keyCode === 27) { // escape...
|
||||
this.props.onCancelClick();
|
||||
}
|
||||
},
|
||||
|
||||
onSearch: function() {
|
||||
this.props.onSearch(this.refs.search_term.value, this.state.scope);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var searchButtonClasses = classNames({ mx_SearchBar_searchButton : true, mx_SearchBar_searching: this.props.searchInProgress });
|
||||
var thisRoomClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'Room' });
|
||||
var allRoomsClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'All' });
|
||||
|
||||
return (
|
||||
<div className="mx_SearchBar">
|
||||
<input ref="search_term" className="mx_SearchBar_input" type="text" autoFocus={true} placeholder="Search..." onKeyDown={this.onSearchChange}/>
|
||||
<div className={ searchButtonClasses } onClick={this.onSearch}><img src="img/search-button.svg" width="37" height="37" alt="Search"/></div>
|
||||
<div className={ thisRoomClasses } onClick={this.onThisRoomClick}>This Room</div>
|
||||
<div className={ allRoomsClasses } onClick={this.onAllRoomsClick}>All Rooms</div>
|
||||
<img className="mx_SearchBar_cancel" src="img/cancel.svg" width="18" height="18" onClick={this.props.onCancelClick} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
56
src/components/views/settings/IntegrationsManager.js
Normal file
56
src/components/views/settings/IntegrationsManager.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'IntegrationsManager',
|
||||
|
||||
propTypes: {
|
||||
src: React.PropTypes.string.isRequired, // the source of the integration manager being embedded
|
||||
onFinished: React.PropTypes.func.isRequired, // callback when the lightbox is dismissed
|
||||
},
|
||||
|
||||
// XXX: keyboard shortcuts for managing dialogs should be done by the modal
|
||||
// dialog base class somehow, surely...
|
||||
componentDidMount: function() {
|
||||
document.addEventListener("keydown", this.onKeyDown);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
},
|
||||
|
||||
onKeyDown: function(ev) {
|
||||
if (ev.keyCode == 27) { // escape
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.props.onFinished();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_IntegrationsManager">
|
||||
<iframe src={ this.props.src }></iframe>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
866
src/components/views/settings/Notifications.js
Normal file
866
src/components/views/settings/Notifications.js
Normal file
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
var React = require('react');
|
||||
var q = require("q");
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var UserSettingsStore = require('matrix-react-sdk/lib/UserSettingsStore');
|
||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||
|
||||
var notifications = require('../../../notifications');
|
||||
|
||||
// TODO: this "view" component still has far too much application logic in it,
|
||||
// which should be factored out to other files.
|
||||
|
||||
// TODO: this component also does a lot of direct poking into this.state, which
|
||||
// is VERY NAUGHTY.
|
||||
|
||||
var NotificationUtils = notifications.NotificationUtils;
|
||||
var VectorPushRulesDefinitions = notifications.VectorPushRulesDefinitions;
|
||||
var PushRuleVectorState = notifications.PushRuleVectorState;
|
||||
var ContentRules = notifications.ContentRules;
|
||||
|
||||
/**
|
||||
* Rules that Vector used to set in order to override the actions of default rules.
|
||||
* These are used to port peoples existing overrides to match the current API.
|
||||
* These can be removed and forgotten once everyone has moved to the new client.
|
||||
*/
|
||||
var LEGACY_RULES = {
|
||||
"im.vector.rule.contains_display_name": ".m.rule.contains_display_name",
|
||||
"im.vector.rule.room_one_to_one": ".m.rule.room_one_to_one",
|
||||
"im.vector.rule.room_message": ".m.rule.message",
|
||||
"im.vector.rule.invite_for_me": ".m.rule.invite_for_me",
|
||||
"im.vector.rule.call": ".m.rule.call",
|
||||
"im.vector.rule.notices": ".m.rule.suppress_notices"
|
||||
};
|
||||
|
||||
function portLegacyActions(actions) {
|
||||
var decoded = NotificationUtils.decodeActions(actions);
|
||||
if (decoded !== null) {
|
||||
return NotificationUtils.encodeActions(decoded);
|
||||
} else {
|
||||
// We don't recognise one of the actions here, so we don't try to
|
||||
// canonicalise them.
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Notififications',
|
||||
|
||||
phases: {
|
||||
LOADING: "LOADING", // The component is loading or sending data to the hs
|
||||
DISPLAY: "DISPLAY", // The component is ready and display data
|
||||
ERROR: "ERROR" // There was an error
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
// The array of threepids from the JS SDK (required for email notifications)
|
||||
threepids: React.PropTypes.array.isRequired,
|
||||
// The brand string set when creating an email pusher
|
||||
brand: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
threepids: []
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
phase: this.phases.LOADING,
|
||||
masterPushRule: undefined, // The master rule ('.m.rule.master')
|
||||
vectorPushRules: [], // HS default push rules displayed in Vector UI
|
||||
vectorContentRules: { // Keyword push rules displayed in Vector UI
|
||||
vectorState: PushRuleVectorState.ON,
|
||||
rules: []
|
||||
},
|
||||
externalPushRules: [], // Push rules (except content rule) that have been defined outside Vector UI
|
||||
externalContentRules: [] // Keyword push rules that have been defined outside Vector UI
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this._refreshFromServer();
|
||||
},
|
||||
|
||||
onEnableNotificationsChange: function(event) {
|
||||
var self = this;
|
||||
this.setState({
|
||||
phase: this.phases.LOADING
|
||||
});
|
||||
|
||||
MatrixClientPeg.get().setPushRuleEnabled('global', self.state.masterPushRule.kind, self.state.masterPushRule.rule_id, !event.target.checked).done(function() {
|
||||
self._refreshFromServer();
|
||||
});
|
||||
},
|
||||
|
||||
onEnableDesktopNotificationsChange: function(event) {
|
||||
UserSettingsStore.setEnableNotifications(event.target.checked);
|
||||
},
|
||||
|
||||
onEnableEmailNotificationsChange: function(address, event) {
|
||||
var emailPusherPromise;
|
||||
if (event.target.checked) {
|
||||
var data = {}
|
||||
data['brand'] = this.props.brand || 'Vector';
|
||||
emailPusherPromise = UserSettingsStore.addEmailPusher(address, data);
|
||||
} else {
|
||||
var emailPusher = UserSettingsStore.getEmailPusher(this.state.pushers, address);
|
||||
emailPusher.kind = null;
|
||||
emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher);
|
||||
}
|
||||
emailPusherPromise.done(() => {
|
||||
this._refreshFromServer();
|
||||
}, (error) => {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error saving email notification preferences",
|
||||
description: "Vector was unable to save your email notification preferences.",
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onNotifStateButtonClicked: function(event) {
|
||||
// FIXME: use .bind() rather than className metadata here surely
|
||||
var vectorRuleId = event.target.className.split("-")[0];
|
||||
var newPushRuleVectorState = event.target.className.split("-")[1];
|
||||
|
||||
if ("_keywords" === vectorRuleId) {
|
||||
this._setKeywordsPushRuleVectorState(newPushRuleVectorState)
|
||||
}
|
||||
else {
|
||||
var rule = this.getRule(vectorRuleId);
|
||||
if (rule) {
|
||||
this._setPushRuleVectorState(rule, newPushRuleVectorState);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onKeywordsClicked: function(event) {
|
||||
var self = this;
|
||||
|
||||
// Compute the keywords list to display
|
||||
var keywords = [];
|
||||
for (var i in this.state.vectorContentRules.rules) {
|
||||
var rule = this.state.vectorContentRules.rules[i];
|
||||
keywords.push(rule.pattern);
|
||||
}
|
||||
if (keywords.length) {
|
||||
// As keeping the order of per-word push rules hs side is a bit tricky to code,
|
||||
// display the keywords in alphabetical order to the user
|
||||
keywords.sort();
|
||||
|
||||
keywords = keywords.join(", ");
|
||||
}
|
||||
else {
|
||||
keywords = "";
|
||||
}
|
||||
|
||||
var TextInputDialog = sdk.getComponent("dialogs.TextInputDialog");
|
||||
Modal.createDialog(TextInputDialog, {
|
||||
title: "Keywords",
|
||||
description: "Enter keywords separated by a comma:",
|
||||
value: keywords,
|
||||
onFinished: function onFinished(should_leave, newValue) {
|
||||
|
||||
if (should_leave && newValue !== keywords) {
|
||||
var newKeywords = newValue.split(',');
|
||||
for (var i in newKeywords) {
|
||||
newKeywords[i] = newKeywords[i].trim();
|
||||
}
|
||||
|
||||
// Remove duplicates and empty
|
||||
newKeywords = newKeywords.reduce(function(array, keyword){
|
||||
if (keyword !== "" && array.indexOf(keyword) < 0) {
|
||||
array.push(keyword);
|
||||
}
|
||||
return array;
|
||||
},[]);
|
||||
|
||||
self._setKeywords(newKeywords);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getRule: function(vectorRuleId) {
|
||||
for (var i in this.state.vectorPushRules) {
|
||||
var rule = this.state.vectorPushRules[i];
|
||||
if (rule.vectorRuleId === vectorRuleId) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_setPushRuleVectorState: function(rule, newPushRuleVectorState) {
|
||||
if (rule && rule.vectorState !== newPushRuleVectorState) {
|
||||
|
||||
this.setState({
|
||||
phase: this.phases.LOADING
|
||||
});
|
||||
|
||||
var self = this;
|
||||
var cli = MatrixClientPeg.get();
|
||||
var deferreds = [];
|
||||
var ruleDefinition = VectorPushRulesDefinitions[rule.vectorRuleId];
|
||||
|
||||
if (rule.rule) {
|
||||
var actions = ruleDefinition.vectorStateToActions[newPushRuleVectorState];
|
||||
|
||||
if (!actions) {
|
||||
// The new state corresponds to disabling the rule.
|
||||
deferreds.push(cli.setPushRuleEnabled('global', rule.rule.kind, rule.rule.rule_id, false));
|
||||
}
|
||||
else {
|
||||
// The new state corresponds to enabling the rule and setting specific actions
|
||||
deferreds.push(this._updatePushRuleActions(rule.rule, actions, true));
|
||||
}
|
||||
}
|
||||
|
||||
q.all(deferreds).done(function() {
|
||||
self._refreshFromServer();
|
||||
}, function(error) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Can't change settings",
|
||||
description: error.toString(),
|
||||
onFinished: self._refreshFromServer
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_setKeywordsPushRuleVectorState: function(newPushRuleVectorState) {
|
||||
// Is there really a change?
|
||||
if (this.state.vectorContentRules.vectorState === newPushRuleVectorState
|
||||
|| this.state.vectorContentRules.rules.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
this.setState({
|
||||
phase: this.phases.LOADING
|
||||
});
|
||||
|
||||
// Update all rules in self.state.vectorContentRules
|
||||
var deferreds = [];
|
||||
for (var i in this.state.vectorContentRules.rules) {
|
||||
var rule = this.state.vectorContentRules.rules[i];
|
||||
|
||||
var enabled, actions;
|
||||
switch (newPushRuleVectorState) {
|
||||
case PushRuleVectorState.ON:
|
||||
if (rule.actions.length !== 1) {
|
||||
actions = PushRuleVectorState.actionsFor(PushRuleVectorState.ON);
|
||||
}
|
||||
|
||||
if (this.state.vectorContentRules.vectorState === PushRuleVectorState.OFF) {
|
||||
enabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case PushRuleVectorState.LOUD:
|
||||
if (rule.actions.length !== 3) {
|
||||
actions = PushRuleVectorState.actionsFor(PushRuleVectorState.LOUD);
|
||||
}
|
||||
|
||||
if (this.state.vectorContentRules.vectorState === PushRuleVectorState.OFF) {
|
||||
enabled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case PushRuleVectorState.OFF:
|
||||
enabled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (actions) {
|
||||
// Note that the workaround in _updatePushRuleActions will automatically
|
||||
// enable the rule
|
||||
deferreds.push(this._updatePushRuleActions(rule, actions, enabled));
|
||||
}
|
||||
else if (enabled != undefined) {
|
||||
deferreds.push(cli.setPushRuleEnabled('global', rule.kind, rule.rule_id, enabled));
|
||||
}
|
||||
}
|
||||
|
||||
q.all(deferreds).done(function(resps) {
|
||||
self._refreshFromServer();
|
||||
}, function(error) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Can't update user notification settings",
|
||||
description: error.toString(),
|
||||
onFinished: self._refreshFromServer
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_setKeywords: function(newKeywords) {
|
||||
this.setState({
|
||||
phase: this.phases.LOADING
|
||||
});
|
||||
|
||||
var self = this;
|
||||
var cli = MatrixClientPeg.get();
|
||||
var removeDeferreds = [];
|
||||
|
||||
// Remove per-word push rules of keywords that are no more in the list
|
||||
var vectorContentRulesPatterns = [];
|
||||
for (var i in self.state.vectorContentRules.rules) {
|
||||
var rule = self.state.vectorContentRules.rules[i];
|
||||
|
||||
vectorContentRulesPatterns.push(rule.pattern);
|
||||
|
||||
if (newKeywords.indexOf(rule.pattern) < 0) {
|
||||
removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id));
|
||||
}
|
||||
}
|
||||
|
||||
// If the keyword is part of `externalContentRules`, remove the rule
|
||||
// before recreating it in the right Vector path
|
||||
for (var i in self.state.externalContentRules) {
|
||||
var rule = self.state.externalContentRules[i];
|
||||
|
||||
if (newKeywords.indexOf(rule.pattern) >= 0) {
|
||||
removeDeferreds.push(cli.deletePushRule('global', rule.kind, rule.rule_id));
|
||||
}
|
||||
}
|
||||
|
||||
var onError = function(error) {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Can't update keywords",
|
||||
description: error.toString(),
|
||||
onFinished: self._refreshFromServer
|
||||
});
|
||||
}
|
||||
|
||||
// Then, add the new ones
|
||||
q.all(removeDeferreds).done(function(resps) {
|
||||
var deferreds = [];
|
||||
|
||||
var pushRuleVectorStateKind = self.state.vectorContentRules.vectorState;
|
||||
if (pushRuleVectorStateKind === PushRuleVectorState.OFF) {
|
||||
// When the current global keywords rule is OFF, we need to look at
|
||||
// the flavor of rules in 'vectorContentRules' to apply the same actions
|
||||
// when creating the new rule.
|
||||
// Thus, this new rule will join the 'vectorContentRules' set.
|
||||
if (self.state.vectorContentRules.rules.length) {
|
||||
pushRuleVectorStateKind = PushRuleVectorState.contentRuleVectorStateKind(self.state.vectorContentRules.rules[0]);
|
||||
}
|
||||
else {
|
||||
// ON is default
|
||||
pushRuleVectorStateKind = PushRuleVectorState.ON;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i in newKeywords) {
|
||||
var keyword = newKeywords[i];
|
||||
|
||||
if (vectorContentRulesPatterns.indexOf(keyword) < 0) {
|
||||
if (self.state.vectorContentRules.vectorState !== PushRuleVectorState.OFF) {
|
||||
deferreds.push(cli.addPushRule
|
||||
('global', 'content', keyword, {
|
||||
actions: PushRuleVectorState.actionsFor(pushRuleVectorStateKind),
|
||||
pattern: keyword
|
||||
}));
|
||||
}
|
||||
else {
|
||||
deferreds.push(self._addDisabledPushRule('global', 'content', keyword, {
|
||||
actions: PushRuleVectorState.actionsFor(pushRuleVectorStateKind),
|
||||
pattern: keyword
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
q.all(deferreds).done(function(resps) {
|
||||
self._refreshFromServer();
|
||||
}, onError);
|
||||
}, onError);
|
||||
},
|
||||
|
||||
// Create a push rule but disabled
|
||||
_addDisabledPushRule: function(scope, kind, ruleId, body) {
|
||||
var cli = MatrixClientPeg.get();
|
||||
return cli.addPushRule(scope, kind, ruleId, body).then(function() {
|
||||
return cli.setPushRuleEnabled(scope, kind, ruleId, false);
|
||||
});
|
||||
},
|
||||
|
||||
// Check if any legacy im.vector rules need to be ported to the new API
|
||||
// for overriding the actions of default rules.
|
||||
_portRulesToNewAPI: function(rulesets) {
|
||||
var self = this;
|
||||
var needsUpdate = [];
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
for (var kind in rulesets.global) {
|
||||
var ruleset = rulesets.global[kind];
|
||||
for (var i = 0; i < ruleset.length; ++i) {
|
||||
var rule = ruleset[i];
|
||||
if (rule.rule_id in LEGACY_RULES) {
|
||||
console.log("Porting legacy rule", rule);
|
||||
needsUpdate.push( function(kind, rule) {
|
||||
return cli.setPushRuleActions(
|
||||
'global', kind, LEGACY_RULES[rule.rule_id], portLegacyActions(rule.actions)
|
||||
).then( function() {
|
||||
return cli.deletePushRule('global', kind, rule.rule_id);
|
||||
})
|
||||
}(kind, rule));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsUpdate.length > 0) {
|
||||
// If some of the rules need to be ported then wait for the porting
|
||||
// to happen and then fetch the rules again.
|
||||
return q.allSettled(needsUpdate).then( function() {
|
||||
return cli.getPushRules();
|
||||
});
|
||||
} else {
|
||||
// Otherwise return the rules that we already have.
|
||||
return rulesets;
|
||||
}
|
||||
},
|
||||
|
||||
_refreshFromServer: function() {
|
||||
var self = this;
|
||||
var pushRulesPromise = MatrixClientPeg.get().getPushRules().then(self._portRulesToNewAPI).then(function(rulesets) {
|
||||
//console.log("resolving pushRulesPromise");
|
||||
|
||||
/// XXX seriously? wtf is this?
|
||||
MatrixClientPeg.get().pushRules = rulesets;
|
||||
|
||||
// Get homeserver default rules and triage them by categories
|
||||
var rule_categories = {
|
||||
// The master rule (all notifications disabling)
|
||||
'.m.rule.master': 'master',
|
||||
|
||||
// The default push rules displayed by Vector UI
|
||||
// XXX: .m.rule.contains_user_name is not managed (not a fancy rule for Vector?)
|
||||
'.m.rule.contains_display_name': 'vector',
|
||||
'.m.rule.room_one_to_one': 'vector',
|
||||
'.m.rule.message': 'vector',
|
||||
'.m.rule.invite_for_me': 'vector',
|
||||
//'.m.rule.member_event': 'vector',
|
||||
'.m.rule.call': 'vector',
|
||||
'.m.rule.suppress_notices': 'vector'
|
||||
|
||||
// Others go to others
|
||||
};
|
||||
|
||||
// HS default rules
|
||||
var defaultRules = {master: [], vector: {}, others: []};
|
||||
|
||||
for (var kind in rulesets.global) {
|
||||
for (var i = 0; i < Object.keys(rulesets.global[kind]).length; ++i) {
|
||||
var r = rulesets.global[kind][i];
|
||||
var cat = rule_categories[r.rule_id];
|
||||
r.kind = kind;
|
||||
|
||||
if (r.rule_id[0] === '.') {
|
||||
if (cat === 'vector') {
|
||||
defaultRules.vector[r.rule_id] = r;
|
||||
}
|
||||
else if (cat === 'master') {
|
||||
defaultRules.master.push(r);
|
||||
}
|
||||
else {
|
||||
defaultRules['others'].push(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the master rule if any defined by the hs
|
||||
if (defaultRules.master.length > 0) {
|
||||
self.state.masterPushRule = defaultRules.master[0];
|
||||
}
|
||||
|
||||
// parse the keyword rules into our state
|
||||
var contentRules = ContentRules.parseContentRules(rulesets);
|
||||
self.state.vectorContentRules = {
|
||||
vectorState: contentRules.vectorState,
|
||||
rules: contentRules.rules,
|
||||
};
|
||||
self.state.externalContentRules = contentRules.externalRules;
|
||||
|
||||
// Build the rules displayed in the Vector UI matrix table
|
||||
self.state.vectorPushRules = [];
|
||||
self.state.externalPushRules = [];
|
||||
|
||||
var vectorRuleIds = [
|
||||
'.m.rule.contains_display_name',
|
||||
'_keywords',
|
||||
'.m.rule.room_one_to_one',
|
||||
'.m.rule.message',
|
||||
'.m.rule.invite_for_me',
|
||||
//'im.vector.rule.member_event',
|
||||
'.m.rule.call',
|
||||
'.m.rule.suppress_notices'
|
||||
];
|
||||
for (var i in vectorRuleIds) {
|
||||
var vectorRuleId = vectorRuleIds[i];
|
||||
|
||||
if (vectorRuleId === '_keywords') {
|
||||
// keywords needs a special handling
|
||||
// For Vector UI, this is a single global push rule but translated in Matrix,
|
||||
// it corresponds to all content push rules (stored in self.state.vectorContentRule)
|
||||
self.state.vectorPushRules.push({
|
||||
"vectorRuleId": "_keywords",
|
||||
"description" : (<span>Messages containing <span className="mx_UserNotifSettings_keywords" onClick={ self.onKeywordsClicked }>keywords</span></span>),
|
||||
"vectorState": self.state.vectorContentRules.vectorState
|
||||
});
|
||||
}
|
||||
else {
|
||||
var ruleDefinition = VectorPushRulesDefinitions[vectorRuleId];
|
||||
var rule = defaultRules.vector[vectorRuleId];
|
||||
|
||||
var vectorState = ruleDefinition.ruleToVectorState(rule);
|
||||
|
||||
//console.log("Refreshing vectorPushRules for " + vectorRuleId +", "+ ruleDefinition.description +", " + rule +", " + vectorState);
|
||||
|
||||
self.state.vectorPushRules.push({
|
||||
"vectorRuleId": vectorRuleId,
|
||||
"description" : ruleDefinition.description,
|
||||
"rule": rule,
|
||||
"vectorState": vectorState,
|
||||
});
|
||||
|
||||
// if there was a rule which we couldn't parse, add it to the external list
|
||||
if (rule && !vectorState) {
|
||||
rule.description = ruleDefinition.description;
|
||||
self.state.externalPushRules.push(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the rules not managed by Vector UI
|
||||
var otherRulesDescriptions = {
|
||||
'.m.rule.message': "Notify for all other messages/rooms",
|
||||
'.m.rule.fallback': "Notify me for anything else"
|
||||
};
|
||||
|
||||
for (var i in defaultRules.others) {
|
||||
var rule = defaultRules.others[i];
|
||||
var ruleDescription = otherRulesDescriptions[rule.rule_id];
|
||||
|
||||
// Show enabled default rules that was modified by the user
|
||||
if (ruleDescription && rule.enabled && !rule.default) {
|
||||
rule.description = ruleDescription;
|
||||
self.state.externalPushRules.push(rule);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var pushersPromise = MatrixClientPeg.get().getPushers().then(function(resp) {
|
||||
//console.log("resolving pushersPromise");
|
||||
self.setState({pushers: resp.pushers});
|
||||
});
|
||||
|
||||
q.all([pushRulesPromise, pushersPromise]).then(function() {
|
||||
self.setState({
|
||||
phase: self.phases.DISPLAY
|
||||
});
|
||||
}, function(error) {
|
||||
self.setState({
|
||||
phase: self.phases.ERROR
|
||||
});
|
||||
}).finally(() => {
|
||||
// actually explicitly update our state having been deep-manipulating it
|
||||
self.setState({
|
||||
masterPushRule: self.state.masterPushRule,
|
||||
vectorContentRules: self.state.vectorContentRules,
|
||||
vectorPushRules: self.state.vectorPushRules,
|
||||
externalContentRules: self.state.externalContentRules,
|
||||
externalPushRules: self.state.externalPushRules,
|
||||
});
|
||||
}).done();
|
||||
},
|
||||
|
||||
_updatePushRuleActions: function(rule, actions, enabled) {
|
||||
var cli = MatrixClientPeg.get();
|
||||
|
||||
return cli.setPushRuleActions(
|
||||
'global', rule.kind, rule.rule_id, actions
|
||||
).then( function() {
|
||||
// Then, if requested, enabled or disabled the rule
|
||||
if (undefined != enabled) {
|
||||
return cli.setPushRuleEnabled(
|
||||
'global', rule.kind, rule.rule_id, enabled
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderNotifRulesTableRow: function(title, className, pushRuleVectorState) {
|
||||
return (
|
||||
<tr key={ className }>
|
||||
<th>
|
||||
{title}
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<input className= {className + "-" + PushRuleVectorState.OFF}
|
||||
type="radio"
|
||||
checked={ pushRuleVectorState === PushRuleVectorState.OFF }
|
||||
onChange={ this.onNotifStateButtonClicked } />
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<input className= {className + "-" + PushRuleVectorState.ON}
|
||||
type="radio"
|
||||
checked={ pushRuleVectorState === PushRuleVectorState.ON }
|
||||
onChange={ this.onNotifStateButtonClicked } />
|
||||
</th>
|
||||
|
||||
<th>
|
||||
<input className= {className + "-" + PushRuleVectorState.LOUD}
|
||||
type="radio"
|
||||
checked={ pushRuleVectorState === PushRuleVectorState.LOUD }
|
||||
onChange={ this.onNotifStateButtonClicked } />
|
||||
</th>
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
|
||||
renderNotifRulesTableRows: function() {
|
||||
var rows = [];
|
||||
for (var i in this.state.vectorPushRules) {
|
||||
var rule = this.state.vectorPushRules[i];
|
||||
//console.log("rendering: " + rule.description + ", " + rule.vectorRuleId + ", " + rule.vectorState);
|
||||
rows.push(this.renderNotifRulesTableRow(rule.description, rule.vectorRuleId, rule.vectorState));
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
|
||||
emailNotificationsRow: function(address, label) {
|
||||
return (<div className="mx_UserNotifSettings_tableRow">
|
||||
<div className="mx_UserNotifSettings_inputCell">
|
||||
<input id="enableEmailNotifications_{address}"
|
||||
ref="enableEmailNotifications_{address}"
|
||||
type="checkbox"
|
||||
checked={ UserSettingsStore.hasEmailPusher(this.state.pushers, address) }
|
||||
onChange={ this.onEnableEmailNotificationsChange.bind(this, address) }
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_UserNotifSettings_labelCell">
|
||||
<label htmlFor="enableEmailNotifications_{address}">
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
</div>);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
|
||||
var spinner;
|
||||
if (this.state.phase === this.phases.LOADING) {
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
spinner = <Loader />;
|
||||
}
|
||||
|
||||
if (this.state.masterPushRule) {
|
||||
var masterPushRuleDiv = (
|
||||
<div className="mx_UserNotifSettings_tableRow">
|
||||
<div className="mx_UserNotifSettings_inputCell">
|
||||
<input id="enableNotifications"
|
||||
ref="enableNotifications"
|
||||
type="checkbox"
|
||||
checked={ !this.state.masterPushRule.enabled }
|
||||
onChange={ this.onEnableNotificationsChange } />
|
||||
</div>
|
||||
<div className="mx_UserNotifSettings_labelCell">
|
||||
<label htmlFor="enableNotifications">
|
||||
Enable notifications for this account
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// When enabled, the master rule inhibits all existing rules
|
||||
// So do not show all notification settings
|
||||
if (this.state.masterPushRule && this.state.masterPushRule.enabled) {
|
||||
return (
|
||||
<div>
|
||||
{masterPushRuleDiv}
|
||||
|
||||
<div className="mx_UserSettings_notifTable">
|
||||
All notifications are currently disabled for all targets.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
var emailNotificationsRow;
|
||||
if (this.props.threepids.filter(function(tp) {
|
||||
if (tp.medium == "email") {
|
||||
return true;
|
||||
}
|
||||
}).length == 0) {
|
||||
emailNotificationsRow = <div>
|
||||
Add an email address above to configure email notifications
|
||||
</div>;
|
||||
} else {
|
||||
// This only supports the first email address in your profile for now
|
||||
emailNotificationsRow = this.emailNotificationsRow(
|
||||
this.props.threepids[0].address,
|
||||
"Enable email notifications ("+this.props.threepids[0].address+")"
|
||||
);
|
||||
}
|
||||
|
||||
// Build external push rules
|
||||
var externalRules = [];
|
||||
for (var i in this.state.externalPushRules) {
|
||||
var rule = this.state.externalPushRules[i];
|
||||
externalRules.push(<li>{ rule.description }</li>);
|
||||
}
|
||||
|
||||
// Show keywords not displayed by the vector UI as a single external push rule
|
||||
var externalKeyWords = [];
|
||||
for (var i in this.state.externalContentRules) {
|
||||
var rule = this.state.externalContentRules[i];
|
||||
externalKeyWords.push(rule.pattern);
|
||||
}
|
||||
if (externalKeyWords.length) {
|
||||
externalKeyWords = externalKeyWords.join(", ");
|
||||
externalRules.push(<li>Notifications on the following keywords follow rules which can’t be displayed here: { externalKeyWords }</li>);
|
||||
}
|
||||
|
||||
var devicesSection;
|
||||
if (this.state.pushers === undefined) {
|
||||
devicesSection = <div className="error">Unable to fetch notification target list</div>
|
||||
} else if (this.state.pushers.length == 0) {
|
||||
devicesSection = null;
|
||||
} else {
|
||||
// TODO: It would be great to be able to delete pushers from here too,
|
||||
// and this wouldn't be hard to add.
|
||||
var rows = [];
|
||||
for (var i = 0; i < this.state.pushers.length; ++i) {
|
||||
rows.push(<tr key={ i }>
|
||||
<td>{this.state.pushers[i].app_display_name}</td>
|
||||
<td>{this.state.pushers[i].device_display_name}</td>
|
||||
</tr>);
|
||||
}
|
||||
devicesSection = (<table className="mx_UserSettings_devicesTable">
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>);
|
||||
}
|
||||
if (devicesSection) {
|
||||
devicesSection = (<div>
|
||||
<h3>Notification targets</h3>
|
||||
{ devicesSection }
|
||||
</div>);
|
||||
}
|
||||
|
||||
var advancedSettings;
|
||||
if (externalRules.length) {
|
||||
advancedSettings = (
|
||||
<div>
|
||||
<h3>Advanced notifications settings</h3>
|
||||
There are advanced notifications which are not shown here.<br/>
|
||||
You might have configured them in another client than Vector. You cannot tune them in Vector but they still apply.
|
||||
<ul>
|
||||
{ externalRules }
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
{masterPushRuleDiv}
|
||||
|
||||
<div className="mx_UserSettings_notifTable">
|
||||
|
||||
{ spinner }
|
||||
|
||||
<div className="mx_UserNotifSettings_tableRow">
|
||||
<div className="mx_UserNotifSettings_inputCell">
|
||||
<input id="enableDesktopNotifications"
|
||||
ref="enableDesktopNotifications"
|
||||
type="checkbox"
|
||||
checked={ UserSettingsStore.getEnableNotifications() }
|
||||
onChange={ this.onEnableDesktopNotificationsChange } />
|
||||
</div>
|
||||
<div className="mx_UserNotifSettings_labelCell">
|
||||
<label htmlFor="enableDesktopNotifications">
|
||||
Enable desktop notifications
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx_UserNotifSettings_tableRow">
|
||||
<div className="mx_UserNotifSettings_inputCell">
|
||||
<input id="enableDesktopAudioNotifications"
|
||||
ref="enableDesktopAudioNotifications"
|
||||
type="checkbox"
|
||||
checked={ UserSettingsStore.getEnableAudioNotifications() }
|
||||
onChange={ (e) => {
|
||||
UserSettingsStore.setEnableAudioNotifications(e.target.checked);
|
||||
this.forceUpdate();
|
||||
}} />
|
||||
</div>
|
||||
<div className="mx_UserNotifSettings_labelCell">
|
||||
<label htmlFor="enableDesktopAudioNotifications">
|
||||
Enable audible notifications in web client
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ emailNotificationsRow }
|
||||
|
||||
<div className="mx_UserNotifSettings_pushRulesTableWrapper">
|
||||
<table className="mx_UserNotifSettings_pushRulesTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="55%"></th>
|
||||
<th width="15%">Off</th>
|
||||
<th width="15%">On</th>
|
||||
<th width="15%">Highlight<br/>& sound</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{ this.renderNotifRulesTableRows() }
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{ advancedSettings }
|
||||
|
||||
{ devicesSection }
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
module.exports = {
|
||||
propTypes: {
|
||||
onValueChanged: React.PropTypes.func,
|
||||
initalValue: React.PropTypes.string,
|
||||
},
|
||||
|
||||
Phases: {
|
||||
Display: "display",
|
||||
Edit: "edit",
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onValueChanged: function() {},
|
||||
initalValue: '',
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
value: this.props.initalValue,
|
||||
phase: this.Phases.Display,
|
||||
}
|
||||
},
|
||||
|
||||
getValue: function() {
|
||||
return this.state.value;
|
||||
},
|
||||
|
||||
setValue: function(val) {
|
||||
this.setState({
|
||||
value: val,
|
||||
phase: this.Phases.Display,
|
||||
});
|
||||
|
||||
this.onValueChanged();
|
||||
},
|
||||
|
||||
cancelEdit: function() {
|
||||
this.setState({
|
||||
phase: this.Phases.Display,
|
||||
});
|
||||
},
|
||||
|
||||
onValueChanged: function() {
|
||||
this.props.onValueChanged(this.state.value);
|
||||
},
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
notificationsAvailable: function() {
|
||||
return !!global.Notification;
|
||||
},
|
||||
|
||||
havePermission: function() {
|
||||
return global.Notification.permission == 'granted';
|
||||
},
|
||||
|
||||
enabled: function() {
|
||||
if (!this.havePermission()) return false;
|
||||
|
||||
if (!global.localStorage) return true;
|
||||
|
||||
var enabled = global.localStorage.getItem('notifications_enabled');
|
||||
if (enabled === null) return true;
|
||||
return enabled === 'true';
|
||||
},
|
||||
|
||||
disable: function() {
|
||||
if (!global.localStorage) return;
|
||||
global.localStorage.setItem('notifications_enabled', 'false');
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
enable: function() {
|
||||
if (!this.havePermission()) {
|
||||
var that = this;
|
||||
global.Notification.requestPermission(function() {
|
||||
that.forceUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
if (!global.localStorage) return;
|
||||
global.localStorage.setItem('notifications_enabled', 'true');
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
if (!this.notificationsAvailable()) {
|
||||
return;
|
||||
}
|
||||
if (!this.enabled()) {
|
||||
this.enable();
|
||||
} else {
|
||||
this.disable();
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
};
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
module.exports = {
|
||||
propTypes: {
|
||||
onCreateRoom: React.PropTypes.func,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onCreateRoom: function() {},
|
||||
};
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
this.props.onCreateRoom();
|
||||
},
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
|
||||
module.exports = {
|
||||
propTypes: {
|
||||
default_name: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
default_name: '',
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
room_name: this.props.default_name,
|
||||
}
|
||||
},
|
||||
|
||||
getName: function() {
|
||||
return this.state.room_name;
|
||||
},
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
};
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var filesize = require('filesize');
|
||||
|
||||
module.exports = {
|
||||
presentableTextForFile: function(content) {
|
||||
var linkText = 'Attachment';
|
||||
if (content.body && content.body.length > 0) {
|
||||
linkText = content.body;
|
||||
}
|
||||
|
||||
var additionals = [];
|
||||
if (content.info) {
|
||||
if (content.info.mimetype && content.info.mimetype.length > 0) {
|
||||
additionals.push(content.info.mimetype);
|
||||
}
|
||||
if (content.info.size) {
|
||||
additionals.push(filesize(content.info.size));
|
||||
}
|
||||
}
|
||||
|
||||
if (additionals.length > 0) {
|
||||
linkText += ' (' + additionals.join(', ') + ')';
|
||||
}
|
||||
return linkText;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var linkify = require('linkifyjs');
|
||||
var linkifyElement = require('linkifyjs/element');
|
||||
var linkifyMatrix = require('../../linkify-matrix.js');
|
||||
linkifyMatrix(linkify);
|
||||
|
||||
module.exports = {
|
||||
componentDidMount: function() {
|
||||
linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
|
||||
}
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
};
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var linkify = require('linkifyjs');
|
||||
var linkifyElement = require('linkifyjs/element');
|
||||
var linkifyMatrix = require('../../linkify-matrix');
|
||||
|
||||
linkifyMatrix(linkify);
|
||||
|
||||
module.exports = {
|
||||
componentDidMount: function() {
|
||||
linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
};
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var dis = require("../../dispatcher");
|
||||
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
|
||||
module.exports = {
|
||||
onClick: function() {
|
||||
dis.dispatch({
|
||||
action: 'view_user',
|
||||
user_id: this.props.member.userId
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
|
||||
var dis = require("../../dispatcher");
|
||||
|
||||
module.exports = {
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
switch (payload.action) {
|
||||
case 'focus_composer':
|
||||
this.refs.textarea.getDOMNode().focus();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onKeyDown: function (ev) {
|
||||
if (ev.keyCode == 13) {
|
||||
var contentText = this.refs.textarea.getDOMNode().value;
|
||||
|
||||
var content = null;
|
||||
if (/^\/me /i.test(contentText)) {
|
||||
content = {
|
||||
msgtype: 'm.emote',
|
||||
body: contentText.substring(4)
|
||||
};
|
||||
} else {
|
||||
content = {
|
||||
msgtype: 'm.text',
|
||||
body: contentText
|
||||
};
|
||||
}
|
||||
|
||||
MatrixClientPeg.get().sendMessage(this.props.roomId, content).then(function() {
|
||||
dis.dispatch({
|
||||
action: 'message_sent'
|
||||
});
|
||||
});
|
||||
this.refs.textarea.getDOMNode().value = '';
|
||||
ev.preventDefault();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
|
||||
module.exports = {
|
||||
shouldHighlight: function() {
|
||||
var actions = MatrixClientPeg.get().getPushActionsForEvent(this.props.mxEvent);
|
||||
if (!actions || !actions.tweaks) { return false; }
|
||||
return actions.tweaks.highlight;
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user