Compare commits
903 Commits
verto
...
matthew/ka
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbce60a025 | ||
|
|
95e173008c | ||
|
|
f2fc4a88ae | ||
|
|
77b45e7231 | ||
|
|
484736bf57 | ||
|
|
e91a108738 | ||
|
|
2ca5529faa | ||
|
|
a35ab6bcef | ||
|
|
e33e9b5d4a | ||
|
|
3910877f8f | ||
|
|
f538a0d5de | ||
|
|
4e5b971d3a | ||
|
|
ffb1584e10 | ||
|
|
dbf3439e35 | ||
|
|
11ac6defed | ||
|
|
718c1cbe6f | ||
|
|
27222a54c7 | ||
|
|
3263076ea6 | ||
|
|
3c659dcf57 | ||
|
|
2cd3150c56 | ||
|
|
cdc1447804 | ||
|
|
11c9aff69a | ||
|
|
58959d014a | ||
|
|
8e008572e5 | ||
|
|
177fba360d | ||
|
|
ad9935739c | ||
|
|
a2193d61b0 | ||
|
|
0ca6efdfca | ||
|
|
f3df0d07f8 | ||
|
|
95a3354b90 | ||
|
|
82b498017d | ||
|
|
b929f80be8 | ||
|
|
05a30e7c68 | ||
|
|
3c75c43d37 | ||
|
|
369e357742 | ||
|
|
93f8fcbacc | ||
|
|
155728b136 | ||
|
|
98551c3f9a | ||
|
|
1732805f31 | ||
|
|
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 | ||
|
|
9d8d4e4896 |
10
.gitignore
vendored
@@ -1,4 +1,8 @@
|
||||
node_modules
|
||||
build
|
||||
bundle.css
|
||||
bundle.js
|
||||
vector/bundle.*
|
||||
lib
|
||||
.DS_Store
|
||||
key.pem
|
||||
cert.pem
|
||||
vector/components.css
|
||||
packages/
|
||||
|
||||
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"
|
||||
]
|
||||
}
|
||||
9
AUTHORS.rst
Normal file
@@ -0,0 +1,9 @@
|
||||
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
|
||||
31
CHANGES.rst
Normal file
@@ -0,0 +1,31 @@
|
||||
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
@@ -0,0 +1,4 @@
|
||||
Contributing code to Vector
|
||||
===========================
|
||||
|
||||
Vector follows the same pattern as https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst
|
||||
171
README.md
@@ -10,138 +10,79 @@ Getting started
|
||||
2. Clone the repo: `git clone https://github.com/vector-im/vector-web.git`
|
||||
3. Switch to the SDK directory: `cd vector-web`
|
||||
4. Install the prerequisites: `npm install`
|
||||
5. Switch to the example directory: `cd examples/vector`
|
||||
6. Install the example app prerequisites: `npm install`
|
||||
7. Build the example and start a server: `npm start`
|
||||
5. Start the development builder and a testing server: `npm start`
|
||||
6. Wait a few seconds for the initial build to finish.
|
||||
7. Open http://127.0.0.1:8080/ in your browser to see your newly built Vector.
|
||||
|
||||
Now open http://127.0.0.1:8080/ in your browser to see your newly built
|
||||
Vector.
|
||||
With `npm start`, any changes you make to the source files will cause a rebuild so
|
||||
your changes will show up when you refresh. This development server also disables
|
||||
caching, so do NOT use it in production.
|
||||
|
||||
For production use, run `npm run build` to build all the necessary files
|
||||
into the `vector` directory and run your own server.
|
||||
|
||||
Development
|
||||
===========
|
||||
|
||||
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:
|
||||
For simple tweaks, you can work on any of the source files within Vector with the
|
||||
setup above, and your changes will cause an instant rebuild.
|
||||
|
||||
1. Link the react sdk package into the example:
|
||||
`cd vector-web/examples/vector; npm link ../../`
|
||||
2. Start a watcher for the CSS files:
|
||||
`cd vector-web; npm run start:css`
|
||||
However, all serious development on Vector happens on the `develop` branch. This typically
|
||||
depends on the `develop` snapshot versions of `matrix-react-sdk` and `matrix-js-sdk`
|
||||
too, which isn't handled by Vector's `package.json`. To get the right dependencies, check out
|
||||
the `develop` branches of these libraries and then use `npm link` to tell Vector
|
||||
about them:
|
||||
|
||||
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.
|
||||
1. `git clone git@github.com:matrix-org/matrix-react-sdk.git`
|
||||
2. `cd matrix-react-sdk`
|
||||
3. `git checkout develop`
|
||||
4. `npm install`
|
||||
5. `npm run build`
|
||||
6. `npm start` (to start the dev rebuilder)
|
||||
7. `cd ../vector-web`
|
||||
8. Link the react sdk package into the example:
|
||||
`npm link path/to/your/react/sdk`
|
||||
|
||||
IMPORTANT: If you customise components in your application (and hence require
|
||||
react from your app) you must be sure to:
|
||||
Similarly, you may need to `npm link path/to/your/js/sdk` in your `matrix-react-sdk`
|
||||
directory.
|
||||
|
||||
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.
|
||||
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`.
|
||||
|
||||
How to customise the SDK
|
||||
========================
|
||||
You may need to run `npm i source-map-loader` in matrix-js-sdk if you get errors
|
||||
about "Cannot resolve module 'source-map-loader'" due to shortcomings in webpack.
|
||||
|
||||
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.
|
||||
Deployment
|
||||
==========
|
||||
|
||||
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:
|
||||
Just run `npm run build` and then mount the `vector` directory on your webserver to
|
||||
actually serve up the app, which is entirely static content.
|
||||
|
||||
* 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.
|
||||
Enabling encryption
|
||||
===================
|
||||
|
||||
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:
|
||||
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.
|
||||
|
||||
* Views are named with upper camel case (e.g. molecules/MessageTile.js)
|
||||
To build a version of vector with support for end-to-end encryption, install
|
||||
the olm module with `npm i https://matrix.org/packages/npm/olm/olm-0.1.0.tgz`
|
||||
before running `npm start`. The olm library will be detected and used if
|
||||
available.
|
||||
|
||||
* The view's CSS file MUST have the same name (e.g. molecules/MessageTile.css)
|
||||
To enable encryption for a room, type
|
||||
|
||||
* 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.
|
||||
```
|
||||
/encrypt on
|
||||
```
|
||||
|
||||
* 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.
|
||||
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`.)
|
||||
|
||||
* 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.
|
||||
Note that historical encrypted messages cannot currently be decoded - history
|
||||
is therefore lost when the page is reloaded.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
|
||||
* 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.
|
||||
|
||||
* We don't use the atomify library itself, as React already provides most
|
||||
of the modularity requirements it brings to the table.
|
||||
|
||||
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:
|
||||
|
||||
* 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.
|
||||
|
||||
For more specific detail on any of these steps, look at the `custom` example in
|
||||
matrix-react-sdk/examples.
|
||||
There is currently no visual indication of whether encryption is enabled for a
|
||||
room, or whether a particular message was encrypted.
|
||||
|
||||
4
config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"default_hs_url": "http://127.0.0.1:8008",
|
||||
"default_is_url": "https://vector.im"
|
||||
}
|
||||
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
@@ -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 +0,0 @@
|
||||
../../skins/base/fonts/
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square70x70logo src="/icons/mstile-70x70.png"/>
|
||||
<square150x150logo src="/icons/mstile-150x150.png"/>
|
||||
<square310x310logo src="/icons/mstile-310x310.png"/>
|
||||
<wide310x150logo src="/icons/mstile-310x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
@@ -1 +0,0 @@
|
||||
../../skins/base/img
|
||||
@@ -1,46 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en" style="height: 100%;">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Vector</title>
|
||||
<link href='fonts/Lato.css' rel='stylesheet' type='text/css'>
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/icons/apple-touch-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/icons/apple-touch-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/icons/apple-touch-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/icons/apple-touch-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/icons/apple-touch-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/icons/apple-touch-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/icons/apple-touch-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/icons/apple-touch-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" href="/icons/favicon-32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="/icons/android-chrome-192x192.png" sizes="192x192">
|
||||
<link rel="icon" type="image/png" href="/icons/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="icon" type="image/png" href="/icons/favicon-16x16.png" sizes="16x16">
|
||||
<link rel="manifest" href="/icons/manifest.json">
|
||||
<link rel="shortcut icon" href="/icons/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-title" content="Vector">
|
||||
<meta name="application-name" content="Vector">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="msapplication-TileImage" content="/icons/mstile-144x144.png">
|
||||
<meta name="msapplication-config" content="/icons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
</head>
|
||||
<body style="height: 100%;">
|
||||
<audio id="ringbackAudio" loop>
|
||||
<source src="media/ringback.ogg" type="audio/ogg" />
|
||||
<source src="media/ringback.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<audio id="callendAudio">
|
||||
<source src="media/callend.ogg" type="audio/ogg" />
|
||||
<source src="media/callend.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<audio id="busyAudio">
|
||||
<source src="media/busy.ogg" type="audio/ogg" />
|
||||
<source src="media/busy.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<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,83 +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);
|
||||
} else {
|
||||
window.matrixChat.showScreen(location.hash.substring(2));
|
||||
}
|
||||
}
|
||||
|
||||
function onHashChange(ev) {
|
||||
routeUrl(window.location);
|
||||
}
|
||||
|
||||
var loaded = false;
|
||||
|
||||
// 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')
|
||||
);
|
||||
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
window.onload = function() {
|
||||
routeUrl(window.location);
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
@@ -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 --ignore olm -t [ envify --NODE_ENV production ] -t reactify index.js | uglifyjs -c -m -o bundle.js",
|
||||
"start": "parallelshell \"watchify --ignore olm -v -d -t reactify index.js -o bundle.js\" \"http-server\""
|
||||
}
|
||||
}
|
||||
14
jenkins.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash -l
|
||||
export NVM_DIR="/home/jenkins/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||
nvm use 4
|
||||
npm install
|
||||
(cd node_modules/matrix-react-sdk && npm run build) # npm doesn't do this when dependencies point at github.com >:(
|
||||
npm run build # Dumps artificats to /vector
|
||||
|
||||
# gzip up ./vector
|
||||
rm vector-*.tar.gz || true # rm previous artifacts without failing if it doesn't exist
|
||||
REACT_SHA=$(grep 'gitHead' node_modules/matrix-react-sdk/package.json | cut -d \" -f 4 | head -c 12) # node_modules deps from 'npm install' don't have a .git dir so can't rev-parse.
|
||||
JSSDK_SHA=$(grep 'gitHead' node_modules/matrix-js-sdk/package.json | cut -d \" -f 4 | head -c 12) # But they do set the commit in package.json under 'gitHead' which we're grabbing here.
|
||||
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)
|
||||
28
karma.conf.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// karma.conf.js
|
||||
var webpack = require('webpack');
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true,
|
||||
frameworks: ['mocha'],
|
||||
files: [
|
||||
'tests.webpack.js'
|
||||
],
|
||||
preprocessors: {
|
||||
'tests.webpack.js': ['webpack']
|
||||
},
|
||||
reporters: ['dots'],
|
||||
webpack: {
|
||||
module: {
|
||||
loaders: [
|
||||
{test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel-loader'}
|
||||
]
|
||||
},
|
||||
watch: true
|
||||
},
|
||||
webpackServer: {
|
||||
noInfo: true
|
||||
}
|
||||
});
|
||||
};
|
||||
74
package.json
@@ -1,40 +1,72 @@
|
||||
{
|
||||
"name": "matrix-react-sdk",
|
||||
"version": "0.0.1",
|
||||
"description": "SDK for matrix.org using React",
|
||||
"name": "vector-web",
|
||||
"version": "0.1.2",
|
||||
"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: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": "npm run build:css && npm run build:compile && npm run build:bundle",
|
||||
"package": "npm run build && mkdir -p packages && tar chvzf packages/vector-`git describe --dirty || echo unknown`.tar.gz vector",
|
||||
"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 start:js\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
|
||||
"start:prod": "parallelshell \"npm run start:js:prod\" \"npm run start:skins:css\" \"http-server -c 1 vector\"",
|
||||
"clean": "rimraf lib vector/bundle.css vector/bundle.js vector/bundle.js.map vector/webpack.css*",
|
||||
"prepublish": "npm run build:css && npm run build:compile"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.5.0",
|
||||
"classnames": "^2.1.2",
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"filesize": "^3.1.2",
|
||||
"flux": "^2.0.3",
|
||||
"matrix-js-sdk": "git://github.com/matrix-org/matrix-js-sdk.git#no-trickle-ice",
|
||||
"flux": "~2.0.3",
|
||||
"gemini-scrollbar": "^1.3.0",
|
||||
"gfm.css": "^1.1.1",
|
||||
"highlight.js": "^9.0.0",
|
||||
"linkifyjs": "^2.0.0-beta.4",
|
||||
"matrix-js-sdk": "https://github.com/matrix-org/matrix-js-sdk.git#develop",
|
||||
"matrix-react-sdk": "https://github.com/matrix-org/matrix-react-sdk.git#develop",
|
||||
"modernizr": "^3.1.0",
|
||||
"q": "^1.4.1",
|
||||
"react": "^0.13.3",
|
||||
"react-loader": "^1.4.0",
|
||||
"linkifyjs": "^2.0.0-beta.4"
|
||||
"react": "^0.14.2",
|
||||
"react-dnd": "^2.0.2",
|
||||
"react-dnd-html5-backend": "^2.0.0",
|
||||
"react-dom": "^0.14.2",
|
||||
"react-gemini-scrollbar": "^2.0.1",
|
||||
"sanitize-html": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel": "^5.8.23",
|
||||
"babel-core": "^5.8.25",
|
||||
"babel-loader": "^5.3.2",
|
||||
"catw": "^1.0.1",
|
||||
"css-raw-loader": "^0.1.1",
|
||||
"http-server": "^0.8.4",
|
||||
"json-loader": "^0.5.3",
|
||||
"parallelshell": "^1.2.0",
|
||||
"react-tools": "^0.13.3",
|
||||
"uglifycss": "0.0.15"
|
||||
"rimraf": "^2.4.3",
|
||||
"source-map-loader": "^0.1.5",
|
||||
"webpack": "^1.12.13",
|
||||
"karma": ">=0.0.0",
|
||||
"karma-cli": ">=0.0.0",
|
||||
"karma-mocha": ">=0.0.0",
|
||||
"karma-webpack": ">=0.0.0",
|
||||
"karma-sourcemap-loader": ">=0.0.0",
|
||||
"karma-chrome-launcher": ">=0.0.0",
|
||||
"mocha": ">=0.0.0",
|
||||
"expect": ">=0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +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.
|
||||
*/
|
||||
|
||||
body {
|
||||
font-family: 'Lato', Helvetica, Arial, Sans-Serif;
|
||||
font-size: 16px;
|
||||
color: #454545;
|
||||
border: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
div.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #80cef4;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* FIXME: show them on hoverover, and fix for firefox */
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html {
|
||||
overflow: -moz-scrollbars-none;
|
||||
}
|
||||
|
||||
/* FIXME: why is all the dialog stuff in here rather than in per-component files? */
|
||||
|
||||
.mx_Dialog_Background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
opacity: 0.2;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.mx_Dialog_Wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mx_Dialog {
|
||||
background-color: #fff;
|
||||
color: #747474;
|
||||
text-align: center;
|
||||
z-index: 2010;
|
||||
font-weight: 300;
|
||||
font-size: 16px;
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.mx_ImageView {
|
||||
margin: 6px;
|
||||
/* hack: flexbox bug? */
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mx_Dialog_content {
|
||||
margin: 24px;
|
||||
}
|
||||
|
||||
.mx_Dialog_buttons {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.mx_Dialog button {
|
||||
border: 0px;
|
||||
height: 36px;
|
||||
border-radius: 36px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
background-color: #80cef4;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
.mx_ErrorDialogTitle,
|
||||
.mx_QuestionDialogTitle {
|
||||
min-height: 16px;
|
||||
padding: 12px;
|
||||
border-bottom: 1px solid #a9dbf4;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.mx_RoomDropTarget,
|
||||
.mx_RoomList_favourites_label,
|
||||
.mx_RoomList_archive_label,
|
||||
.mx_LeftPanel_hideButton,
|
||||
.mx_RoomHeader_search,
|
||||
.mx_RoomSettings_encrypt,
|
||||
.mx_CreateRoom_encrypt,
|
||||
.mx_RightPanel_filebutton
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
.mx_MemberInfo {
|
||||
text-align: center;
|
||||
border: 1px solid #a9dbf4;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
margin-left: -295px;
|
||||
margin-top: 0px;
|
||||
z-index: 1000;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_chevron {
|
||||
padding: 12px;
|
||||
position: absolute;
|
||||
right: -21px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
/*
|
||||
* a hacky shim to extend the hitmask of the overlay to overlap
|
||||
* better with the main menu itself
|
||||
*/
|
||||
.mx_MemberInfo_shim {
|
||||
position: absolute;
|
||||
left: 212px;
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_avatar {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_avatarImg {
|
||||
border-radius: 128px;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_field {
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_MemberInfo_button {
|
||||
vertical-align: middle;
|
||||
max-width: 100px;
|
||||
height: 36px;
|
||||
background-color: #50e3c2;
|
||||
line-height: 36px;
|
||||
border-radius: 36px;
|
||||
color: #fff;
|
||||
margin: auto;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
@@ -1,107 +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_MemberTile {
|
||||
cursor: pointer;
|
||||
display: table-row;
|
||||
height: 49px;
|
||||
}
|
||||
|
||||
.mx_MemberTile_avatar {
|
||||
display: table-cell;
|
||||
padding-right: 12px;
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
vertical-align: middle;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mx_MemberTile_avatarImg {
|
||||
z-index: 20;
|
||||
border-radius: 20px;
|
||||
background-color: #dbdbdb;
|
||||
}
|
||||
|
||||
.mx_MemberTile_inviteEditing {
|
||||
display: initial ! important;
|
||||
}
|
||||
|
||||
.mx_MemberTile_inviteEditing .mx_MemberTile_avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_MemberTile_inviteEditing .mx_MemberTile_name {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.mx_MemberTile_inviteEditing .mx_MemberTile_name input {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #c7c7c7;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
padding: 9px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.mx_MemberTile_power {
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
left: -4px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.mx_MemberTile_name {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_MemberTile_nameWrapper {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_MemberTile_nameSpan {
|
||||
}
|
||||
|
||||
.mx_MemberTile_unavailable .mx_MemberTile_avatar,
|
||||
.mx_MemberTile_unavailable .mx_MemberTile_name,
|
||||
.mx_MemberTile_unavailable .mx_MemberTile_nameSpan
|
||||
{
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.mx_MemberTile_offline .mx_MemberTile_avatar,
|
||||
.mx_MemberTile_offline .mx_MemberTile_name,
|
||||
.mx_MemberTile_offline .mx_MemberTile_nameSpan
|
||||
{
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.mx_MemberTile_zalgo {
|
||||
font-family: Helvetica, Arial, Sans-Serif;
|
||||
}
|
||||
|
||||
.mx_MemberTile_leave {
|
||||
float: right;
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
.mx_MessageTile {
|
||||
max-width: 100%;
|
||||
clear: both;
|
||||
margin-top: 32px;
|
||||
margin-left: 56px;
|
||||
}
|
||||
|
||||
.mx_MessageTile_avatar {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin-left: -64px;
|
||||
margin-top: -7px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mx_MessageTile_avatar img {
|
||||
background-color: #dbdbdb;
|
||||
border-radius: 20px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.mx_MessageTile_continuation {
|
||||
margin-top: 8px ! important;
|
||||
}
|
||||
|
||||
.mx_MessageTile .mx_SenderProfile {
|
||||
color: #454545;
|
||||
opacity: 0.5;
|
||||
font-size: 14px;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mx_MessageTile .mx_MessageTimestamp {
|
||||
color: #454545;
|
||||
opacity: 0.5;
|
||||
font-size: 14px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mx_MessageTile_content {
|
||||
padding-right: 100px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mx_MessageTile_notice .mx_MessageTile_content {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.mx_MessageTile_sending {
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.mx_MessageTile_notSent {
|
||||
color: #f11;
|
||||
}
|
||||
|
||||
.mx_MessageTile_highlight {
|
||||
color: #00f;
|
||||
}
|
||||
|
||||
.mx_MessageTile_msgOption {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mx_MessageTimestamp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_MessageTile_last .mx_MessageTimestamp {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mx_MessageTile:hover .mx_MessageTimestamp {
|
||||
display: block;
|
||||
}
|
||||
@@ -1,154 +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_RoomHeader {
|
||||
}
|
||||
|
||||
.mx_RoomHeader_wrapper {
|
||||
max-width: 720px;
|
||||
margin: auto;
|
||||
height: 88px;
|
||||
border-bottom: 1px solid #a8dbf3;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_leftRow {
|
||||
height: 48px;
|
||||
margin-top: 18px;
|
||||
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-moz-box-ordinal-group: 1;
|
||||
-ms-flex-order: 1;
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_textButton {
|
||||
height: 48px;
|
||||
margin-top: 18px;
|
||||
background-color: #80cef4;
|
||||
border-radius: 48px;
|
||||
margin-right: 8px;
|
||||
color: #fff;
|
||||
line-height: 48px;
|
||||
text-align: center;
|
||||
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-moz-box-ordinal-group: 2;
|
||||
-ms-flex-order: 2;
|
||||
-webkit-order: 2;
|
||||
order: 2;
|
||||
|
||||
-webkit-flex: 0 0 90px;
|
||||
flex: 0 0 90px;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_rightRow {
|
||||
height: 48px;
|
||||
margin-top: 18px;
|
||||
background-color: #fff;
|
||||
border-radius: 48px;
|
||||
border: 1px solid #a9dbf4;
|
||||
|
||||
-webkit-box-ordinal-group: 3;
|
||||
-moz-box-ordinal-group: 3;
|
||||
-ms-flex-order: 3;
|
||||
-webkit-order: 3;
|
||||
order: 3;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_info {
|
||||
display: table-cell;
|
||||
height: 48px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_simpleHeader {
|
||||
line-height: 88px;
|
||||
color: #80cef4;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
overflow: scroll;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_name {
|
||||
vertical-align: middle;
|
||||
height: 28px;
|
||||
color: #80cef4;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_nameEditing {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_nameInput {
|
||||
border-radius: 3px;
|
||||
width: 260px;
|
||||
border: 1px solid #c7c7c7;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
padding: 9px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_topic {
|
||||
vertical-align: bottom;
|
||||
float: left;
|
||||
max-height: 38px;
|
||||
color: #70b5d7;
|
||||
font-weight: 300;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_avatar {
|
||||
display: table-cell;
|
||||
width: 48px;
|
||||
height: 50px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_avatar img {
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_button {
|
||||
height: 48px;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_button img {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
.mx_RoomSettings {
|
||||
max-height: 250px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.mx_RoomSettings_settings {
|
||||
display: table;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.mx_RoomSettings_settings > div {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.mx_RoomSettings_settings > div > * {
|
||||
display: table-cell;
|
||||
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.mx_RoomSettings input,
|
||||
.mx_RoomSettings textarea {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #c7c7c7;
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
padding: 9px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.mx_RoomSettings_description {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
.mx_RoomSettings_buttons {
|
||||
text-align: right;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mx_RoomSettings_button {
|
||||
display: inline;
|
||||
border: 0px;
|
||||
height: 36px;
|
||||
border-radius: 36px;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
background-color: #80cef4;
|
||||
width: auto;
|
||||
margin: auto;
|
||||
padding: 6px;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
@@ -1,166 +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;
|
||||
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
flex-direction: column;
|
||||
-webkit-flex-direction: column;
|
||||
}
|
||||
|
||||
.mx_RoomView .mx_RoomHeader {
|
||||
-webkit-box-ordinal-group: 1;
|
||||
-moz-box-ordinal-group: 1;
|
||||
-ms-flex-order: 1;
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
|
||||
-webkit-flex: 0 0 88px;
|
||||
flex: 0 0 88px;
|
||||
}
|
||||
|
||||
.mx_RoomView_auxPanel {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-moz-box-ordinal-group: 2;
|
||||
-ms-flex-order: 2;
|
||||
-webkit-order: 2;
|
||||
order: 2;
|
||||
|
||||
min-width: 0px;
|
||||
max-width: 720px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
|
||||
border-bottom: 1px solid #a8dbf3;
|
||||
|
||||
overflow: scroll;
|
||||
-webkit-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mx_RoomView_messagePanel {
|
||||
-webkit-box-ordinal-group: 3;
|
||||
-moz-box-ordinal-group: 3;
|
||||
-ms-flex-order: 3;
|
||||
-webkit-order: 3;
|
||||
order: 3;
|
||||
|
||||
-webkit-flex: 1 1 0;
|
||||
flex: 1 1 0;
|
||||
|
||||
width: 100%;
|
||||
margin-top: 18px;
|
||||
margin-bottom: 18px;
|
||||
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.mx_RoomView_messageListWrapper {
|
||||
max-width: 720px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.mx_RoomView_MessageList {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_RoomView_MessageList h2 {
|
||||
clear: both;
|
||||
margin-top: 32px;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid #a8dbf3;
|
||||
}
|
||||
|
||||
.mx_RoomView_invitePrompt {
|
||||
-webkit-box-ordinal-group: 2;
|
||||
-moz-box-ordinal-group: 2;
|
||||
-ms-flex-order: 2;
|
||||
-webkit-order: 2;
|
||||
order: 2;
|
||||
|
||||
min-width: 0px;
|
||||
max-width: 720px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mx_RoomView_statusArea {
|
||||
-webkit-box-ordinal-group: 4;
|
||||
-moz-box-ordinal-group: 4;
|
||||
-ms-flex-order: 4;
|
||||
-webkit-order: 4;
|
||||
order: 4;
|
||||
|
||||
width: 100%;
|
||||
-webkit-flex: 0 0 58px;
|
||||
flex: 0 0 58px;
|
||||
}
|
||||
|
||||
.mx_RoomView_statusAreaBox {
|
||||
max-width: 720px;
|
||||
margin: auto;
|
||||
border-top: 1px solid #a8dbf3;
|
||||
}
|
||||
|
||||
.mx_RoomView_typingBar {
|
||||
margin-top: 17px;
|
||||
margin-left: 56px;
|
||||
color: #818794;
|
||||
}
|
||||
|
||||
.mx_RoomView_typingBar img {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
margin-left: -64px;
|
||||
margin-top: -7px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mx_RoomView .mx_MessageComposer {
|
||||
-webkit-box-ordinal-group: 5;
|
||||
-moz-box-ordinal-group: 5;
|
||||
-ms-flex-order: 5;
|
||||
-webkit-order: 5;
|
||||
order: 5;
|
||||
|
||||
width: 100%;
|
||||
-webkit-flex: 0 0 63px;
|
||||
flex: 0 0 63px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.mx_RoomView_uploadProgressOuter {
|
||||
width: 100%;
|
||||
background-color: black;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
.mx_RoomView_uploadProgressInner {
|
||||
background-color: blue;
|
||||
height: 5px;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 179 B |
|
Before Width: | Height: | Size: 181 B |
|
Before Width: | Height: | Size: 237 B |
|
Before Width: | Height: | Size: 999 B |
|
Before Width: | Height: | Size: 331 B |
|
Before Width: | Height: | Size: 415 B |
|
Before Width: | Height: | Size: 977 B |
|
Before Width: | Height: | Size: 200 B |
|
Before Width: | Height: | Size: 854 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1003 B |
|
Before Width: | Height: | Size: 704 B |
@@ -1,77 +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() {
|
||||
console.log("onClickDiv triggered");
|
||||
this.setState({
|
||||
phase: this.Phases.Edit,
|
||||
})
|
||||
},
|
||||
|
||||
onFocus: function(ev) {
|
||||
ev.target.setSelectionRange(0, ev.target.value.length);
|
||||
},
|
||||
|
||||
onFinish: function(ev) {
|
||||
if (ev.target.value) {
|
||||
this.setValue(ev.target.value, ev.key === "Enter");
|
||||
} else {
|
||||
this.cancelEdit();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var editable_el;
|
||||
|
||||
if (this.state.phase == this.Phases.Display) {
|
||||
if (this.state.value) {
|
||||
editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.state.value}</div>;
|
||||
} else {
|
||||
editable_el = <div ref="display_div" onClick={this.onClickDiv}>{this.props.label}</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} placeholder={this.props.placeHolder} autoFocus/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_EditableText">
|
||||
{editable_el}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,59 +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 ImageViewController = require("../../../../src/controllers/atoms/ImageView");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ImageView',
|
||||
mixins: [ImageViewController],
|
||||
|
||||
render: function() {
|
||||
|
||||
// XXX: can't we just do max-width: 80%, max-height: 80% on the CSS?
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
return (
|
||||
<img className="mx_ImageView" src={this.props.src} style={style} />
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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,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 PresetsController = require("../../../../../src/controllers/atoms/create_room/Presets");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CreateRoomPresets',
|
||||
mixins: [PresetsController],
|
||||
|
||||
onValueChanged: function(ev) {
|
||||
this.props.onChange(ev.target.value)
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
|
||||
<option value={this.Presets.PrivateChat}>Private Chat</option>
|
||||
<option value={this.Presets.PublicChat}>Public Chat</option>
|
||||
<option value={this.Presets.Custom}>Custom</option>
|
||||
</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,79 +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 RoomAliasController = require("../../../../../src/controllers/atoms/create_room/RoomAlias");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomAlias',
|
||||
mixins: [RoomAliasController],
|
||||
|
||||
onValueChanged: function(ev) {
|
||||
this.props.onChange(ev.target.value);
|
||||
},
|
||||
|
||||
onFocus: function(ev) {
|
||||
var target = ev.target;
|
||||
var curr_val = ev.target.value;
|
||||
|
||||
if (this.props.homeserver) {
|
||||
if (curr_val == "") {
|
||||
setTimeout(function() {
|
||||
target.value = "#:" + this.props.homeserver;
|
||||
target.setSelectionRange(1, 1);
|
||||
}, 0);
|
||||
} else {
|
||||
var suffix = ":" + this.props.homeserver;
|
||||
setTimeout(function() {
|
||||
target.setSelectionRange(
|
||||
curr_val.startsWith("#") ? 1 : 0,
|
||||
curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length
|
||||
);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onBlur: function(ev) {
|
||||
var curr_val = ev.target.value;
|
||||
|
||||
if (this.props.homeserver) {
|
||||
if (curr_val == "#:" + this.props.homeserver) {
|
||||
ev.target.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (curr_val != "") {
|
||||
var new_val = ev.target.value;
|
||||
var suffix = ":" + this.props.homeserver;
|
||||
if (!curr_val.startsWith("#")) new_val = "#" + new_val;
|
||||
if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
|
||||
ev.target.value = new_val;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<input type="text" className="mx_RoomAlias" placeholder="Alias (optional)"
|
||||
onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
|
||||
value={this.props.alias}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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 React = require('react');
|
||||
var classNames = require('classnames');
|
||||
|
||||
var dis = require("../../../../src/dispatcher");
|
||||
|
||||
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
|
||||
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'});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_BottomLeftMenu">
|
||||
<div className="mx_BottomLeftMenu_options">
|
||||
<div className="mx_RoomTile" onClick={this.onCreateRoomClick}>
|
||||
<div className="mx_RoomTile_avatar">
|
||||
<img src="img/create-big.png" alt="Create new room" title="Create new room" width="42" height="42"/>
|
||||
</div>
|
||||
<div className="mx_RoomTile_name">Create new room</div>
|
||||
</div>
|
||||
<div className="mx_RoomTile" onClick={this.onRoomDirectoryClick}>
|
||||
<div className="mx_RoomTile_avatar">
|
||||
<img src="img/directory-big.png" alt="Directory" title="Directory" width="42" height="42"/>
|
||||
</div>
|
||||
<div className="mx_RoomTile_name">Directory</div>
|
||||
</div>
|
||||
<div className="mx_RoomTile" onClick={this.onSettingsClick}>
|
||||
<div className="mx_RoomTile_avatar">
|
||||
<img src="img/settings-big.png" alt="Settings" title="Settings" width="42" height="42"/>
|
||||
</div>
|
||||
<div className="mx_RoomTile_name">Settings</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,65 +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 ChangeAvatarController = require("../../../../src/controllers/molecules/ChangeAvatar");
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ChangeAvatar',
|
||||
mixins: [ChangeAvatarController],
|
||||
|
||||
onFileSelected: function(ev) {
|
||||
this.setAvatarFromFile(ev.target.files[0]);
|
||||
},
|
||||
|
||||
onError: function(error) {
|
||||
this.setState({
|
||||
errorText: "Failed to upload profile picture!"
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
switch (this.state.phase) {
|
||||
case this.Phases.Display:
|
||||
case this.Phases.Error:
|
||||
return (
|
||||
<div>
|
||||
<div className="mx_Dialog_content">
|
||||
<img src={this.state.avatarUrl}/>
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
Upload new:
|
||||
<input type="file" onChange={this.onFileSelected}/>
|
||||
{this.state.errorText}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.props.onFinished}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case this.Phases.Uploading:
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,85 +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 ChangePasswordController = require("../../../../src/controllers/molecules/ChangePassword");
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ChangePassword',
|
||||
mixins: [ChangePasswordController],
|
||||
|
||||
onClickChange: function() {
|
||||
var old_password = this.refs.old_input.getDOMNode().value;
|
||||
var new_password = this.refs.new_input.getDOMNode().value;
|
||||
var confirm_password = this.refs.confirm_input.getDOMNode().value;
|
||||
if (new_password != confirm_password) {
|
||||
this.setState({
|
||||
state: this.Phases.Error,
|
||||
errorString: "Passwords don't match"
|
||||
});
|
||||
} else if (new_password == '' || old_password == '') {
|
||||
this.setState({
|
||||
state: this.Phases.Error,
|
||||
errorString: "Passwords can't be empty"
|
||||
});
|
||||
} else {
|
||||
this.changePassword(old_password, new_password);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
switch (this.state.phase) {
|
||||
case this.Phases.Edit:
|
||||
case this.Phases.Error:
|
||||
return (
|
||||
<div>
|
||||
<div className="mx_Dialog_content">
|
||||
<div>{this.state.errorString}</div>
|
||||
<div><label>Old password <input type="password" ref="old_input"/></label></div>
|
||||
<div><label>New password <input type="password" ref="new_input"/></label></div>
|
||||
<div><label>Confirm password <input type="password" ref="confirm_input"/></label></div>
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onClickChange}>Change Password</button>
|
||||
<button onClick={this.props.onFinished}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
case this.Phases.Uploading:
|
||||
return (
|
||||
<div className="mx_Dialog_content">
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
case this.Phases.Success:
|
||||
return (
|
||||
<div>
|
||||
<div className="mx_Dialog_content">
|
||||
Success!
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.props.onFinished}>Ok</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,48 +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 EventAsTextTileController = require("../../../../src/controllers/molecules/EventAsTextTile");
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
|
||||
var TextForEvent = require("../../../../src/TextForEvent");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'EventAsTextTile',
|
||||
mixins: [EventAsTextTileController],
|
||||
|
||||
render: function() {
|
||||
var text = TextForEvent.textForEvent(this.props.mxEvent);
|
||||
var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
|
||||
return (
|
||||
<div className="mx_MessageTile mx_MessageTile_notice">
|
||||
<div className="mx_MessageTile_avatar">
|
||||
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
|
||||
</div>
|
||||
{ timestamp }
|
||||
<span className="mx_SenderProfile"></span>
|
||||
<span className="mx_MessageTile_content">
|
||||
{TextForEvent.textForEvent(this.props.mxEvent)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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,87 +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');
|
||||
var Modal = require('../../../../src/Modal');
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var ImageView = ComponentBroker.get("atoms/ImageView");
|
||||
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function(ev) {
|
||||
var ms = ev.getModifierState();
|
||||
if (ev.button == 0 && !ev.metaKey) {
|
||||
ev.preventDefault();
|
||||
var content = this.props.mxEvent.getContent();
|
||||
var httpUrl = MatrixClientPeg.get().mxcUrlToHttp(content.url);
|
||||
Modal.createDialog(ImageView, {
|
||||
src: httpUrl,
|
||||
width: content.info.w,
|
||||
height: content.info.h
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
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)} onClick={this.onClick}>
|
||||
<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,55 +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 MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
var TextForEvent = require('../../../../src/TextForEvent');
|
||||
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MRoomMemberTile',
|
||||
mixins: [MRoomMemberTileController],
|
||||
|
||||
getMemberEventText: function() {
|
||||
return TextForEvent.textForEvent(this.props.mxEvent);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX: for now, just cheekily borrow the css from message tile...
|
||||
var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
|
||||
var text = this.getMemberEventText();
|
||||
if (!text) return <div/>;
|
||||
return (
|
||||
<div className="mx_MessageTile mx_MessageTile_notice">
|
||||
<div className="mx_MessageTile_avatar">
|
||||
<img src={ this.props.mxEvent.target ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.target, 40, 40, "crop") : null } width="40" height="40" alt=""/>
|
||||
</div>
|
||||
{ timestamp }
|
||||
<span className="mx_SenderProfile"></span>
|
||||
<span className="mx_MessageTile_content">
|
||||
{ text }
|
||||
</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,120 +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 MemberInfoController = require("../../../../src/controllers/molecules/MemberInfo");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MemberInfo',
|
||||
mixins: [MemberInfoController],
|
||||
|
||||
componentDidMount: function() {
|
||||
var self = this;
|
||||
|
||||
var memberInfo = this.getDOMNode();
|
||||
var memberListScroll = document.getElementsByClassName("mx_MemberList_border")[0];
|
||||
if (memberListScroll) {
|
||||
memberInfo.style.top = (memberInfo.parentElement.offsetTop - memberListScroll.scrollTop) + "px";
|
||||
}
|
||||
},
|
||||
|
||||
getDuration: function(time) {
|
||||
if (!time) return;
|
||||
var t = parseInt(time / 1000);
|
||||
var s = t % 60;
|
||||
var m = parseInt(t / 60) % 60;
|
||||
var h = parseInt(t / (60 * 60)) % 24;
|
||||
var d = parseInt(t / (60 * 60 * 24));
|
||||
if (t < 60) {
|
||||
if (t < 0) {
|
||||
return "0s";
|
||||
}
|
||||
return s + "s";
|
||||
}
|
||||
if (t < 60 * 60) {
|
||||
return m + "m";
|
||||
}
|
||||
if (t < 24 * 60 * 60) {
|
||||
return h + "h";
|
||||
}
|
||||
return d + "d ";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var power;
|
||||
if (this.props.member) {
|
||||
var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
|
||||
power = <img src={ img } className="mx_MemberTile_power" width="48" height="48" alt=""/>;
|
||||
}
|
||||
var activeAgo = "unknown";
|
||||
if (this.state.active >= 0) {
|
||||
activeAgo = this.getDuration(this.state.active);
|
||||
}
|
||||
var kickButton, banButton, muteButton, giveModButton;
|
||||
if (this.state.can.kick) {
|
||||
kickButton = <div className="mx_MemberInfo_button" onClick={this.onKick}>
|
||||
Kick
|
||||
</div>;
|
||||
}
|
||||
if (this.state.can.ban) {
|
||||
banButton = <div className="mx_MemberInfo_button" onClick={this.onBan}>
|
||||
Ban
|
||||
</div>;
|
||||
}
|
||||
if (this.state.can.mute) {
|
||||
var muteLabel = this.state.muted ? "Unmute" : "Mute";
|
||||
muteButton = <div className="mx_MemberInfo_button" onClick={this.onMuteToggle}>
|
||||
{muteLabel}
|
||||
</div>;
|
||||
}
|
||||
if (this.state.can.modifyLevel) {
|
||||
var giveOpLabel = this.state.isTargetMod ? "Revoke Mod" : "Make Mod";
|
||||
giveModButton = <div className="mx_MemberInfo_button" onClick={this.onModToggle}>
|
||||
{giveOpLabel}
|
||||
</div>
|
||||
}
|
||||
|
||||
var opLabel;
|
||||
if (this.state.isTargetMod) {
|
||||
var level = this.props.member.powerLevelNorm + "%";
|
||||
opLabel = <div className="mx_MemberInfo_field">Moderator ({level})</div>
|
||||
}
|
||||
return (
|
||||
<div className="mx_MemberInfo">
|
||||
<img className="mx_MemberInfo_chevron" src="img/chevron-right.png" width="9" height="16" />
|
||||
<div className="mx_MemberInfo_shim"></div>
|
||||
<div className="mx_MemberInfo_avatar">
|
||||
<img className="mx_MemberInfo_avatarImg"
|
||||
src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 128, 128, "crop") : null }
|
||||
width="128" height="128" alt=""/>
|
||||
</div>
|
||||
<div className="mx_MemberInfo_field">{this.props.member.userId}</div>
|
||||
{opLabel}
|
||||
<div className="mx_MemberInfo_field">Presence: {this.state.presence}</div>
|
||||
<div className="mx_MemberInfo_field">Last active: {activeAgo}</div>
|
||||
<div className="mx_MemberInfo_button" onClick={this.onChatClick}>Start chat</div>
|
||||
{muteButton}
|
||||
{kickButton}
|
||||
{banButton}
|
||||
{giveModButton}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,107 +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 Modal = require("../../../../src/Modal");
|
||||
var MemberTileController = require("../../../../src/controllers/molecules/MemberTile");
|
||||
var MemberInfo = ComponentBroker.get('molecules/MemberInfo');
|
||||
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
|
||||
|
||||
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
|
||||
// Revert to Arial when this happens, which on OSX works at least.
|
||||
var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MemberTile',
|
||||
mixins: [MemberTileController],
|
||||
|
||||
// XXX: should these be in the controller?
|
||||
getInitialState: function() {
|
||||
return { 'hover': false };
|
||||
},
|
||||
|
||||
mouseEnter: function(e) {
|
||||
this.setState({ 'hover': true });
|
||||
},
|
||||
|
||||
mouseLeave: function(e) {
|
||||
this.setState({ 'hover': false });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var isMyUser = MatrixClientPeg.get().credentials.userId == this.props.member.userId;
|
||||
|
||||
var power;
|
||||
if (this.props.member) {
|
||||
var img = "img/p/p" + Math.floor(20 * this.props.member.powerLevelNorm / 100) + ".png";
|
||||
power = <img src={ img } className="mx_MemberTile_power" width="48" height="48" alt=""/>;
|
||||
}
|
||||
var presenceClass = "mx_MemberTile_offline";
|
||||
var mainClassName = "mx_MemberTile ";
|
||||
if (this.props.member.user) {
|
||||
if (this.props.member.user.presence === "online") {
|
||||
presenceClass = "mx_MemberTile_online";
|
||||
}
|
||||
else if (this.props.member.user.presence === "unavailable") {
|
||||
presenceClass = "mx_MemberTile_unavailable";
|
||||
}
|
||||
}
|
||||
mainClassName += presenceClass;
|
||||
|
||||
var name = this.props.member.name;
|
||||
if (isMyUser) name += " (me)";
|
||||
var leave = isMyUser ? <span className="mx_MemberTile_leave" onClick={this.onLeaveClick}>X</span> : null;
|
||||
|
||||
var nameClass = this.state.hover ? "mx_MemberTile_nameSpan" : "mx_MemberTile_name";
|
||||
if (zalgo.test(name)) {
|
||||
nameClass += " mx_MemberTile_zalgo";
|
||||
}
|
||||
|
||||
var nameEl;
|
||||
if (this.state.hover) {
|
||||
nameEl =
|
||||
<div className="mx_MemberTile_nameWrapper">
|
||||
<MemberInfo member={this.props.member} />
|
||||
<span className={nameClass}>{name}</span>
|
||||
{leave}
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
nameEl =
|
||||
<div className={nameClass}>
|
||||
{name}
|
||||
{leave}
|
||||
</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={mainClassName} onMouseEnter={ this.mouseEnter } onMouseLeave={ this.mouseLeave }>
|
||||
<div className="mx_MemberTile_avatar">
|
||||
<img className="mx_MemberTile_avatarImg"
|
||||
src={ this.props.member ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.member, 40, 40, "crop") : null }
|
||||
width="40" height="40" alt=""/>
|
||||
{ power }
|
||||
</div>
|
||||
{ nameEl }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,65 +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 MessageComposerController = require("../../../../src/controllers/molecules/MessageComposer");
|
||||
var ContentMessages = require("../../../../src/ContentMessages");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MessageComposer',
|
||||
mixins: [MessageComposerController],
|
||||
|
||||
onUploadClick: function(ev) {
|
||||
this.refs.uploadInput.getDOMNode().click();
|
||||
},
|
||||
|
||||
onUploadFileSelected: function(ev) {
|
||||
var files = ev.target.files;
|
||||
// MessageComposer shouldn't have to rely on it's parent passing in a callback to upload a file
|
||||
if (files && files.length > 0) {
|
||||
this.props.uploadFile(files[0]);
|
||||
}
|
||||
this.refs.uploadInput.getDOMNode().value = null;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
|
||||
var uploadInputStyle = {display: 'none'};
|
||||
return (
|
||||
<div className="mx_MessageComposer">
|
||||
<div className="mx_MessageComposer_wrapper">
|
||||
<div className="mx_MessageComposer_row">
|
||||
<div className="mx_MessageComposer_avatar">
|
||||
<img src={ MatrixClientPeg.get().getAvatarUrlForMember(me, 40, 40, "crop") } width="40" height="40" alt=""/>
|
||||
</div>
|
||||
<div className="mx_MessageComposer_input">
|
||||
<textarea ref="textarea" onKeyDown={this.onKeyDown} placeholder="Type a message" />
|
||||
</div>
|
||||
<div className="mx_MessageComposer_upload" onClick={this.onUploadClick}>
|
||||
<img src="img/upload.png" width="32" height="32"/>
|
||||
<input type="file" style={uploadInputStyle} ref="uploadInput" onChange={this.onUploadFileSelected} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,87 +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 MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
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: ['sending', 'queued'].indexOf(
|
||||
this.props.mxEvent.status
|
||||
) !== -1,
|
||||
mx_MessageTile_notSent: this.props.mxEvent.status == 'not_sent',
|
||||
mx_MessageTile_highlight: this.shouldHighlight(),
|
||||
mx_MessageTile_continuation: this.props.continuation,
|
||||
mx_MessageTile_last: this.props.last,
|
||||
});
|
||||
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
var avatar, sender, resend;
|
||||
if (!this.props.continuation) {
|
||||
avatar = (
|
||||
<div className="mx_MessageTile_avatar">
|
||||
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
|
||||
</div>
|
||||
);
|
||||
sender = <SenderProfile mxEvent={this.props.mxEvent} />;
|
||||
}
|
||||
if (this.props.mxEvent.status === "not_sent" && !this.state.resending) {
|
||||
resend = <button className="mx_MessageTile_msgOption" onClick={this.onResend}>
|
||||
Resend
|
||||
</button>;
|
||||
}
|
||||
return (
|
||||
<div className={classes}>
|
||||
{ avatar }
|
||||
{ timestamp }
|
||||
{ resend }
|
||||
{ sender }
|
||||
<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,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 classNames = require('classnames');
|
||||
|
||||
//var RoomCreateController = require("../../../../src/controllers/molecules/RoomCreateController");
|
||||
|
||||
var MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomCreate',
|
||||
// mixins: [RoomCreateController],
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_RoomCreate">
|
||||
<div className="mx_RoomCreate_table">
|
||||
<div className="mx_RoomTile">
|
||||
<div className="mx_RoomTile_avatar">
|
||||
<img src="img/create.png" width="32" height="32"/>
|
||||
</div>
|
||||
<div className="mx_RoomTile_name">Create new room</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,126 +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 MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
var RoomHeaderController = require("../../../../src/controllers/molecules/RoomHeader");
|
||||
var EditableText = ComponentBroker.get("atoms/EditableText");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomHeader',
|
||||
mixins: [RoomHeaderController],
|
||||
|
||||
onNameChange: function(new_name) {
|
||||
if (this.props.room.name != new_name && new_name) {
|
||||
MatrixClientPeg.get().setRoomName(this.props.room.roomId, new_name);
|
||||
}
|
||||
},
|
||||
|
||||
getRoomName: function() {
|
||||
return this.refs.name_edit.getDOMNode().value;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
var header;
|
||||
if (this.props.simpleHeader) {
|
||||
header =
|
||||
<div className="mx_RoomHeader_wrapper">
|
||||
<div className="mx_RoomHeader_simpleHeader">
|
||||
{ this.props.simpleHeader }
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
||||
|
||||
var callButtons;
|
||||
if (this.state) {
|
||||
switch (this.state.call_state) {
|
||||
case "ringback":
|
||||
case "connected":
|
||||
callButtons = (
|
||||
<div className="mx_RoomHeader_textButton" onClick={this.onHangupClick}>
|
||||
End call
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var name = null;
|
||||
var topic_el = null;
|
||||
var save_button = null;
|
||||
var settings_button = null;
|
||||
var actual_name = this.props.room.currentState.getStateEvents('m.room.name', '');
|
||||
if (actual_name) actual_name = actual_name.getContent().name;
|
||||
if (this.props.editing) {
|
||||
name =
|
||||
<div className="mx_RoomHeader_nameEditing">
|
||||
<input className="mx_RoomHeader_nameInput" type="text" defaultValue={actual_name} placeholder="Name" ref="name_edit"/>
|
||||
</div>
|
||||
// if (topic) topic_el = <div className="mx_RoomHeader_topic"><textarea>{ topic.getContent().topic }</textarea></div>
|
||||
} else {
|
||||
name =
|
||||
<div className="mx_RoomHeader_name">
|
||||
<EditableText label={this.props.room.name} initialValue={actual_name} placeHolder="Name" onValueChanged={this.onNameChange} />
|
||||
</div>
|
||||
if (topic) topic_el = <div className="mx_RoomHeader_topic">{ topic.getContent().topic }</div>;
|
||||
settings_button = (
|
||||
<div className="mx_RoomHeader_button" onClick={this.props.onSettingsClick}>
|
||||
<img src="img/settings.png" width="32" height="32"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
header =
|
||||
<div className="mx_RoomHeader_wrapper">
|
||||
<div className="mx_RoomHeader_leftRow">
|
||||
<div className="mx_RoomHeader_avatar">
|
||||
<img src={ MatrixClientPeg.get().getAvatarUrlForRoom(this.props.room, 48, 48, "crop") } width="48" height="48" alt=""/>
|
||||
</div>
|
||||
<div className="mx_RoomHeader_info">
|
||||
{ name }
|
||||
{ topic_el }
|
||||
</div>
|
||||
</div>
|
||||
{callButtons}
|
||||
<div className="mx_RoomHeader_rightRow">
|
||||
{ settings_button }
|
||||
<div className="mx_RoomHeader_button mx_RoomHeader_search">
|
||||
<img src="img/search.png" title="Search" alt="Search" width="32" height="32"/>
|
||||
</div>
|
||||
<div className="mx_RoomHeader_button mx_RoomHeader_video" onClick={this.onVideoClick}>
|
||||
<img src="img/video.png" title="Video call" alt="Video call" width="32" height="32"/>
|
||||
</div>
|
||||
<div className="mx_RoomHeader_button mx_RoomHeader_voice" onClick={this.onVoiceClick}>
|
||||
<img src="img/voip.png" title="VoIP call" alt="VoIP call" width="32" height="32"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomHeader">
|
||||
{ header }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,219 +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 RoomSettingsController = require("../../../../src/controllers/molecules/RoomSettings");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomSettings',
|
||||
mixins: [RoomSettingsController],
|
||||
|
||||
getTopic: function() {
|
||||
return this.refs.topic.getDOMNode().value;
|
||||
},
|
||||
|
||||
getJoinRules: function() {
|
||||
return this.refs.is_private.getDOMNode().checked ? "invite" : "public";
|
||||
},
|
||||
|
||||
getHistoryVisibility: function() {
|
||||
return this.refs.share_history.getDOMNode().checked ? "shared" : "invited";
|
||||
},
|
||||
|
||||
getPowerLevels: function() {
|
||||
if (!this.state.power_levels_changed) return undefined;
|
||||
|
||||
var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
|
||||
power_levels = power_levels.getContent();
|
||||
|
||||
var new_power_levels = {
|
||||
ban: parseInt(this.refs.ban.getDOMNode().value),
|
||||
kick: parseInt(this.refs.kick.getDOMNode().value),
|
||||
redact: parseInt(this.refs.redact.getDOMNode().value),
|
||||
invite: parseInt(this.refs.invite.getDOMNode().value),
|
||||
events_default: parseInt(this.refs.events_default.getDOMNode().value),
|
||||
state_default: parseInt(this.refs.state_default.getDOMNode().value),
|
||||
users_default: parseInt(this.refs.users_default.getDOMNode().value),
|
||||
users: power_levels.users,
|
||||
events: power_levels.events,
|
||||
};
|
||||
|
||||
return new_power_levels;
|
||||
},
|
||||
|
||||
onPowerLevelsChanged: function() {
|
||||
this.setState({
|
||||
power_levels_changed: true
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var topic = this.props.room.currentState.getStateEvents('m.room.topic', '');
|
||||
if (topic) topic = topic.getContent().topic;
|
||||
|
||||
var join_rule = this.props.room.currentState.getStateEvents('m.room.join_rules', '');
|
||||
if (join_rule) join_rule = join_rule.getContent().join_rule;
|
||||
|
||||
var history_visibility = this.props.room.currentState.getStateEvents('m.room.history_visibility', '');
|
||||
if (history_visibility) history_visibility = history_visibility.getContent().history_visibility;
|
||||
|
||||
var power_levels = this.props.room.currentState.getStateEvents('m.room.power_levels', '');
|
||||
|
||||
if (power_levels) {
|
||||
power_levels = power_levels.getContent();
|
||||
|
||||
var ban_level = parseInt(power_levels.ban);
|
||||
var kick_level = parseInt(power_levels.kick);
|
||||
var redact_level = parseInt(power_levels.redact);
|
||||
var invite_level = parseInt(power_levels.invite || 0);
|
||||
var send_level = parseInt(power_levels.events_default || 0);
|
||||
var state_level = parseInt(power_levels.state_default || 0);
|
||||
var default_user_level = parseInt(power_levels.users_default || 0);
|
||||
|
||||
if (power_levels.ban == undefined) ban_level = 50;
|
||||
if (power_levels.kick == undefined) kick_level = 50;
|
||||
if (power_levels.redact == undefined) redact_level = 50;
|
||||
|
||||
var user_levels = power_levels.users || [];
|
||||
var events_levels = power_levels.events || [];
|
||||
|
||||
var user_id = MatrixClientPeg.get().credentials.userId;
|
||||
|
||||
var current_user_level = user_levels[user_id];
|
||||
if (current_user_level == undefined) current_user_level = default_user_level;
|
||||
|
||||
var power_level_level = events_levels["m.room.power_levels"];
|
||||
if (power_level_level == undefined) {
|
||||
power_level_level = state_level;
|
||||
}
|
||||
|
||||
var can_change_levels = current_user_level >= power_level_level;
|
||||
} else {
|
||||
var ban_level = 50;
|
||||
var kick_level = 50;
|
||||
var redact_level = 50;
|
||||
var invite_level = 0;
|
||||
var send_level = 0;
|
||||
var state_level = 0;
|
||||
var default_user_level = 0;
|
||||
|
||||
var user_levels = [];
|
||||
var events_levels = [];
|
||||
|
||||
var current_user_level = 0;
|
||||
|
||||
var power_level_level = 0;
|
||||
|
||||
var can_change_levels = false;
|
||||
}
|
||||
|
||||
var banned = this.props.room.getMembersWithMemership("ban");
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSettings">
|
||||
<textarea className="mx_RoomSettings_description" placeholder="Topic" defaultValue={topic} ref="topic"/> <br/>
|
||||
<label><input type="checkbox" ref="is_private" defaultChecked={join_rule != "public"}/> Make this room private</label> <br/>
|
||||
<label><input type="checkbox" ref="share_history" defaultChecked={history_visibility == "shared"}/> Share message history with new users</label> <br/>
|
||||
<label className="mx_RoomSettings_encrypt"><input type="checkbox" /> Encrypt room</label> <br/>
|
||||
|
||||
<h3>Power levels</h3>
|
||||
<div className="mx_RoomSettings_power_levels mx_RoomSettings_settings">
|
||||
<div>
|
||||
<label htmlFor="mx_RoomSettings_ban_level">Ban level</label>
|
||||
<input type="text" defaultValue={ban_level} size="3" ref="ban" id="mx_RoomSettings_ban_level"
|
||||
disabled={!can_change_levels || current_user_level < ban_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mx_RoomSettings_kick_level">Kick level</label>
|
||||
<input type="text" defaultValue={kick_level} size="3" ref="kick" id="mx_RoomSettings_kick_level"
|
||||
disabled={!can_change_levels || current_user_level < kick_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mx_RoomSettings_redact_level">Redact level</label>
|
||||
<input type="text" defaultValue={redact_level} size="3" ref="redact" id="mx_RoomSettings_redact_level"
|
||||
disabled={!can_change_levels || current_user_level < redact_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mx_RoomSettings_invite_level">Invite level</label>
|
||||
<input type="text" defaultValue={invite_level} size="3" ref="invite" id="mx_RoomSettings_invite_level"
|
||||
disabled={!can_change_levels || current_user_level < invite_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mx_RoomSettings_event_level">Send event level</label>
|
||||
<input type="text" defaultValue={send_level} size="3" ref="events_default" id="mx_RoomSettings_event_level"
|
||||
disabled={!can_change_levels || current_user_level < send_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mx_RoomSettings_state_level">Set state level</label>
|
||||
<input type="text" defaultValue={state_level} size="3" ref="state_default" id="mx_RoomSettings_state_level"
|
||||
disabled={!can_change_levels || current_user_level < state_level} onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="mx_RoomSettings_user_level">Default user level</label>
|
||||
<input type="text" defaultValue={default_user_level} size="3" ref="users_default"
|
||||
id="mx_RoomSettings_user_level" disabled={!can_change_levels || current_user_level < default_user_level}
|
||||
onChange={this.onPowerLevelsChanged}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>User levels</h3>
|
||||
<div className="mx_RoomSettings_user_levels mx_RoomSettings_settings">
|
||||
{Object.keys(user_levels).map(function(user, i) {
|
||||
return (
|
||||
<div key={user}>
|
||||
<label htmlFor={"mx_RoomSettings_user_"+i}>{user}</label>
|
||||
<input type="text" defaultValue={user_levels[user]} size="3" id={"mx_RoomSettings_user_"+i} disabled/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<h3>Event levels</h3>
|
||||
<div className="mx_RoomSettings_event_lvels mx_RoomSettings_settings">
|
||||
{Object.keys(events_levels).map(function(event_type, i) {
|
||||
return (
|
||||
<div key={event_type}>
|
||||
<label htmlFor={"mx_RoomSettings_event_"+i}>{event_type}</label>
|
||||
<input type="text" defaultValue={events_levels[event_type]} size="3" id={"mx_RoomSettings_event_"+i} disabled/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<h3>Banned users</h3>
|
||||
<div className="mx_RoomSettings_banned">
|
||||
{banned.map(function(member, i) {
|
||||
return (
|
||||
<div key={i}>
|
||||
{member.userId}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mx_RoomSettings_buttons">
|
||||
<div className="mx_RoomSettings_button" onClick={this.props.onSaveClick}>
|
||||
Save this room
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,65 +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,
|
||||
'mx_RoomTile_selected': this.props.selected,
|
||||
'mx_RoomTile_unread': this.props.unread,
|
||||
'mx_RoomTile_highlight': this.props.highlight,
|
||||
'mx_RoomTile_invited': this.props.room.currentState.members[myUserId].membership == 'invite'
|
||||
});
|
||||
var name = this.props.room.name.replace(":", ":\u200b");
|
||||
var badge;
|
||||
if (this.props.highlight) {
|
||||
badge = <div className="mx_RoomTile_badge"/>;
|
||||
}
|
||||
/*
|
||||
if (this.props.highlight) {
|
||||
badge = <div className="mx_RoomTile_badge">!</div>;
|
||||
}
|
||||
else if (this.props.unread) {
|
||||
badge = <div className="mx_RoomTile_badge">1</div>;
|
||||
}
|
||||
var nameCell;
|
||||
if (badge) {
|
||||
nameCell = <div className="mx_RoomTile_nameBadge"><div className="mx_RoomTile_name">{name}</div><div className="mx_RoomTile_badgeCell">{badge}</div></div>;
|
||||
}
|
||||
else {
|
||||
nameCell = <div className="mx_RoomTile_name">{name}</div>;
|
||||
}
|
||||
*/
|
||||
return (
|
||||
<div className={classes} onClick={this.onClick}>
|
||||
<div className="mx_RoomTile_avatar"><img src={ MatrixClientPeg.get().getAvatarUrlForRoom(this.props.room, 40, 40, "crop") } width="40" height="40" alt=""/>{ badge }</div>
|
||||
<div className="mx_RoomTile_name">{name}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,53 +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 SenderProfileController = require("../../../../src/controllers/molecules/SenderProfile");
|
||||
|
||||
// The Lato WOFF doesn't include sensible combining diacritics, so Chrome chokes on rendering them.
|
||||
// Revert to Arial when this happens, which on OSX works at least.
|
||||
var zalgo = /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/;
|
||||
|
||||
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 classes = classNames({
|
||||
mx_SenderProfile: true,
|
||||
// taken from https://en.wikipedia.org/wiki/Combining_character
|
||||
mx_SenderProfile_zalgo: zalgo.test(name),
|
||||
});
|
||||
|
||||
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={classes}>
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,51 +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 Modal = require('../../../../src/Modal');
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var ErrorDialog = ComponentBroker.get('organisms/ErrorDialog');
|
||||
|
||||
var ServerConfigController = require("../../../../src/controllers/molecules/ServerConfig");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ServerConfig',
|
||||
mixins: [ServerConfigController],
|
||||
|
||||
showHelpPopup: function() {
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: 'Custom Server Options',
|
||||
description: "You can use the custom server options to log into other Matrix servers by specifying a different Home server URL. This allows you to use Vector with an existing Matrix account on a different Home server. You can also set a cutom Identity server but this will affect people ability to find you if you use a server in a group other than tha main Matrix.org group.",
|
||||
button: "Dismiss",
|
||||
focus: true
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_ServerConfig">
|
||||
<label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl">Home server URL</label>
|
||||
<input className="mx_Login_field" id="hsurl" type="text" value={this.state.hs_url} onChange={this.hsChanged} />
|
||||
<label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl">Identity server URL</label>
|
||||
<input className="mx_Login_field" type="text" value={this.state.is_url} onChange={this.isChanged} />
|
||||
<a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>What does this mean?</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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');
|
||||
|
||||
var UnknownMessageTileController = require("../../../../src/controllers/molecules/UnknownMessageTile");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'UnknownMessageTile',
|
||||
mixins: [UnknownMessageTileController],
|
||||
|
||||
render: function() {
|
||||
var content = this.props.mxEvent.getContent();
|
||||
return (
|
||||
<span className="mx_UnknownMessageTile">
|
||||
{content.body}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,46 +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);
|
||||
this.refs.user_id_input.getDOMNode().value = "";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
return (
|
||||
<div>
|
||||
<ul className="mx_UserSelector_UserIdList" ref="list">
|
||||
{this.props.selected_users.map(function(user_id, i) {
|
||||
return <li key={user_id}>{user_id} - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>
|
||||
})}
|
||||
</ul>
|
||||
<input type="text" ref="user_id_input" defaultValue="" className="mx_UserSelector_userIdInput" placeholder="ex. @bob:example.com"/>
|
||||
<button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">Add User</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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 MatrixClientPeg = require("../../../../../src/MatrixClientPeg");
|
||||
var ComponentBroker = require('../../../../../src/ComponentBroker');
|
||||
var CallViewController = require(
|
||||
"../../../../../src/controllers/molecules/voip/CallView"
|
||||
);
|
||||
var VideoView = ComponentBroker.get('molecules/voip/VideoView');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CallView',
|
||||
mixins: [CallViewController],
|
||||
|
||||
getVideoView: function() {
|
||||
return this.refs.video;
|
||||
},
|
||||
|
||||
render: function(){
|
||||
return (
|
||||
<VideoView ref="video"/>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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 MatrixClientPeg = require("../../../../../src/MatrixClientPeg");
|
||||
var IncomingCallBoxController = require(
|
||||
"../../../../../src/controllers/molecules/voip/IncomingCallBox"
|
||||
);
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'IncomingCallBox',
|
||||
mixins: [IncomingCallBoxController],
|
||||
|
||||
getRingAudio: function() {
|
||||
return this.refs.ringAudio.getDOMNode();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.incomingCall || !this.state.incomingCall.roomId) {
|
||||
return (
|
||||
<div>
|
||||
<audio ref="ringAudio" loop>
|
||||
<source src="media/ring.ogg" type="audio/ogg" />
|
||||
<source src="media/ring.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
var caller = MatrixClientPeg.get().getRoom(this.state.incomingCall.roomId).name;
|
||||
return (
|
||||
<div className="mx_IncomingCallBox">
|
||||
<img className="mx_IncomingCallBox_chevron" src="img/chevron-left.png" width="9" height="16" />
|
||||
<audio ref="ringAudio" loop>
|
||||
<source src="media/ring.ogg" type="audio/ogg" />
|
||||
<source src="media/ring.mp3" type="audio/mpeg" />
|
||||
</audio>
|
||||
<div className="mx_IncomingCallBox_title">
|
||||
Incoming { this.state.incomingCall ? this.state.incomingCall.type : '' } call from { caller }
|
||||
</div>
|
||||
<div className="mx_IncomingCallBox_buttons">
|
||||
<div className="mx_IncomingCallBox_buttons_cell">
|
||||
<div className="mx_IncomingCallBox_buttons_decline" onClick={this.onRejectClick}>
|
||||
Decline
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_IncomingCallBox_buttons_cell">
|
||||
<div className="mx_IncomingCallBox_buttons_accept" onClick={this.onAnswerClick}>
|
||||
Accept
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,50 +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 MCallAnswerTileController = require("../../../../../src/controllers/molecules/voip/MCallAnswerTile");
|
||||
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MCallAnswerTile',
|
||||
mixins: [MCallAnswerTileController],
|
||||
|
||||
getAnswerText: function(event) {
|
||||
var senderName = event.sender ? event.sender.name : "Someone";
|
||||
return senderName + " answered the call.";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX: for now, just cheekily borrow the css from message tile...
|
||||
return (
|
||||
<div className="mx_MessageTile mx_MessageTile_notice">
|
||||
<div className="mx_MessageTile_avatar">
|
||||
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
|
||||
</div>
|
||||
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
<span className="mx_SenderProfile"></span>
|
||||
<span className="mx_MessageTile_content">
|
||||
{this.getAnswerText(this.props.mxEvent)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,50 +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 MCallHangupTileController = require("../../../../../src/controllers/molecules/voip/MCallHangupTile");
|
||||
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MCallHangupTile',
|
||||
mixins: [MCallHangupTileController],
|
||||
|
||||
getHangupText: function(event) {
|
||||
var senderName = event.sender ? event.sender.name : "Someone";
|
||||
return senderName + " ended the call.";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX: for now, just cheekily borrow the css from message tile...
|
||||
return (
|
||||
<div className="mx_MessageTile mx_MessageTile_notice">
|
||||
<div className="mx_MessageTile_avatar">
|
||||
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
|
||||
</div>
|
||||
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
<span className="mx_SenderProfile"></span>
|
||||
<span className="mx_MessageTile_content">
|
||||
{this.getHangupText(this.props.mxEvent)}
|
||||
</span>
|
||||
</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 MatrixClientPeg = require("../../../../../src/MatrixClientPeg");
|
||||
var ComponentBroker = require('../../../../../src/ComponentBroker');
|
||||
var MCallInviteTileController = require("../../../../../src/controllers/molecules/voip/MCallInviteTile");
|
||||
var MessageTimestamp = ComponentBroker.get('atoms/MessageTimestamp');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MCallInviteTile',
|
||||
mixins: [MCallInviteTileController],
|
||||
|
||||
getInviteText: function(event) {
|
||||
var senderName = event.sender ? event.sender.name : "Someone";
|
||||
// FIXME: Find a better way to determine this from the event?
|
||||
var type = "voice";
|
||||
if (event.getContent().offer &&
|
||||
event.getContent().offer.sdp.indexOf('m=video') !== -1) {
|
||||
type = "video";
|
||||
}
|
||||
return senderName + " placed a " + type + " call.";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX: for now, just cheekily borrow the css from message tile...
|
||||
return (
|
||||
<div className="mx_MessageTile mx_MessageTile_notice">
|
||||
<div className="mx_MessageTile_avatar">
|
||||
<img src={ this.props.mxEvent.sender ? MatrixClientPeg.get().getAvatarUrlForMember(this.props.mxEvent.sender, 40, 40, "crop") : null } width="40" height="40" alt=""/>
|
||||
</div>
|
||||
<MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||
<span className="mx_SenderProfile"></span>
|
||||
<span className="mx_MessageTile_content">
|
||||
{this.getInviteText(this.props.mxEvent)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,50 +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 VideoViewController = require("../../../../../src/controllers/molecules/voip/VideoView");
|
||||
var VideoFeed = ComponentBroker.get('atoms/voip/VideoFeed');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'VideoView',
|
||||
mixins: [VideoViewController],
|
||||
|
||||
getRemoteVideoElement: function() {
|
||||
return this.refs.remote.getDOMNode();
|
||||
},
|
||||
|
||||
getLocalVideoElement: function() {
|
||||
return this.refs.local.getDOMNode();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_VideoView">
|
||||
<div className="mx_VideoView_remoteVideoFeed">
|
||||
<VideoFeed ref="remote"/>
|
||||
</div>
|
||||
<div className="mx_VideoView_localVideoFeed">
|
||||
<VideoFeed ref="local"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,170 +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 PresetValues = require('../../../../src/controllers/atoms/create_room/Presets').Presets;
|
||||
|
||||
var CreateRoomButton = ComponentBroker.get("atoms/create_room/CreateRoomButton");
|
||||
var RoomAlias = ComponentBroker.get("atoms/create_room/RoomAlias");
|
||||
var Presets = ComponentBroker.get("atoms/create_room/Presets");
|
||||
var UserSelector = ComponentBroker.get("molecules/UserSelector");
|
||||
var RoomHeader = ComponentBroker.get("molecules/RoomHeader");
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'CreateRoom',
|
||||
mixins: [CreateRoomController],
|
||||
|
||||
getPreset: function() {
|
||||
return this.refs.presets.getPreset();
|
||||
},
|
||||
|
||||
getName: function() {
|
||||
return this.refs.name_textbox.getName();
|
||||
},
|
||||
|
||||
getTopic: function() {
|
||||
return this.refs.topic.getTopic();
|
||||
},
|
||||
|
||||
getAliasLocalpart: function() {
|
||||
return this.refs.alias.getAliasLocalpart();
|
||||
},
|
||||
|
||||
getInvitedUsers: function() {
|
||||
return this.refs.user_selector.getUserIds();
|
||||
},
|
||||
|
||||
onPresetChanged: function(preset) {
|
||||
switch (preset) {
|
||||
case PresetValues.PrivateChat:
|
||||
this.setState({
|
||||
preset: preset,
|
||||
is_private: true,
|
||||
share_history: false,
|
||||
});
|
||||
break;
|
||||
case PresetValues.PublicChat:
|
||||
this.setState({
|
||||
preset: preset,
|
||||
is_private: false,
|
||||
share_history: true,
|
||||
});
|
||||
break;
|
||||
case PresetValues.Custom:
|
||||
this.setState({
|
||||
preset: preset,
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onPrivateChanged: function(ev) {
|
||||
this.setState({
|
||||
preset: PresetValues.Custom,
|
||||
is_private: ev.target.checked,
|
||||
});
|
||||
},
|
||||
|
||||
onShareHistoryChanged: function(ev) {
|
||||
this.setState({
|
||||
preset: PresetValues.Custom,
|
||||
share_history: ev.target.checked,
|
||||
});
|
||||
},
|
||||
|
||||
onTopicChange: function(ev) {
|
||||
this.setState({
|
||||
topic: ev.target.value,
|
||||
});
|
||||
},
|
||||
|
||||
onNameChange: function(ev) {
|
||||
this.setState({
|
||||
room_name: ev.target.value,
|
||||
});
|
||||
},
|
||||
|
||||
onInviteChanged: function(invited_users) {
|
||||
this.setState({
|
||||
invited_users: invited_users,
|
||||
});
|
||||
},
|
||||
|
||||
onAliasChanged: function(alias) {
|
||||
this.setState({
|
||||
alias: alias
|
||||
})
|
||||
},
|
||||
|
||||
onEncryptChanged: function(ev) {
|
||||
this.setState({
|
||||
encrypt: ev.target.checked,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var curr_phase = this.state.phase;
|
||||
if (curr_phase == this.phases.CREATING) {
|
||||
return (
|
||||
<Loader/>
|
||||
);
|
||||
} 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">
|
||||
<RoomHeader simpleHeader="Create room" />
|
||||
<div className="mx_CreateRoom_body">
|
||||
<input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder="Name"/> <br />
|
||||
<textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder="Topic"/> <br />
|
||||
<RoomAlias ref="alias" alias={this.state.alias} onChange={this.onAliasChanged}/> <br />
|
||||
<UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br />
|
||||
<Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br />
|
||||
<div>
|
||||
<label><input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/> Make this room private</label>
|
||||
</div>
|
||||
<div>
|
||||
<label><input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/> Share message history with new users</label>
|
||||
</div>
|
||||
<div className="mx_CreateRoom_encrypt">
|
||||
<label><input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged}/> Encrypt room</label>
|
||||
</div>
|
||||
<div>
|
||||
<CreateRoomButton onCreateRoom={this.onCreateRoom} /> <br />
|
||||
</div>
|
||||
{error_box}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,54 +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';
|
||||
|
||||
/*
|
||||
* Usage:
|
||||
* Modal.createDialog(ErrorDialog, {
|
||||
* title: "some text", (default: "Error")
|
||||
* description: "some more text",
|
||||
* button: "Button Text",
|
||||
* onClose: someFunction,
|
||||
* focus: true|false (default: true)
|
||||
* });
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
var ErrorDialogController = require("../../../../src/controllers/organisms/ErrorDialog");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'ErrorDialog',
|
||||
mixins: [ErrorDialogController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_ErrorDialog">
|
||||
<div className="mx_ErrorDialogTitle">
|
||||
{this.props.title}
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
{this.props.description}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.props.onFinished} autoFocus={this.props.focus}>
|
||||
{this.props.button}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -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 ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var RoomList = ComponentBroker.get('organisms/RoomList');
|
||||
var BottomLeftMenu = ComponentBroker.get('molecules/BottomLeftMenu');
|
||||
var IncomingCallBox = ComponentBroker.get('molecules/voip/IncomingCallBox');
|
||||
var RoomCreate = ComponentBroker.get('molecules/RoomCreate');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'LeftPanel',
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_LeftPanel">
|
||||
<img className="mx_LeftPanel_hideButton" src="img/hide.png" width="32" height="32" alt="<"/>
|
||||
<IncomingCallBox />
|
||||
<RoomList selectedRoom={this.props.selectedRoom} />
|
||||
<BottomLeftMenu />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,116 +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 MemberListController = require("../../../../src/controllers/organisms/MemberList");
|
||||
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
|
||||
var MemberTile = ComponentBroker.get("molecules/MemberTile");
|
||||
var EditableText = ComponentBroker.get("atoms/EditableText");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MemberList',
|
||||
mixins: [MemberListController],
|
||||
|
||||
getInitialState: function() {
|
||||
return { editing: false };
|
||||
},
|
||||
|
||||
// FIXME: combine this more nicely with the MemberInfo positioning stuff...
|
||||
onMemberListScroll: function(ev) {
|
||||
if (this.refs.memberListScroll) {
|
||||
var memberListScroll = this.refs.memberListScroll.getDOMNode();
|
||||
// offset the current MemberInfo bubble
|
||||
var memberInfo = document.getElementsByClassName("mx_MemberInfo")[0];
|
||||
if (memberInfo) {
|
||||
memberInfo.style.top = (memberInfo.parentElement.offsetTop - memberListScroll.scrollTop) + "px";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
makeMemberTiles: function() {
|
||||
var self = this;
|
||||
return Object.keys(self.state.memberDict).map(function(userId) {
|
||||
var m = self.state.memberDict[userId];
|
||||
return (
|
||||
<MemberTile key={userId} member={m} ref={userId} />
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
onPopulateInvite: function(inputText, shouldSubmit) {
|
||||
// reset back to placeholder
|
||||
this.refs.invite.setValue("Invite", false, true);
|
||||
this.setState({ editing: false });
|
||||
if (!shouldSubmit) {
|
||||
return; // enter key wasn't pressed
|
||||
}
|
||||
this.onInvite(inputText);
|
||||
},
|
||||
|
||||
onClickInvite: function(ev) {
|
||||
this.setState({ editing: true });
|
||||
this.refs.invite.onClickDiv();
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
},
|
||||
|
||||
inviteTile: function() {
|
||||
// if (this.state.inviting) {
|
||||
// return (
|
||||
// <div></div>
|
||||
// );
|
||||
// }
|
||||
|
||||
var classes = classNames({
|
||||
mx_MemberTile: true,
|
||||
mx_MemberTile_inviteEditing: this.state.editing,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={ classes } onClick={ this.onClickInvite } >
|
||||
<div className="mx_MemberTile_avatar"><img src="img/create-big.png" width="40" height="40" alt=""/></div>
|
||||
<div className="mx_MemberTile_name">
|
||||
<EditableText ref="invite" label="Invite" placeHolder="@user:domain.com" initialValue="" onValueChanged={this.onPopulateInvite}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_MemberList">
|
||||
<div className="mx_MemberList_chevron">
|
||||
<img src="img/chevron.png" width="24" height="13"/>
|
||||
</div>
|
||||
<div className="mx_MemberList_border" ref="memberListScroll" onScroll={ this.onMemberListScroll }>
|
||||
<h2>Members</h2>
|
||||
<div className="mx_MemberList_wrapper">
|
||||
{this.makeMemberTiles()}
|
||||
{this.inviteTile()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,94 +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 TextForEvent = require("../../../../src/TextForEvent");
|
||||
var extend = require("../../../../src/extend");
|
||||
var dis = require("../../../../src/dispatcher");
|
||||
|
||||
|
||||
var NotifierView = {
|
||||
notificationMessageForEvent: function(ev) {
|
||||
return TextForEvent.textForEvent(ev);
|
||||
},
|
||||
|
||||
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;
|
||||
// notificationMessageForEvent includes sender,
|
||||
// but we already have the sender here
|
||||
if (ev.getContent().body) msg = ev.getContent().body;
|
||||
} else if (ev.getType() == 'm.room.member') {
|
||||
// context is all in the message here, we don't need
|
||||
// to display sender info
|
||||
title = room.name;
|
||||
} else if (ev.sender) {
|
||||
title = ev.sender.name + " (" + room.name + ")";
|
||||
// notificationMessageForEvent includes sender,
|
||||
// but we've just out sender in the title
|
||||
if (ev.getContent().body) msg = ev.getContent().body;
|
||||
}
|
||||
|
||||
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,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';
|
||||
|
||||
/*
|
||||
* Usage:
|
||||
* Modal.createDialog(ErrorDialog, {
|
||||
* title: "some text", (default: "Error")
|
||||
* description: "some more text",
|
||||
* button: "Button Text",
|
||||
* onClose: someFunction,
|
||||
* focus: true|false (default: true)
|
||||
* });
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
var QuestionDialogController = require("../../../../src/controllers/organisms/QuestionDialog");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'QuestionDialog',
|
||||
mixins: [QuestionDialogController],
|
||||
|
||||
onOk: function() {
|
||||
this.props.onFinished(true);
|
||||
},
|
||||
|
||||
onCancel: function() {
|
||||
this.props.onFinished(false);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_QuestionDialog">
|
||||
<div className="mx_QuestionDialogTitle">
|
||||
{this.props.title}
|
||||
</div>
|
||||
<div className="mx_Dialog_content">
|
||||
{this.props.description}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
<button onClick={this.onOk} autoFocus={this.props.focus}>
|
||||
{this.props.button}
|
||||
</button>
|
||||
|
||||
<button onClick={this.onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,78 +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 MemberList = ComponentBroker.get('organisms/MemberList');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RightPanel',
|
||||
|
||||
Phase : {
|
||||
Blank: 'Blank',
|
||||
None: 'None',
|
||||
MemberList: 'MemberList',
|
||||
FileList: 'FileList',
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
phase : this.Phase.MemberList
|
||||
}
|
||||
},
|
||||
|
||||
onMemberListButtonClick: function() {
|
||||
if (this.state.phase == this.Phase.None) {
|
||||
this.setState({ phase: this.Phase.MemberList });
|
||||
}
|
||||
else {
|
||||
this.setState({ phase: this.Phase.None });
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var buttonGroup;
|
||||
var panel;
|
||||
if (this.props.roomId) {
|
||||
buttonGroup =
|
||||
<div className="mx_RightPanel_headerButtonGroup">
|
||||
<div className="mx_RightPanel_headerButton mx_RightPanel_filebutton">
|
||||
<img src="img/file.png" width="32" height="32" title="Files" alt="Files"/>
|
||||
</div>
|
||||
<div className="mx_RightPanel_headerButton" onClick={ this.onMemberListButtonClick }>
|
||||
<img src="img/members.png" width="32" height="32" title="Members" alt="Members"/>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
if (this.state.phase == this.Phase.MemberList) {
|
||||
panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RightPanel">
|
||||
<div className="mx_RightPanel_header">
|
||||
{ buttonGroup }
|
||||
</div>
|
||||
{ panel }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,147 +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 Modal = require("../../../../src/Modal");
|
||||
var ComponentBroker = require('../../../../src/ComponentBroker');
|
||||
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
|
||||
var RoomHeader = ComponentBroker.get('molecules/RoomHeader');
|
||||
var dis = require("../../../../src/dispatcher");
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomDirectory',
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
publicRooms: [],
|
||||
roomAlias: '',
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount: 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));
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to get public room list",
|
||||
description: err.message
|
||||
});
|
||||
}
|
||||
else {
|
||||
self.setState({
|
||||
publicRooms: data.chunk,
|
||||
loading: false,
|
||||
});
|
||||
self.forceUpdate();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
joinRoom: function(roomId) {
|
||||
var self = this;
|
||||
self.setState({ loading: true });
|
||||
// XXX: check that JS SDK suppresses duplicate attempts to join the same room
|
||||
MatrixClientPeg.get().joinRoom(roomId).done(function() {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: roomId
|
||||
});
|
||||
}, function(err) {
|
||||
console.error("Failed to join room: %s", JSON.stringify(err));
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to join room",
|
||||
description: err.message
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getRows: function(filter) {
|
||||
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.aliases[0].search(filter) >= 0 && a.num_joined_members > 0);
|
||||
}).sort(function(a,b) {
|
||||
return a.num_joined_members - b.num_joined_members;
|
||||
});
|
||||
var rows = [];
|
||||
var self = this;
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var name = rooms[i].name || rooms[i].aliases[0];
|
||||
// <img src={ MatrixClientPeg.get().getAvatarUrlForRoom(rooms[i].room_id, 40, 40, "crop") } width="40" height="40" alt=""/>
|
||||
rows.unshift(
|
||||
<tbody key={ rooms[i].room_id }>
|
||||
<tr onClick={self.joinRoom.bind(null, rooms[i].room_id)}>
|
||||
<td className="mx_RoomDirectory_name">{ name }</td>
|
||||
<td>{ rooms[i].aliases[0] }</td>
|
||||
<td>{ rooms[i].num_joined_members }</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="mx_RoomDirectory_topic" colSpan="3">{ rooms[i].topic }</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
);
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
|
||||
onKeyUp: function(ev) {
|
||||
this.forceUpdate();
|
||||
this.setState({ roomAlias : this.refs.roomAlias.getDOMNode().value })
|
||||
if (ev.key == "Enter") {
|
||||
this.joinRoom(this.refs.roomAlias.getDOMNode().value);
|
||||
}
|
||||
if (ev.key == "Down") {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.loading) {
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<RoomHeader simpleHeader="Public Rooms" />
|
||||
<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 }/>
|
||||
<div className="mx_RoomDirectory_tableWrapper">
|
||||
<table className="mx_RoomDirectory_table">
|
||||
<tr><th width="45%">Room</th><th width="45%">Alias</th><th width="10%">Members</th></tr>
|
||||
{ this.getRows(this.state.roomAlias) }
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,47 +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 RoomDropTarget = ComponentBroker.get('molecules/RoomDropTarget');
|
||||
|
||||
var RoomListController = require("../../../../src/controllers/organisms/RoomList");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomList',
|
||||
mixins: [RoomListController],
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_RoomList">
|
||||
<h2 className="mx_RoomList_favourites_label">Favourites</h2>
|
||||
<RoomDropTarget text="Drop here to favourite"/>
|
||||
|
||||
<h2 className="mx_RoomList_recents_label">Recents</h2>
|
||||
<div className="mx_RoomList_recents">
|
||||
{this.makeRoomTiles()}
|
||||
</div>
|
||||
|
||||
<h2 className="mx_RoomList_archive_label">Archive</h2>
|
||||
<RoomDropTarget text="Drop here to archive"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,190 +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 filesize = require('filesize');
|
||||
var q = require('q');
|
||||
|
||||
var MessageTile = ComponentBroker.get('molecules/MessageTile');
|
||||
var RoomHeader = ComponentBroker.get('molecules/RoomHeader');
|
||||
var MessageComposer = ComponentBroker.get('molecules/MessageComposer');
|
||||
var CallView = ComponentBroker.get("molecules/voip/CallView");
|
||||
var RoomSettings = ComponentBroker.get("molecules/RoomSettings");
|
||||
var RoomViewController = require("../../../../src/controllers/organisms/RoomView");
|
||||
|
||||
var Loader = require("react-loader");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomView',
|
||||
mixins: [RoomViewController],
|
||||
|
||||
onSettingsClick: function() {
|
||||
this.setState({editingRoomSettings: true});
|
||||
},
|
||||
|
||||
onSaveClick: function() {
|
||||
this.setState({
|
||||
editingRoomSettings: false,
|
||||
uploadingRoomSettings: true,
|
||||
});
|
||||
|
||||
var new_name = this.refs.header.getRoomName();
|
||||
var new_topic = this.refs.room_settings.getTopic();
|
||||
var new_join_rule = this.refs.room_settings.getJoinRules();
|
||||
var new_history_visibility = this.refs.room_settings.getHistoryVisibility();
|
||||
var new_power_levels = this.refs.room_settings.getPowerLevels();
|
||||
|
||||
this.uploadNewState(
|
||||
new_name,
|
||||
new_topic,
|
||||
new_join_rule,
|
||||
new_history_visibility,
|
||||
new_power_levels
|
||||
);
|
||||
},
|
||||
|
||||
getUnreadMessagesString: function() {
|
||||
if (!this.state.numUnreadMessages) {
|
||||
return "";
|
||||
}
|
||||
return this.state.numUnreadMessages + " unread messages";
|
||||
},
|
||||
|
||||
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">
|
||||
<RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/>
|
||||
<div className="mx_RoomView_invitePrompt">
|
||||
<div>{inviteEvent.user_id} has invited you to a room</div>
|
||||
<br/>
|
||||
<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
|
||||
});
|
||||
|
||||
var statusBar = (
|
||||
<div />
|
||||
);
|
||||
|
||||
if (this.state.upload) {
|
||||
var innerProgressStyle = {
|
||||
width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%'
|
||||
};
|
||||
statusBar = (
|
||||
<div className="mx_RoomView_uploadBar">
|
||||
<span className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</span>
|
||||
<span className="mx_RoomView_uploadBytes">
|
||||
{filesize(this.state.upload.uploadedBytes)} / {filesize(this.state.upload.totalBytes)}
|
||||
</span>
|
||||
<div className="mx_RoomView_uploadProgressOuter">
|
||||
<div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
var typingString = this.getWhoIsTypingString();
|
||||
var unreadMsgs = this.getUnreadMessagesString();
|
||||
// unread count trumps who is typing since the unread count is only
|
||||
// set when you've scrolled up
|
||||
if (unreadMsgs) {
|
||||
statusBar = (
|
||||
<div className="mx_RoomView_typingBar">
|
||||
<img src="img/typing.png" width="40" height="40" alt=""/>
|
||||
{unreadMsgs}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else if (typingString) {
|
||||
statusBar = (
|
||||
<div className="mx_RoomView_typingBar">
|
||||
<img src="img/typing.png" width="40" height="40" alt=""/>
|
||||
{typingString}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var roomEdit = null;
|
||||
|
||||
if (this.state.editingRoomSettings) {
|
||||
roomEdit = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />;
|
||||
}
|
||||
|
||||
if (this.state.uploadingRoomSettings) {
|
||||
roomEdit = <Loader/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomView">
|
||||
<RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings}
|
||||
onSettingsClick={this.onSettingsClick}/>
|
||||
<div className="mx_RoomView_auxPanel">
|
||||
<CallView room={this.state.room}/>
|
||||
{ roomEdit }
|
||||
</div>
|
||||
<div ref="messageWrapper" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }>
|
||||
<div className="mx_RoomView_messageListWrapper">
|
||||
<div className="mx_RoomView_MessageList" aria-live="polite">
|
||||
<div className={scrollheader_classes}>
|
||||
</div>
|
||||
{this.getEventTiles()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_RoomView_statusArea">
|
||||
<div className="mx_RoomView_statusAreaBox">
|
||||
{statusBar}
|
||||
</div>
|
||||
</div>
|
||||
<MessageComposer room={this.state.room} uploadFile={this.uploadFile} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,113 +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 MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
|
||||
var UserSettingsController = require("../../../../src/controllers/organisms/UserSettings");
|
||||
|
||||
var EditableText = ComponentBroker.get('atoms/EditableText');
|
||||
var EnableNotificationsButton = ComponentBroker.get('atoms/EnableNotificationsButton');
|
||||
var ChangeAvatar = ComponentBroker.get('molecules/ChangeAvatar');
|
||||
var ChangePassword = ComponentBroker.get('molecules/ChangePassword');
|
||||
var LogoutPrompt = ComponentBroker.get('organisms/LogoutPrompt');
|
||||
var Loader = require("react-loader");
|
||||
|
||||
var Modal = require("../../../../src/Modal");
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'UserSettings',
|
||||
mixins: [UserSettingsController],
|
||||
|
||||
editAvatar: function() {
|
||||
var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl);
|
||||
Modal.createDialog(ChangeAvatar, {initialAvatarUrl: url});
|
||||
},
|
||||
|
||||
addEmail: function() {
|
||||
|
||||
},
|
||||
|
||||
editDisplayName: function() {
|
||||
this.refs.displayname.edit();
|
||||
},
|
||||
|
||||
changePassword: function() {
|
||||
Modal.createDialog(ChangePassword);
|
||||
},
|
||||
|
||||
onLogoutClicked: function(ev) {
|
||||
this.logoutModal = Modal.createDialog(LogoutPrompt, {onCancel: this.onLogoutPromptCancel});
|
||||
},
|
||||
|
||||
onLogoutPromptCancel: function() {
|
||||
this.logoutModal.closeDialog();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
switch (this.state.phase) {
|
||||
case this.Phases.Loading:
|
||||
return <Loader />
|
||||
case this.Phases.Display:
|
||||
return (
|
||||
<div className="mx_UserSettings">
|
||||
<div className="mx_UserSettings_User">
|
||||
<h1>User Settings</h1>
|
||||
<hr/>
|
||||
<div className="mx_UserSettings_User_Inner">
|
||||
<div className="mx_UserSettings_Avatar">
|
||||
<div className="mx_UserSettings_Avatar_Text">Profile Photo</div>
|
||||
<div className="mx_UserSettings_Avatar_Edit" onClick={this.editAvatar}>Edit</div>
|
||||
</div>
|
||||
|
||||
<div className="mx_UserSettings_DisplayName">
|
||||
<EditableText ref="displayname" initialValue={this.state.displayName} label="Click to set display name." onValueChanged={this.changeDisplayname}/>
|
||||
<div className="mx_UserSettings_DisplayName_Edit" onClick={this.editDisplayName}>Edit</div>
|
||||
</div>
|
||||
|
||||
<div className="mx_UserSettings_3pids">
|
||||
{this.state.threepids.map(function(val) {
|
||||
return <div>{val.address}</div>;
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mx_UserSettings_Add3pid" onClick={this.addEmail}>Add email</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx_UserSettings_Global">
|
||||
<h1>Global Settings</h1>
|
||||
<hr/>
|
||||
<div className="mx_UserSettings_Global_Inner">
|
||||
<div className="mx_UserSettings_ChangePassword" onClick={this.changePassword}>
|
||||
Change Password
|
||||
</div>
|
||||
<div className="mx_UserSettings_ClientVersion">
|
||||
Version {this.state.clientVersion}
|
||||
</div>
|
||||
<div className="mx_UserSettings_EnableNotifications">
|
||||
<EnableNotificationsButton />
|
||||
</div>
|
||||
<div className="mx_UserSettings_Logout">
|
||||
<button onClick={this.onLogoutClicked}>Sign Out</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,121 +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 LeftPanel = ComponentBroker.get('organisms/LeftPanel');
|
||||
var RoomView = ComponentBroker.get('organisms/RoomView');
|
||||
var RightPanel = ComponentBroker.get('organisms/RightPanel');
|
||||
var Login = ComponentBroker.get('templates/Login');
|
||||
var UserSettings = ComponentBroker.get('organisms/UserSettings');
|
||||
var Register = ComponentBroker.get('templates/Register');
|
||||
var CreateRoom = ComponentBroker.get('organisms/CreateRoom');
|
||||
var RoomDirectory = ComponentBroker.get('organisms/RoomDirectory');
|
||||
var MatrixToolbar = ComponentBroker.get('molecules/MatrixToolbar');
|
||||
var Notifier = ComponentBroker.get('organisms/Notifier');
|
||||
|
||||
var MatrixChatController = require("../../../../src/controllers/pages/MatrixChat");
|
||||
|
||||
// should be atomised
|
||||
var Loader = require("react-loader");
|
||||
var classNames = require("classnames");
|
||||
|
||||
var dis = require("../../../../src/dispatcher");
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'MatrixChat',
|
||||
mixins: [MatrixChatController],
|
||||
|
||||
onRoomCreated: function(room_id) {
|
||||
dis.dispatch({
|
||||
action: "view_room",
|
||||
room_id: room_id,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.logged_in && this.state.ready) {
|
||||
|
||||
var page_element;
|
||||
var right_panel = "";
|
||||
|
||||
switch (this.state.page_type) {
|
||||
case this.PageTypes.RoomView:
|
||||
page_element = <RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} />
|
||||
right_panel = <RightPanel roomId={this.state.currentRoom} />
|
||||
break;
|
||||
case this.PageTypes.UserSettings:
|
||||
page_element = <UserSettings />
|
||||
right_panel = <RightPanel/>
|
||||
break;
|
||||
case this.PageTypes.CreateRoom:
|
||||
page_element = <CreateRoom onRoomCreated={this.onRoomCreated}/>
|
||||
right_panel = <RightPanel/>
|
||||
break;
|
||||
case this.PageTypes.RoomDirectory:
|
||||
page_element = <RoomDirectory />
|
||||
right_panel = <RightPanel/>
|
||||
break;
|
||||
}
|
||||
|
||||
if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled()) {
|
||||
return (
|
||||
<div className="mx_MatrixChat_wrapper">
|
||||
<MatrixToolbar />
|
||||
<div className="mx_MatrixChat mx_MatrixChat_toolbarShowing">
|
||||
<LeftPanel selectedRoom={this.state.currentRoom} />
|
||||
<div className="mx_MatrixChat_middlePanel">
|
||||
{page_element}
|
||||
</div>
|
||||
{right_panel}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="mx_MatrixChat">
|
||||
<LeftPanel selectedRoom={this.state.currentRoom} />
|
||||
<div className="mx_MatrixChat_middlePanel">
|
||||
{page_element}
|
||||
</div>
|
||||
{right_panel}
|
||||
</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,174 +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 MatrixClientPeg = require("../../../../src/MatrixClientPeg");
|
||||
|
||||
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({
|
||||
DEFAULT_HS_URL: 'https://matrix.org',
|
||||
DEFAULT_IS_URL: 'https://matrix.org',
|
||||
|
||||
displayName: 'Login',
|
||||
mixins: [LoginController],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
serverConfigVisible: false
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.onHSChosen();
|
||||
this.customHsUrl = this.DEFAULT_HS_URL;
|
||||
this.customIsUrl = this.DEFAULT_IS_URL;
|
||||
},
|
||||
|
||||
getHsUrl: function() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customHsUrl;
|
||||
} else {
|
||||
return this.DEFAULT_HS_URL;
|
||||
}
|
||||
},
|
||||
|
||||
getIsUrl: function() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customIsUrl;
|
||||
} else {
|
||||
return this.DEFAULT_IS_URL;
|
||||
}
|
||||
},
|
||||
|
||||
onServerConfigVisibleChange: function(ev) {
|
||||
this.setState({
|
||||
serverConfigVisible: ev.target.checked
|
||||
}, this.onHsUrlChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
},
|
||||
|
||||
onHsUrlChanged: function() {
|
||||
this.customHsUrl = this.refs.serverConfig.getHsUrl();
|
||||
this.customIsUrl = this.refs.serverConfig.getIsUrl();
|
||||
MatrixClientPeg.replaceUsingUrls(
|
||||
this.getHsUrl(),
|
||||
this.getIsUrl()
|
||||
);
|
||||
this.setState({
|
||||
hs_url: this.getHsUrl(),
|
||||
is_url: this.getIsUrl()
|
||||
});
|
||||
// XXX: HSes do not have to offer password auth, so we
|
||||
// need to update and maybe show a different component
|
||||
// when a new HS is entered.
|
||||
/*if (this.updateHsTimeout) {
|
||||
clearTimeout(this.updateHsTimeout);
|
||||
}
|
||||
var self = this;
|
||||
this.updateHsTimeout = setTimeout(function() {
|
||||
self.onHSChosen();
|
||||
}, 500);*/
|
||||
},
|
||||
|
||||
componentForStep: function(step) {
|
||||
switch (step) {
|
||||
case 'choose_hs':
|
||||
var serverConfigStyle = {};
|
||||
serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none';
|
||||
return (
|
||||
<div>
|
||||
<input className="mx_Login_checkbox" id="advanced" type="checkbox" value={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} />
|
||||
<label className="mx_Login_label" htmlFor="advanced">Use custom server options (advanced)</label>
|
||||
<div style={serverConfigStyle}>
|
||||
<ServerConfig ref="serverConfig"
|
||||
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl}
|
||||
onHsUrlChanged={this.onHsUrlChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
// XXX: clearly these should be separate organisms
|
||||
case 'stage_m.login.password':
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onUserPassEntered}>
|
||||
<input className="mx_Login_field" ref="user" type="text" value={this.state.username} onChange={this.onUsernameChanged} placeholder="Email or user name" /><br />
|
||||
<input className="mx_Login_field" ref="pass" type="password" value={this.state.password} onChange={this.onPasswordChanged} placeholder="Password" /><br />
|
||||
{this.componentForStep('choose_hs')}
|
||||
<input className="mx_Login_submit" type="submit" value="Log in" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
onUsernameChanged: function(ev) {
|
||||
this.setState({username: ev.target.value});
|
||||
},
|
||||
|
||||
onPasswordChanged: function(ev) {
|
||||
this.setState({password: ev.target.value});
|
||||
},
|
||||
|
||||
loginContent: function() {
|
||||
if (this.state.busy) {
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<h2>Sign in</h2>
|
||||
{this.componentForStep(this.state.step)}
|
||||
<div className="mx_Login_error">{this.state.errorText}</div>
|
||||
<a className="mx_Login_create" onClick={this.showRegister} href="#">Create a new account</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_Login">
|
||||
<div className="mx_Login_box">
|
||||
<div className="mx_Login_logo">
|
||||
<img src="img/logo.png" width="249" height="78" alt="vector"/>
|
||||
</div>
|
||||
{this.loginContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,192 +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({
|
||||
DEFAULT_HS_URL: 'https://matrix.org',
|
||||
DEFAULT_IS_URL: 'https://matrix.org',
|
||||
|
||||
displayName: 'Register',
|
||||
mixins: [RegisterController],
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
serverConfigVisible: false
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.customHsUrl = this.DEFAULT_HS_URL;
|
||||
this.customIsUrl = this.DEFAULT_IS_URL;
|
||||
},
|
||||
|
||||
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() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customHsUrl;
|
||||
} else {
|
||||
return this.DEFAULT_HS_URL;
|
||||
}
|
||||
},
|
||||
|
||||
getIsUrl: function() {
|
||||
if (this.state.serverConfigVisible) {
|
||||
return this.customIsUrl;
|
||||
} else {
|
||||
return this.DEFAULT_IS_URL;
|
||||
}
|
||||
},
|
||||
|
||||
onServerConfigVisibleChange: function(ev) {
|
||||
this.setState({
|
||||
serverConfigVisible: ev.target.checked
|
||||
});
|
||||
},
|
||||
|
||||
getUserIdSuffix: function() {
|
||||
return '';
|
||||
var actualHsUrl = document.createElement('a');
|
||||
actualHsUrl.href = this.getHsUrl();
|
||||
var defaultHsUrl = document.createElement('a');
|
||||
defaultHsUrl.href = this.DEFAULT_HS_URL;
|
||||
if (actualHsUrl.host == defaultHsUrl.host) {
|
||||
return ':matrix.org';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
onServerUrlChanged: function(newUrl) {
|
||||
this.customHsUrl = this.refs.serverConfig.getHsUrl();
|
||||
this.customIsUrl = this.refs.serverConfig.getIsUrl();
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
componentForStep: function(step) {
|
||||
switch (step) {
|
||||
case 'initial':
|
||||
var serverConfigStyle = {};
|
||||
serverConfigStyle.display = this.state.serverConfigVisible ? 'block' : 'none';
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onInitialStageSubmit}>
|
||||
<input className="mx_Login_field" type="text" ref="email" placeholder="Email address" defaultValue={this.savedParams.email} /><br />
|
||||
<input className="mx_Login_field" type="text" ref="username" placeholder="User name" defaultValue={this.savedParams.username} />{this.getUserIdSuffix()}<br />
|
||||
<input className="mx_Login_field" type="password" ref="password" placeholder="Password" defaultValue={this.savedParams.password} /><br />
|
||||
<input className="mx_Login_field" type="password" ref="confirmPassword" placeholder="Confirm password" defaultValue={this.savedParams.confirmPassword} /><br />
|
||||
|
||||
<input className="mx_Login_checkbox" id="advanced" type="checkbox" value={this.state.serverConfigVisible} onChange={this.onServerConfigVisibleChange} />
|
||||
<label htmlFor="advanced">Use custom server options (advanced)</label>
|
||||
<div style={serverConfigStyle}>
|
||||
<ServerConfig ref="serverConfig"
|
||||
defaultHsUrl={this.customHsUrl} defaultIsUrl={this.customIsUrl}
|
||||
onHsUrlChanged={this.onServerUrlChanged} onIsUrlChanged={this.onServerUrlChanged} />
|
||||
</div>
|
||||
<br />
|
||||
<input className="mx_Login_submit" type="submit" value="Register" />
|
||||
</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 are not a robot
|
||||
<div id="mx_recaptcha"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
registerContent: function() {
|
||||
if (this.state.busy) {
|
||||
return (
|
||||
<Loader />
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<h2>Create an account</h2>
|
||||
{this.componentForStep(this.state.step)}
|
||||
<div className="mx_Login_error">{this.state.errorText}</div>
|
||||
<a className="mx_Login_create" onClick={this.showLogin} href="#">I already have an 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_Login">
|
||||
<div className="mx_Login_box">
|
||||
<div className="mx_Login_logo">
|
||||
<img src="img/logo.png" width="249" height="78" alt="vector"/>
|
||||
</div>
|
||||
{this.registerContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,276 +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';
|
||||
|
||||
/*
|
||||
* Manages a list of all the currently active calls.
|
||||
*
|
||||
* This handler dispatches when voip calls are added/updated/removed from this list:
|
||||
* {
|
||||
* action: 'call_state'
|
||||
* room_id: <room ID of the call>
|
||||
* }
|
||||
*
|
||||
* To know the state of the call, this handler exposes a getter to
|
||||
* obtain the call for a room:
|
||||
* var call = CallHandler.getCall(roomId)
|
||||
* var state = call.call_state; // ringing|ringback|connected|ended|busy|stop_ringback|stop_ringing
|
||||
*
|
||||
* This handler listens for and handles the following actions:
|
||||
* {
|
||||
* action: 'place_call',
|
||||
* type: 'voice|video',
|
||||
* room_id: <room that the place call button was pressed in>
|
||||
* }
|
||||
*
|
||||
* {
|
||||
* action: 'incoming_call'
|
||||
* call: MatrixCall
|
||||
* }
|
||||
*
|
||||
* {
|
||||
* action: 'hangup'
|
||||
* room_id: <room that the hangup button was pressed in>
|
||||
* }
|
||||
*
|
||||
* {
|
||||
* action: 'answer'
|
||||
* room_id: <room that the answer button was pressed in>
|
||||
* }
|
||||
*/
|
||||
|
||||
var MatrixClientPeg = require("./MatrixClientPeg");
|
||||
var Modal = require("./Modal");
|
||||
var ComponentBroker = require('./ComponentBroker');
|
||||
var ErrorDialog = ComponentBroker.get("organisms/ErrorDialog");
|
||||
var Matrix = require("matrix-js-sdk");
|
||||
var dis = require("./dispatcher");
|
||||
var q = require("q");
|
||||
|
||||
var calls = {
|
||||
//room_id: MatrixCall
|
||||
};
|
||||
|
||||
function play(audioId) {
|
||||
// TODO: Attach an invisible element for this instead
|
||||
// which listens?
|
||||
var audio = document.getElementById(audioId);
|
||||
if (audio) {
|
||||
audio.load();
|
||||
audio.play();
|
||||
}
|
||||
}
|
||||
|
||||
function pause(audioId) {
|
||||
// TODO: Attach an invisible element for this instead
|
||||
// which listens?
|
||||
var audio = document.getElementById(audioId);
|
||||
if (audio) {
|
||||
audio.pause();
|
||||
}
|
||||
}
|
||||
|
||||
function _setCallListeners(call) {
|
||||
call.on("error", function(err) {
|
||||
console.error("Call error: %s", err);
|
||||
console.error(err.stack);
|
||||
call.hangup();
|
||||
_setCallState(undefined, call.roomId, "ended");
|
||||
});
|
||||
call.on("hangup", function() {
|
||||
_setCallState(undefined, call.roomId, "ended");
|
||||
});
|
||||
// map web rtc states to dummy UI state
|
||||
// ringing|ringback|connected|ended|busy|stop_ringback|stop_ringing
|
||||
call.on("state", function(newState, oldState) {
|
||||
if (newState === "ringing") {
|
||||
_setCallState(call, call.roomId, "ringing");
|
||||
pause("ringbackAudio");
|
||||
}
|
||||
else if (newState === "invite_sent") {
|
||||
_setCallState(call, call.roomId, "ringback");
|
||||
play("ringbackAudio");
|
||||
}
|
||||
else if (newState === "ended" && oldState === "connected") {
|
||||
_setCallState(call, call.roomId, "ended");
|
||||
pause("ringbackAudio");
|
||||
play("callendAudio");
|
||||
}
|
||||
else if (newState === "ended" && oldState === "invite_sent" &&
|
||||
(call.hangupParty === "remote" ||
|
||||
(call.hangupParty === "local" && call.hangupReason === "invite_timeout")
|
||||
)) {
|
||||
_setCallState(call, call.roomId, "busy");
|
||||
pause("ringbackAudio");
|
||||
play("busyAudio");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Call Timeout",
|
||||
description: "The remote side failed to pick up."
|
||||
});
|
||||
}
|
||||
else if (oldState === "invite_sent") {
|
||||
_setCallState(call, call.roomId, "stop_ringback");
|
||||
pause("ringbackAudio");
|
||||
}
|
||||
else if (oldState === "ringing") {
|
||||
_setCallState(call, call.roomId, "stop_ringing");
|
||||
pause("ringbackAudio");
|
||||
}
|
||||
else if (newState === "connected") {
|
||||
_setCallState(call, call.roomId, "connected");
|
||||
pause("ringbackAudio");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _setCallState(call, roomId, status) {
|
||||
console.log(
|
||||
"Call state in %s changed to %s (%s)", roomId, status, (call ? call.state : "-")
|
||||
);
|
||||
calls[roomId] = call;
|
||||
if (call) {
|
||||
call.call_state = status;
|
||||
}
|
||||
dis.dispatch({
|
||||
action: 'call_state',
|
||||
room_id: roomId
|
||||
});
|
||||
}
|
||||
|
||||
function findOrMakeVertoRoom() {
|
||||
var VERTO_USERID = '@verto_1234:matrix.org';
|
||||
var defer = q.defer();
|
||||
|
||||
var allRooms = MatrixClientPeg.get().getRooms();
|
||||
for (var i = 0; i < Object.keys(allRooms).length; ++i) {
|
||||
var r = allRooms[i];
|
||||
var membs = r.getJoinedMembers();
|
||||
if (membs.length == 2 && (membs[0].userId == VERTO_USERID || membs[0].userId == VERTO_USERID)) {
|
||||
defer.resolve(r);
|
||||
return defer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
MatrixClientPeg.get().createRoom({
|
||||
invite: [ VERTO_USERID ]
|
||||
}).done(function(result) {
|
||||
defer.resolve(MatrixClientPeg.get().getRoom(result.room_id));
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
dis.register(function(payload) {
|
||||
switch (payload.action) {
|
||||
case 'place_call':
|
||||
if (calls[payload.room_id]) {
|
||||
return; // don't allow >1 call to be placed.
|
||||
}
|
||||
var room = MatrixClientPeg.get().getRoom(payload.room_id);
|
||||
if (!room) {
|
||||
console.error("Room %s does not exist.", payload.room_id);
|
||||
return;
|
||||
}
|
||||
var members = room.getJoinedMembers();
|
||||
if (members.length > 2) {
|
||||
var vertoRoom = findOrMakeVertoRoom().done(function(r) {
|
||||
console.log("Place %s conference call in %s", payload.type, payload.room_id);
|
||||
var call = Matrix.createNewMatrixCall(
|
||||
MatrixClientPeg.get(), r.roomId
|
||||
);
|
||||
_setCallListeners(call);
|
||||
_setCallState(call, call.roomId, "ringback");
|
||||
if (payload.type === 'voice') {
|
||||
call.placeVoiceCall();
|
||||
}
|
||||
else if (payload.type === 'video') {
|
||||
call.placeVideoCall(
|
||||
payload.remote_element,
|
||||
payload.local_element
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.error("Unknown call type: %s", payload.type);
|
||||
}
|
||||
});
|
||||
return;
|
||||
} else if (members.length !== 2) {
|
||||
var text = members.length === 1 ? "yourself." : "more than 2 people.";
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
description: "You cannot place a call with " + text
|
||||
});
|
||||
console.error(
|
||||
"Fail: There are %s joined members in this room, not 2.",
|
||||
room.getJoinedMembers().length
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.log("Place %s call in %s", payload.type, payload.room_id);
|
||||
var call = Matrix.createNewMatrixCall(
|
||||
MatrixClientPeg.get(), payload.room_id
|
||||
);
|
||||
_setCallListeners(call);
|
||||
_setCallState(call, call.roomId, "ringback");
|
||||
if (payload.type === 'voice') {
|
||||
call.placeVoiceCall();
|
||||
}
|
||||
else if (payload.type === 'video') {
|
||||
call.placeVideoCall(
|
||||
payload.remote_element,
|
||||
payload.local_element
|
||||
);
|
||||
}
|
||||
else {
|
||||
console.error("Unknown call type: %s", payload.type);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'incoming_call':
|
||||
if (calls[payload.call.roomId]) {
|
||||
payload.call.hangup("busy");
|
||||
return; // don't allow >1 call to be received, hangup newer one.
|
||||
}
|
||||
var call = payload.call;
|
||||
_setCallListeners(call);
|
||||
_setCallState(call, call.roomId, "ringing");
|
||||
break;
|
||||
case 'hangup':
|
||||
if (!calls[payload.room_id]) {
|
||||
return; // no call to hangup
|
||||
}
|
||||
calls[payload.room_id].hangup();
|
||||
_setCallState(null, payload.room_id, "ended");
|
||||
break;
|
||||
case 'answer':
|
||||
if (!calls[payload.room_id]) {
|
||||
return; // no call to answer
|
||||
}
|
||||
calls[payload.room_id].answer();
|
||||
_setCallState(calls[payload.room_id], payload.room_id, "connected");
|
||||
dis.dispatch({
|
||||
action: "view_room",
|
||||
room_id: payload.room_id
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
getCall: function(roomId) {
|
||||
return calls[roomId] || null;
|
||||
}
|
||||
};
|
||||
@@ -1,117 +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/RoomAlias');
|
||||
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');
|
||||
require('../skins/base/views/organisms/UserSettings');
|
||||
require('../skins/base/views/molecules/ChangeAvatar');
|
||||
require('../skins/base/views/molecules/ChangePassword');
|
||||
require('../skins/base/views/molecules/RoomSettings');
|
||||
// new for vector
|
||||
require('../skins/base/views/organisms/LeftPanel');
|
||||
require('../skins/base/views/organisms/RightPanel');
|
||||
require('../skins/base/views/organisms/LogoutPrompt');
|
||||
require('../skins/base/views/organisms/RoomDirectory');
|
||||
require('../skins/base/views/molecules/RoomCreate');
|
||||
require('../skins/base/views/molecules/RoomDropTarget');
|
||||
require('../skins/base/views/molecules/BottomLeftMenu');
|
||||
require('../skins/base/views/molecules/DateSeparator');
|
||||
require('../skins/base/views/atoms/voip/VideoFeed');
|
||||
require('../skins/base/views/atoms/ImageView');
|
||||
require('../skins/base/views/molecules/voip/VideoView');
|
||||
require('../skins/base/views/molecules/voip/CallView');
|
||||
require('../skins/base/views/molecules/voip/IncomingCallBox');
|
||||
require('../skins/base/views/molecules/voip/MCallInviteTile');
|
||||
require('../skins/base/views/molecules/voip/MCallAnswerTile');
|
||||
require('../skins/base/views/molecules/voip/MCallHangupTile');
|
||||
require('../skins/base/views/molecules/EventAsTextTile');
|
||||
require('../skins/base/views/molecules/MemberInfo');
|
||||
require('../skins/base/views/organisms/ErrorDialog');
|
||||
require('../skins/base/views/organisms/QuestionDialog');
|
||||
}
|
||||
@@ -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,101 +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 deviceId() {
|
||||
var id = Math.floor(Math.random()*16777215).toString(16);
|
||||
id = "W" + "000000".substring(id.length) + id;
|
||||
if (localStorage) {
|
||||
id = localStorage.getItem("mx_device_id") || id;
|
||||
localStorage.setItem("mx_device_id", id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
function createClient(hs_url, is_url, user_id, access_token) {
|
||||
var opts = {
|
||||
baseUrl: hs_url,
|
||||
idBaseUrl: is_url,
|
||||
accessToken: access_token,
|
||||
userId: user_id
|
||||
};
|
||||
|
||||
if (localStorage) {
|
||||
opts.sessionStore = new Matrix.WebStorageSessionStore(localStorage);
|
||||
opts.deviceId = deviceId();
|
||||
}
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
unset: function() {
|
||||
matrixClient = null;
|
||||
},
|
||||
|
||||
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) {
|
||||
if (localStorage) {
|
||||
try {
|
||||
localStorage.clear();
|
||||
} catch (e) {
|
||||
console.warn("Error using local storage");
|
||||
}
|
||||
}
|
||||
createClient(hs_url, is_url, user_id, access_token);
|
||||
if (localStorage) {
|
||||
try {
|
||||
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!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
62
src/Modal.js
@@ -1,62 +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 q = require('q');
|
||||
|
||||
module.exports = {
|
||||
DialogContainerId: "mx_Dialog_Container",
|
||||
|
||||
getOrCreateContainer: function() {
|
||||
var container = document.getElementById(this.DialogContainerId);
|
||||
|
||||
if (!container) {
|
||||
container = document.createElement("div");
|
||||
container.id = this.DialogContainerId;
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
createDialog: function (Element, props) {
|
||||
var self = this;
|
||||
|
||||
var closeDialog = function() {
|
||||
React.unmountComponentAtNode(self.getOrCreateContainer());
|
||||
|
||||
if (props && props.onFinished) props.onFinished.apply(null, arguments);
|
||||
};
|
||||
|
||||
// FIXME: If a dialog uses getDefaultProps it clobbers the onFinished
|
||||
// property set here so you can't close the dialog from a button click!
|
||||
var dialog = (
|
||||
<div className="mx_Dialog_Wrapper">
|
||||
<div className="mx_Dialog">
|
||||
<Element {...props} onFinished={closeDialog}/>
|
||||
</div>
|
||||
<div className="mx_Dialog_Background" onClick={closeDialog}></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
React.render(dialog, this.getOrCreateContainer());
|
||||
|
||||
return {close: closeDialog};
|
||||
},
|
||||
};
|
||||
107
src/Presence.js
@@ -1,107 +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");
|
||||
|
||||
// Time in ms after that a user is considered as unavailable/away
|
||||
var UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
|
||||
var PRESENCE_STATES = ["online", "offline", "unavailable"];
|
||||
|
||||
// The current presence state
|
||||
var state, timer;
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Start listening the user activity to evaluate his presence state.
|
||||
* Any state change will be sent to the Home Server.
|
||||
*/
|
||||
start: function() {
|
||||
var self = this;
|
||||
this.running = true;
|
||||
if (undefined === state) {
|
||||
// The user is online if they move the mouse or press a key
|
||||
document.onmousemove = function() { self._resetTimer(); };
|
||||
document.onkeypress = function() { self._resetTimer(); };
|
||||
this._resetTimer();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop tracking user activity
|
||||
*/
|
||||
stop: function() {
|
||||
this.running = false;
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = undefined;
|
||||
}
|
||||
state = undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the current presence state.
|
||||
* @returns {string} the presence state (see PRESENCE enum)
|
||||
*/
|
||||
getState: function() {
|
||||
return state;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the presence state.
|
||||
* If the state has changed, the Home Server will be notified.
|
||||
* @param {string} newState the new presence state (see PRESENCE enum)
|
||||
*/
|
||||
setState: function(newState) {
|
||||
if (newState === state) {
|
||||
return;
|
||||
}
|
||||
if (PRESENCE_STATES.indexOf(newState) === -1) {
|
||||
throw new Error("Bad presence state: " + newState);
|
||||
}
|
||||
if (!this.running) {
|
||||
return;
|
||||
}
|
||||
state = newState;
|
||||
MatrixClientPeg.get().setPresence(state).done(function() {
|
||||
console.log("Presence: %s", newState);
|
||||
}, function(err) {
|
||||
console.error("Failed to set presence: %s", err);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback called when the user made no action on the page for UNAVAILABLE_TIME ms.
|
||||
* @private
|
||||
*/
|
||||
_onUnavailableTimerFire: function() {
|
||||
this.setState("unavailable");
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback called when the user made an action on the page
|
||||
* @private
|
||||
*/
|
||||
_resetTimer: function() {
|
||||
var self = this;
|
||||
this.setState("online");
|
||||
// Re-arm the timer
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function() {
|
||||
self._onUnavailableTimerFire();
|
||||
}, UNAVAILABLE_TIME_MS);
|
||||
}
|
||||
};
|
||||
@@ -1,253 +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");
|
||||
var encryption = require("./encryption");
|
||||
|
||||
var reject = function(msg) {
|
||||
return {
|
||||
error: msg
|
||||
};
|
||||
};
|
||||
|
||||
var success = function(promise) {
|
||||
return {
|
||||
promise: promise
|
||||
};
|
||||
};
|
||||
|
||||
var commands = {
|
||||
// Change your nickname
|
||||
nick: function(room_id, args) {
|
||||
if (args) {
|
||||
return success(
|
||||
MatrixClientPeg.get().setDisplayName(args)
|
||||
);
|
||||
}
|
||||
return reject("Usage: /nick <display_name>");
|
||||
},
|
||||
|
||||
encrypt: function(room_id, args) {
|
||||
if (args == "on") {
|
||||
var client = MatrixClientPeg.get();
|
||||
var members = client.getRoom(room_id).currentState.members;
|
||||
var user_ids = Object.keys(members);
|
||||
return success(
|
||||
encryption.enableEncryption(client, room_id, user_ids)
|
||||
);
|
||||
}
|
||||
if (args == "off") {
|
||||
var client = MatrixClientPeg.get();
|
||||
return success(
|
||||
encryption.disableEncryption(client, room_id)
|
||||
);
|
||||
|
||||
}
|
||||
return reject("Usage: encrypt <on/off>");
|
||||
},
|
||||
|
||||
// Change the room topic
|
||||
topic: function(room_id, args) {
|
||||
if (args) {
|
||||
return success(
|
||||
MatrixClientPeg.get().setRoomTopic(room_id, args)
|
||||
);
|
||||
}
|
||||
return reject("Usage: /topic <topic>");
|
||||
},
|
||||
|
||||
// Invite a user
|
||||
invite: function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
return success(
|
||||
MatrixClientPeg.get().invite(room_id, matches[1])
|
||||
);
|
||||
}
|
||||
}
|
||||
return reject("Usage: /invite <userId>");
|
||||
},
|
||||
|
||||
// Join a room
|
||||
join: function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
var room_alias = matches[1];
|
||||
// Try to find a room with this alias
|
||||
var rooms = MatrixClientPeg.get().getRooms();
|
||||
var roomId;
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var aliasEvents = rooms[i].currentState.getStateEvents(
|
||||
"m.room.aliases"
|
||||
);
|
||||
for (var j = 0; j < aliasEvents.length; j++) {
|
||||
var aliases = aliasEvents[j].getContent().aliases || [];
|
||||
for (var k = 0; k < aliases.length; k++) {
|
||||
if (aliases[k] === room_alias) {
|
||||
roomId = rooms[i].roomId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (roomId) { break; }
|
||||
}
|
||||
if (roomId) { break; }
|
||||
}
|
||||
if (roomId) { // we've already joined this room, view it.
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: roomId
|
||||
});
|
||||
return success();
|
||||
}
|
||||
else {
|
||||
// attempt to join this alias.
|
||||
return success(
|
||||
MatrixClientPeg.get().joinRoom(room_alias).then(
|
||||
function(room) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.roomId
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return reject("Usage: /join <room_alias>");
|
||||
},
|
||||
|
||||
// Kick a user from the room with an optional reason
|
||||
kick: function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+?)( +(.*))?$/);
|
||||
if (matches) {
|
||||
return success(
|
||||
MatrixClientPeg.get().kick(room_id, matches[1], matches[3])
|
||||
);
|
||||
}
|
||||
}
|
||||
return reject("Usage: /kick <userId> [<reason>]");
|
||||
},
|
||||
|
||||
// Ban a user from the room with an optional reason
|
||||
ban: function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+?)( +(.*))?$/);
|
||||
if (matches) {
|
||||
return success(
|
||||
MatrixClientPeg.get().ban(room_id, matches[1], matches[3])
|
||||
);
|
||||
}
|
||||
}
|
||||
return reject("Usage: /ban <userId> [<reason>]");
|
||||
},
|
||||
|
||||
// Unban a user from the room
|
||||
unban: function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
// Reset the user membership to "leave" to unban him
|
||||
return success(
|
||||
MatrixClientPeg.get().unban(room_id, matches[1])
|
||||
);
|
||||
}
|
||||
}
|
||||
return reject("Usage: /unban <userId>");
|
||||
},
|
||||
|
||||
// Define the power level of a user
|
||||
op: function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+?)( +(\d+))?$/);
|
||||
var powerLevel = 50; // default power level for op
|
||||
if (matches) {
|
||||
var user_id = matches[1];
|
||||
if (matches.length === 4 && undefined !== matches[3]) {
|
||||
powerLevel = parseInt(matches[3]);
|
||||
}
|
||||
if (powerLevel !== NaN) {
|
||||
var room = MatrixClientPeg.get().getRoom(room_id);
|
||||
if (!room) {
|
||||
return reject("Bad room ID: " + room_id);
|
||||
}
|
||||
var powerLevelEvent = room.currentState.getStateEvents(
|
||||
"m.room.power_levels", ""
|
||||
);
|
||||
return success(
|
||||
MatrixClientPeg.get().setPowerLevel(
|
||||
room_id, user_id, powerLevel, powerLevelEvent
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return reject("Usage: /op <userId> [<power level>]");
|
||||
},
|
||||
|
||||
// Reset the power level of a user
|
||||
deop: function(room_id, args) {
|
||||
if (args) {
|
||||
var matches = args.match(/^(\S+)$/);
|
||||
if (matches) {
|
||||
var room = MatrixClientPeg.get().getRoom(room_id);
|
||||
if (!room) {
|
||||
return reject("Bad room ID: " + room_id);
|
||||
}
|
||||
|
||||
var powerLevelEvent = room.currentState.getStateEvents(
|
||||
"m.room.power_levels", ""
|
||||
);
|
||||
return success(
|
||||
MatrixClientPeg.get().setPowerLevel(
|
||||
room_id, args, undefined, powerLevelEvent
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return reject("Usage: /deop <userId>");
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* Process the given text for /commands and perform them.
|
||||
* @param {string} roomId The room in which the command was performed.
|
||||
* @param {string} input The raw text input by the user.
|
||||
* @return {Object|null} An object with the property 'error' if there was an error
|
||||
* processing the command, or 'promise' if a request was sent out.
|
||||
* Returns null if the input didn't match a command.
|
||||
*/
|
||||
processInput: function(roomId, input) {
|
||||
// trim any trailing whitespace, as it can confuse the parser for
|
||||
// IRC-style commands
|
||||
input = input.replace(/\s+$/, "");
|
||||
if (input[0] === "/" && input[1] !== "/") {
|
||||
var bits = input.match(/^(\S+?)( +(.*))?$/);
|
||||
var cmd = bits[1].substring(1).toLowerCase();
|
||||
var args = bits[3];
|
||||
if (commands[cmd]) {
|
||||
return commands[cmd](roomId, args);
|
||||
}
|
||||
}
|
||||
return null; // not a command
|
||||
}
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
|
||||
function textForMemberEvent(ev) {
|
||||
// XXX: SYJS-16
|
||||
var senderName = ev.sender ? ev.sender.name : ev.getSender();
|
||||
var targetName = ev.target ? ev.target.name : ev.getStateKey();
|
||||
var reason = ev.getContent().reason ? (
|
||||
" Reason: " + ev.getContent().reason
|
||||
) : "";
|
||||
switch (ev.getContent().membership) {
|
||||
case 'invite':
|
||||
return senderName + " invited " + targetName + ".";
|
||||
case 'ban':
|
||||
return senderName + " banned " + targetName + "." + reason;
|
||||
case 'join':
|
||||
if (ev.getPrevContent() && ev.getPrevContent().membership == 'join') {
|
||||
if (ev.getPrevContent().displayname && ev.getContent().displayname && ev.getPrevContent().displayname != ev.getContent().displayname) {
|
||||
return ev.getSender() + " changed their display name from " +
|
||||
ev.getPrevContent().displayname + " to " +
|
||||
ev.getContent().displayname;
|
||||
} else if (!ev.getPrevContent().displayname && ev.getContent().displayname) {
|
||||
return ev.getSender() + " set their display name to " + ev.getContent().displayname;
|
||||
} else if (ev.getPrevContent().displayname && !ev.getContent().displayname) {
|
||||
return ev.getSender() + " removed their display name";
|
||||
} else if (ev.getPrevContent().avatar_url && !ev.getContent().avatar_url) {
|
||||
return ev.getSender() + " removed their profile picture";
|
||||
} else if (ev.getPrevContent().avatar_url && ev.getContent().avatar_url && ev.getPrevContent().avatar_url != ev.getContent().avatar_url) {
|
||||
return ev.getSender() + " changed their profile picture";
|
||||
} else if (!ev.getPrevContent().avatar_url && ev.getContent().avatar_url) {
|
||||
return ev.getSender() + " set a profile picture";
|
||||
}
|
||||
} else {
|
||||
if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key);
|
||||
return targetName + " joined the room.";
|
||||
}
|
||||
return '';
|
||||
case 'leave':
|
||||
if (ev.getSender() === ev.getStateKey()) {
|
||||
return targetName + " left the room.";
|
||||
}
|
||||
else if (ev.getPrevContent().membership === "ban") {
|
||||
return senderName + " unbanned " + targetName + ".";
|
||||
}
|
||||
else if (ev.getPrevContent().membership === "join") {
|
||||
return senderName + " kicked " + targetName + "." + reason;
|
||||
}
|
||||
else {
|
||||
return targetName + " left the room.";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function textForTopicEvent(ev) {
|
||||
var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
|
||||
return senderDisplayName + ' changed the topic to, "' + ev.getContent().topic + '"';
|
||||
};
|
||||
|
||||
function textForMessageEvent(ev) {
|
||||
var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
|
||||
var message = senderDisplayName + ': ' + ev.getContent().body;
|
||||
if (ev.getContent().msgtype === "m.emote") {
|
||||
message = "* " + senderDisplayName + " " + message;
|
||||
} else if (ev.getContent().msgtype === "m.image") {
|
||||
message = senderDisplayName + " sent an image.";
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
var handlers = {
|
||||
'm.room.message': textForMessageEvent,
|
||||
'm.room.topic': textForTopicEvent,
|
||||
'm.room.member': textForMemberEvent
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
textForEvent: function(ev) {
|
||||
var hdlr = handlers[ev.getType()];
|
||||
if (!hdlr) return "";
|
||||
return hdlr(ev);
|
||||
}
|
||||
}
|
||||
134
src/VectorConferenceHandler.js
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
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;
|
||||
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';
|
||||