diff --git a/.gitignore b/.gitignore
index 25307818b..247b54065 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ dist/*.js.map
dist/*.tar.gz
public/css/*.css
public/css/*.map
+public/css/themes/*
release/
# Node stuff
diff --git a/index.html b/index.html
index 9cb4e8c31..c7638dc19 100644
--- a/index.html
+++ b/index.html
@@ -53,7 +53,7 @@
-
+
diff --git a/package-lock.json b/package-lock.json
index 64310efe2..eb8b8395a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1275,14 +1275,14 @@
}
},
"ajv": {
- "version": "5.5.2",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
- "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
+ "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
"requires": {
- "co": "^4.6.0",
- "fast-deep-equal": "^1.0.0",
+ "fast-deep-equal": "^2.0.1",
"fast-json-stable-stringify": "^2.0.0",
- "json-schema-traverse": "^0.3.0"
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
}
},
"ajv-errors": {
@@ -1418,40 +1418,6 @@
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
- },
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
}
},
"argparse": {
@@ -1752,7 +1718,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
- "optional": true,
"requires": {
"tweetnacl": "^0.14.3"
},
@@ -1760,8 +1725,7 @@
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
- "optional": true
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
}
}
},
@@ -2808,7 +2772,8 @@
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
},
"code-point-at": {
"version": "1.1.0",
@@ -2853,9 +2818,9 @@
}
},
"combined-stream": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
- "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
+ "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
"requires": {
"delayed-stream": "~1.0.0"
}
@@ -3805,7 +3770,6 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
- "optional": true,
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
@@ -4531,9 +4495,9 @@
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fast-deep-equal": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
- "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
},
"fast-json-stable-stringify": {
"version": "2.0.0",
@@ -4738,12 +4702,12 @@
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
- "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
- "combined-stream": "1.0.6",
+ "combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
@@ -5696,11 +5660,11 @@
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
- "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"requires": {
- "ajv": "^5.3.0",
+ "ajv": "^6.5.5",
"har-schema": "^2.0.0"
}
},
@@ -6691,9 +6655,9 @@
"dev": true
},
"js-base64": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.0.tgz",
- "integrity": "sha512-wlEBIZ5LP8usDylWbDNhKPEFVFdI5hCHpnVoT/Ysvoi/PRhJENm/Rlh9TvjYB38HFfKZN7OzEbRjmjvLkFw11g=="
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
+ "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw=="
},
"js-levenshtein": {
"version": "1.1.6",
@@ -6726,8 +6690,7 @@
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
- "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
- "optional": true
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"jsesc": {
"version": "2.5.2",
@@ -6745,9 +6708,9 @@
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
- "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stringify-safe": {
"version": "5.0.1",
@@ -7382,22 +7345,12 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
- "lodash.assign": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
- "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
- },
"lodash.capitalize": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
"integrity": "sha1-+CbJtOKoUR2E46yinbBeGk87cqk=",
"dev": true
},
- "lodash.clonedeep": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
- "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
- },
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -7410,11 +7363,6 @@
"integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=",
"dev": true
},
- "lodash.mergewith": {
- "version": "4.6.1",
- "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
- "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ=="
- },
"log4js": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz",
@@ -7886,7 +7834,8 @@
"nan": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
- "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw=="
+ "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==",
+ "optional": true
},
"nanomatch": {
"version": "1.2.13",
@@ -7970,19 +7919,6 @@
"which": "1"
},
"dependencies": {
- "glob": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
- "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
@@ -8047,9 +7983,9 @@
}
},
"node-sass": {
- "version": "4.11.0",
- "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz",
- "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==",
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
+ "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
"requires": {
"async-foreach": "^0.1.3",
"chalk": "^1.1.1",
@@ -8058,12 +7994,10 @@
"get-stdin": "^4.0.1",
"glob": "^7.0.3",
"in-publish": "^2.0.0",
- "lodash.assign": "^4.2.0",
- "lodash.clonedeep": "^4.3.2",
- "lodash.mergewith": "^4.6.0",
+ "lodash": "^4.17.11",
"meow": "^3.7.0",
"mkdirp": "^0.5.1",
- "nan": "^2.10.0",
+ "nan": "^2.13.2",
"node-gyp": "^3.8.0",
"npmlog": "^4.0.0",
"request": "^2.88.0",
@@ -8081,18 +8015,15 @@
"which": "^1.2.9"
}
},
- "glob": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
- "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+ },
+ "nan": {
+ "version": "2.13.2",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
+ "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw=="
}
}
},
@@ -8664,9 +8595,9 @@
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"psl": {
- "version": "1.1.29",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
- "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
+ "version": "1.1.31",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
+ "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw=="
},
"public-encrypt": {
"version": "4.0.3",
@@ -9290,21 +9221,6 @@
"lodash": "^4.0.0",
"scss-tokenizer": "^0.2.3",
"yargs": "^7.0.0"
- },
- "dependencies": {
- "glob": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
- "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
}
},
"sass-lint": {
@@ -10081,9 +9997,9 @@
"dev": true
},
"sshpk": {
- "version": "1.14.2",
- "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
- "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
@@ -10099,8 +10015,7 @@
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
- "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
- "optional": true
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
}
}
},
@@ -10149,40 +10064,6 @@
"integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
"requires": {
"readable-stream": "^2.0.1"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "process-nextick-args": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
- "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
- },
- "readable-stream": {
- "version": "2.3.6",
- "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
- "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
}
},
"stream-browserify": {
@@ -10612,21 +10493,6 @@
"integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
"requires": {
"glob": "^7.1.2"
- },
- "dependencies": {
- "glob": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
- "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- }
}
},
"ts-events": {
diff --git a/package.json b/package.json
index 697557d4d..58415ae13 100644
--- a/package.json
+++ b/package.json
@@ -5,8 +5,8 @@
"scripts": {
"build": "npm run build:js && npm run build:css",
"build:js": "webpack --config webpack.prod.js",
- "build:css": "node-sass -o public/css/ --output-style compressed src/sass/",
- "build:css:watch": "node-sass -w -r --source-map true --source-map-embed true -o public/css/ --output-style compressed src/sass/",
+ "build:css": "node-sass -o public/css/ --output-style compressed src/sass/themes",
+ "build:css:watch": "node-sass -w -r --source-map true --source-map-embed true -o public/css/ --output-style compressed src/sass/themes",
"build:tests": "echo -e 'NOTE: Use either \"npm build:unittests\" or \"npm build:uitests\"\n' && exit 1",
"build:unittests": "webpack --config webpack.tests.js",
"build:uitests": "npm run build:css && webpack --config webpack.tests.js",
@@ -67,7 +67,7 @@
"file-saver": "2.0.0",
"messageformat": "^2.0.5",
"msgpack-lite": "~0.1.26",
- "node-sass": "^4.11.0",
+ "node-sass": "^4.12.0",
"sdp": "~2.9.0",
"ts-events": "^3.1.5",
"ts-loader": "^5.3.3",
diff --git a/public/img/ic_dnd_mention_dark.svg b/public/img/ic_dnd_mention_dark.svg
new file mode 100644
index 000000000..980545831
--- /dev/null
+++ b/public/img/ic_dnd_mention_dark.svg
@@ -0,0 +1,35 @@
+
+
diff --git a/public/img/ic_dnd_total_silence_dark.svg b/public/img/ic_dnd_total_silence_dark.svg
new file mode 100644
index 000000000..e79691274
--- /dev/null
+++ b/public/img/ic_dnd_total_silence_dark.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/public/img/ic_image_24px_dark.svg b/public/img/ic_image_24px_dark.svg
new file mode 100644
index 000000000..51e8391d7
--- /dev/null
+++ b/public/img/ic_image_24px_dark.svg
@@ -0,0 +1 @@
+
diff --git a/public/img/ic_insert_drive_file_24px_dark.svg b/public/img/ic_insert_drive_file_24px_dark.svg
new file mode 100644
index 000000000..acc47e766
--- /dev/null
+++ b/public/img/ic_insert_drive_file_24px_dark.svg
@@ -0,0 +1 @@
+
diff --git a/public/img/ic_location_on_24px_dark.svg b/public/img/ic_location_on_24px_dark.svg
new file mode 100644
index 000000000..ca28ba29e
--- /dev/null
+++ b/public/img/ic_location_on_24px_dark.svg
@@ -0,0 +1 @@
+
diff --git a/public/img/ic_mic_24px_dark.svg b/public/img/ic_mic_24px_dark.svg
new file mode 100644
index 000000000..7562d279b
--- /dev/null
+++ b/public/img/ic_mic_24px_dark.svg
@@ -0,0 +1 @@
+
diff --git a/public/img/ic_movie_24px_dark.svg b/public/img/ic_movie_24px_dark.svg
new file mode 100644
index 000000000..6d66cb875
--- /dev/null
+++ b/public/img/ic_movie_24px_dark.svg
@@ -0,0 +1 @@
+
diff --git a/public/img/ic_notifications_off_dark.svg b/public/img/ic_notifications_off_dark.svg
new file mode 100644
index 000000000..cd0296ee8
--- /dev/null
+++ b/public/img/ic_notifications_off_dark.svg
@@ -0,0 +1 @@
+
diff --git a/public/img/ic_pin_dark.svg b/public/img/ic_pin_dark.svg
new file mode 100644
index 000000000..ac8bf71f0
--- /dev/null
+++ b/public/img/ic_pin_dark.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/public/img/ic_poll_24px_dark.svg b/public/img/ic_poll_24px_dark.svg
new file mode 100644
index 000000000..3ef3e38da
--- /dev/null
+++ b/public/img/ic_poll_24px_dark.svg
@@ -0,0 +1 @@
+
diff --git a/public/img/ic_unpin_dark.svg b/public/img/ic_unpin_dark.svg
new file mode 100644
index 000000000..23f8e99fd
--- /dev/null
+++ b/public/img/ic_unpin_dark.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/public/img/spinner_dark.gif b/public/img/spinner_dark.gif
new file mode 100644
index 000000000..470c9b4c2
Binary files /dev/null and b/public/img/spinner_dark.gif differ
diff --git a/public/img/wallpaper_dark.png b/public/img/wallpaper_dark.png
new file mode 100644
index 000000000..670323df0
Binary files /dev/null and b/public/img/wallpaper_dark.png differ
diff --git a/src/components/toggle_button.ts b/src/components/toggle_button.ts
index a331015f4..38047aae2 100644
--- a/src/components/toggle_button.ts
+++ b/src/components/toggle_button.ts
@@ -15,6 +15,8 @@
* along with Threema Web. If not, see .
*/
+import { ThemeService } from '../services/theme';
+
/**
* A generic toggle button.
*
@@ -32,11 +34,17 @@ export default {
iconEnabled: '@',
iconDisabled: '@',
},
- controller: function() {
+ controller: ['ThemeService', function(themeService: ThemeService) {
this.getLabel = () => this.flag ? this.labelEnabled : this.labelDisabled;
- this.getIcon = () => this.flag ? this.iconEnabled : this.iconDisabled;
+ // this.getIcon = () => this.flag ? this.iconEnabled : this.iconDisabled;
this.action = () => this.flag ? this.onDisable() : this.onEnable();
- },
+
+ this.getIcon = () => {
+ const fn = (this.flag ? this.iconEnabled : this.iconDisabled);
+ return themeService.themedFilename(fn);
+ };
+
+ }],
template: `
`,
diff --git a/src/directives/message_icon.ts b/src/directives/message_icon.ts
index 5be0ef70e..b29150cd7 100644
--- a/src/directives/message_icon.ts
+++ b/src/directives/message_icon.ts
@@ -16,6 +16,7 @@
*/
import {hasValue} from '../helpers';
+import { ThemeService } from '../services/theme';
export default [
function() {
@@ -36,11 +37,21 @@ export default [
}
},
);
+ scope.$watch(
+ () => scope.ctrl.themeService.currentTheme, (newTheme, oldTheme) => {
+ if (hasValue(newTheme) && newTheme !== oldTheme) {
+ scope.ctrl.update();
+ }
+ },
+ );
},
controllerAs: 'ctrl',
- controller: [function() {
+ controller: ['ThemeService', function(themeService: ThemeService) {
+
+ this.themeService = themeService;
+
// Return icon depending on message type.
- const getIcon = (msgType: threema.MessageType) => {
+ const getIconFilename = (msgType: threema.MessageType) => {
switch (msgType) {
case 'image':
return 'ic_image_24px.svg';
@@ -62,6 +73,12 @@ export default [
}
};
+ // Return icon depending on the current theme
+ const getIcon = (msgType: threema.MessageType) => {
+ const fn = getIconFilename(msgType);
+ return ((fn !== null) ? themeService.themedFilename(fn) : null);
+ };
+
this.update = () => {
this.icon = getIcon(this.message.type);
this.altText = this.message.type + ' icon';
diff --git a/src/directives/message_quote.ts b/src/directives/message_quote.ts
index 35b22e43b..0f93c4b03 100644
--- a/src/directives/message_quote.ts
+++ b/src/directives/message_quote.ts
@@ -31,10 +31,11 @@ export default [
controllerAs: 'ctrl',
controller: [function() {
this.contact = () => webClientService.contacts.get(this.quote.identity);
+ this.color = () => ((this.contact().color) === '#181818') ? 'rgb(0, 150, 136)' : (this.contact().color);
}],
template: `
-
-
+
diff --git a/src/partials/dialog.settings.html b/src/partials/dialog.settings.html
index c4201365c..823377d87 100644
--- a/src/partials/dialog.settings.html
+++ b/src/partials/dialog.settings.html
@@ -54,6 +54,25 @@ settings.SETTINGS
+
+ Theme
+
+
+
+
+ {{ctrl.themeService.getNameForThemeID(themeID)}}
+
+
+
+
diff --git a/src/partials/messenger.conversation.html b/src/partials/messenger.conversation.html
index 509542d4e..78d418bf8 100644
--- a/src/partials/messenger.conversation.html
+++ b/src/partials/messenger.conversation.html
@@ -56,7 +56,7 @@ messenger.PRIVATE_CHAT
-
-

+
-
diff --git a/src/partials/messenger.navigation.html b/src/partials/messenger.navigation.html
index 3b870df3d..28a881562 100644
--- a/src/partials/messenger.navigation.html
+++ b/src/partials/messenger.navigation.html
@@ -89,13 +89,13 @@
-
+
-
+
-
+
{{ conversation.unreadCount }}
diff --git a/src/partials/messenger.ts b/src/partials/messenger.ts
index 3a6db20f9..fc619e9e4 100644
--- a/src/partials/messenger.ts
+++ b/src/partials/messenger.ts
@@ -36,6 +36,7 @@ import {NotificationService} from '../services/notification';
import {ReceiverService} from '../services/receiver';
import {SettingsService} from '../services/settings';
import {StateService} from '../services/state';
+import {ThemeService} from '../services/theme';
import {TimeoutService} from '../services/timeout';
import {VersionService} from '../services/version';
import {WebClientService} from '../services/webclient';
@@ -132,12 +133,13 @@ class SendFileController extends DialogController {
*/
class SettingsController {
- public static $inject = ['$mdDialog', '$window', 'SettingsService', 'NotificationService'];
+ public static $inject = ['$mdDialog', '$window', 'SettingsService', 'NotificationService', 'ThemeService'];
public $mdDialog: ng.material.IDialogService;
public $window: ng.IWindowService;
public settingsService: SettingsService;
private notificationService: NotificationService;
+ private themeService: ThemeService;
public activeElement: HTMLElement | null;
private desktopNotifications: boolean;
@@ -149,7 +151,8 @@ class SettingsController {
constructor($mdDialog: ng.material.IDialogService,
$window: ng.IWindowService,
settingsService: SettingsService,
- notificationService: NotificationService) {
+ notificationService: NotificationService,
+ themeService: ThemeService) {
this.$mdDialog = $mdDialog;
this.$window = $window;
this.settingsService = settingsService;
@@ -160,6 +163,7 @@ class SettingsController {
this.notificationPermission = notificationService.getNotificationPermission();
this.notificationPreview = notificationService.getWantsPreview();
this.notificationSound = notificationService.getWantsSound();
+ this.themeService = themeService;
}
public cancel(): void {
@@ -190,7 +194,6 @@ class SettingsController {
public setWantsSound(notificationSound: boolean) {
this.notificationService.setWantsSound(notificationSound);
}
-
}
interface ConversationStateParams extends UiStateParams {
@@ -218,6 +221,7 @@ class ConversationController {
private stateService: StateService;
private mimeService: MimeService;
private timeoutService: TimeoutService;
+ private themeService: ThemeService;
// Third party services
private $mdDialog: ng.material.IDialogService;
@@ -274,7 +278,7 @@ class ConversationController {
'$mdDialog', '$mdToast', '$translate', '$filter',
'$state', '$transitions',
'WebClientService', 'StateService', 'ReceiverService', 'MimeService', 'VersionService',
- 'ControllerModelService', 'TimeoutService',
+ 'ControllerModelService', 'TimeoutService', 'ThemeService',
];
constructor($stateParams: ConversationStateParams,
$log: ng.ILogService,
@@ -292,7 +296,8 @@ class ConversationController {
mimeService: MimeService,
versionService: VersionService,
controllerModelService: ControllerModelService,
- timeoutService: TimeoutService) {
+ timeoutService: TimeoutService,
+ themeService: ThemeService) {
this.$stateParams = $stateParams;
this.$log = $log;
this.webClientService = webClientService;
@@ -300,6 +305,7 @@ class ConversationController {
this.stateService = stateService;
this.mimeService = mimeService;
this.timeoutService = timeoutService;
+ this.themeService = themeService;
this.$state = $state;
this.$scope = $scope;
@@ -524,6 +530,10 @@ class ConversationController {
.hideDelay(hideDelayMs));
}
+ public getSpinner(): string {
+ return this.themeService.themedFilename('img/spinner.gif');
+ }
+
/**
* Submit function for input field. Can contain text or file data.
* Return whether sending was successful.
@@ -935,14 +945,17 @@ class NavigationController {
private $translate: ng.translate.ITranslateService;
private $state: UiStateService;
+ public themeService;
+
public static $inject = [
'$log', '$state', '$mdDialog', '$translate',
- 'WebClientService', 'StateService', 'ReceiverService', 'NotificationService', 'TrustedKeyStore',
+ 'WebClientService', 'StateService', 'ThemeService', 'ReceiverService', 'NotificationService', 'TrustedKeyStore',
];
constructor($log: ng.ILogService, $state: UiStateService,
$mdDialog: ng.material.IDialogService, $translate: ng.translate.ITranslateService,
webClientService: WebClientService, stateService: StateService,
+ themeService: ThemeService,
receiverService: ReceiverService, notificationService: NotificationService,
trustedKeyStoreService: TrustedKeyStoreService) {
@@ -961,6 +974,7 @@ class NavigationController {
this.$mdDialog = $mdDialog;
this.$translate = $translate;
this.$state = $state;
+ this.themeService = themeService;
}
public contacts(): threema.ContactReceiver[] {
diff --git a/src/sass/app.scss b/src/sass/_app.scss
similarity index 100%
rename from src/sass/app.scss
rename to src/sass/_app.scss
index cbdbc35c8..bce286f54 100644
--- a/src/sass/app.scss
+++ b/src/sass/_app.scss
@@ -25,6 +25,7 @@
// Components: Micro layout files. Your styles for buttons and navigation and
// similar page components.
//@import 'components/lists';
+@import 'components/buttons';
@import 'components/spinner';
@import 'components/links';
@import 'components/scrollbars';
@@ -41,7 +42,6 @@
@import 'components/avatar_editor';
@import 'components/avatar_area';
@import 'components/drag_file';
-@import 'components/buttons';
@import 'components/mediabox';
@import 'components/mention';
@import 'components/backbutton';
diff --git a/src/sass/base/_typography.scss b/src/sass/base/_typography.scss
index 05f99310f..119fe9506 100644
--- a/src/sass/base/_typography.scss
+++ b/src/sass/base/_typography.scss
@@ -21,6 +21,7 @@ textarea {
}
em {
+ color: $text-dark-color;
font-style: italic;
}
diff --git a/src/sass/components/_avatar_area.scss b/src/sass/components/_avatar_area.scss
index ea95351b9..e0d59ca8b 100644
--- a/src/sass/components/_avatar_area.scss
+++ b/src/sass/components/_avatar_area.scss
@@ -8,7 +8,7 @@
position: relative;
margin: auto;
border-radius: 50%;
- background-color: rgb(198, 198, 198);
+ background-color: $avatar-background-color;
width: $avatar-area-size;
height: $avatar-area-size;
overflow: hidden;
diff --git a/src/sass/components/_backbutton.scss b/src/sass/components/_backbutton.scss
index d188c9c1d..f9cc62e2b 100644
--- a/src/sass/components/_backbutton.scss
+++ b/src/sass/components/_backbutton.scss
@@ -7,7 +7,7 @@
md-icon {
transform: scale(1.2);
- color: $material-grey-dark;
+ color: $icon-dark-color;
}
&.md-focused {
diff --git a/src/sass/components/_battery.scss b/src/sass/components/_battery.scss
index 36d90b152..d1091953e 100644
--- a/src/sass/components/_battery.scss
+++ b/src/sass/components/_battery.scss
@@ -4,7 +4,7 @@
right: 24px;
&.alert md-icon {
- color: $status-error;
+ color: $status-error-color;
}
md-icon {
diff --git a/src/sass/components/_buttons.scss b/src/sass/components/_buttons.scss
index cfa11bf30..4b321975b 100644
--- a/src/sass/components/_buttons.scss
+++ b/src/sass/components/_buttons.scss
@@ -1,6 +1,151 @@
+md-dialog md-dialog-actions .md-button {
+ background-color: $button-color-enabled;
+ color: $button-text-dark-color !important; // $button-text-dark-color
+}
+
+md-select {
+ .md-select-value {
+ color: $text-medium-color !important;
+
+ .md-select-placeholder {
+ color: $text-medium-color;
+ }
+
+ .md-select-icon {
+ color: $text-medium-color;
+ }
+ }
+}
+
+.md-select-menu-container {
+ .md-active {
+ md-select-menu>* {
+ background-color: $background-dark-color;
+ color: $text-medium-color;
+ }
+ }
+}
+
+md-select-menu {
+ md-content {
+ background-color: $background-light-color !important;
+
+ md-option {
+ background-color: $background-light-color !important;
+ color: $text-medium-color;
+
+ &:not([disabled]) {
+ &:hover {
+ background-color: $button-hover-color !important;
+ color: $text-medium-color;
+ }
+ }
+
+ &[selected] {
+ background-color: $button-selected-color !important;
+ color: $text-medium-color !important;
+ }
+ }
+ }
+}
+
+md-menu-content md-menu-item {
+ background-color: $background-light-color;
+ color: $button-text-dark-color;
+}
+
+
button {
i.material-icons {
margin-bottom: 3px;
vertical-align: middle;
+ color: $icon-dark-color !important;
+ }
+}
+
+.md-button {
+ .md-fab {
+ background-color: $primary-500;
+ color: $button-text-dark-color; // $button-text-dark-color
+
+ md-icon {
+ color: $button-text-dark-color;
+ }
+
+ :not([disabled]) {
+ :hover {
+ background-color: $primary-500;
+ }
+
+ .md-focused {
+ background-color: $primary-500;
+ }
+ }
+ }
+
+ .md-primary {
+ background-color: $primary-500;
+ color: $button-text-dark-color; // $button-text-dark-color
+ }
+
+ .md-raised {
+ background-color: $button-color-enabled;
+ color: $button-text-dark-color;
+
+ :not([disabled]) md-icon {
+ color: $button-text-dark-color;
+ }
+
+ :not([disabled]) {
+ :hover {
+ background-color: $button-color-enabled;
+ }
+
+ .md-focused {
+ background-color: $button-color-pressed;
+ }
+ }
+ }
+
+ .md-warn {
+ color: $status-error-color;
+
+ :not([disabled]) md-icon {
+ color: $status-error-color;
+ }
+
+ .md-raised {
+ background-color: $status-error-color;
+ color: $button-text-light-color;
+
+ :not([disabled]) md-icon {
+ color: $button-text-light-color;
+
+ :hover {
+ background-color: $status-error-disabled-color;
+ }
+
+ .md-focused {
+ background-color: $status-error-disabled-color;
+ }
+ }
+ }
+
+ md-fab {
+ background-color: $status-error-color;
+ color: $button-text-light-color;
+
+ :not([disabled]) md-icon {
+ color: $button-text-light-color;
+
+ :hover {
+ background-color: $status-error-disabled-color;
+ }
+
+ .md-focused {
+ background-color: $status-error-disabled-color;
+ }
+ }
+ }
}
}
diff --git a/src/sass/components/_dialogs.scss b/src/sass/components/_dialogs.scss
index 538feda7d..b69629e98 100644
--- a/src/sass/components/_dialogs.scss
+++ b/src/sass/components/_dialogs.scss
@@ -27,6 +27,7 @@ md-dialog {
md-dialog-content {
line-height: 1.3em;
+ color: $text-dark-color;
// If a h2 is the first child of a dialog, remove the top margin
.md-dialog-content > h2 {
@@ -76,3 +77,52 @@ md-dialog.message-history-dialog {
}
}
}
+
+// sass-lint:disable no-vendor-prefixes
+// sass-lint:disable force-pseudo-nesting
+// sass-lint:disable no-mergeable-selectors
+md-input-container {
+ .md-input::-webkit-input-placeholder {
+ color: darken($text-placeholder-color, 30%);
+ }
+
+ .md-input:-moz-placeholder {
+ color: darken($text-placeholder-color, 30%);
+ }
+
+ .md-input::-moz-placeholder {
+ color: darken($text-placeholder-color, 30%);
+ }
+
+ // TODO: Investigate this.
+ // If this pseudo-class is nested within its parent
+ // it doesn't work in Chrome. I have no clue why.
+ .md-input:-ms-input-placeholder {
+ color: darken($text-placeholder-color, 30%);
+ }
+
+ .md-input::-webkit-input-placeholder {
+ color: darken($text-placeholder-color, 30%);
+ }
+}
+
+md-input-container.md-input-focused {
+ .md-input::-webkit-input-placeholder {
+ color: darken($text-placeholder-color, 20%);
+ }
+
+ .md-input:-moz-placeholder {
+ color: darken($text-placeholder-color, 20%);
+ }
+
+ .md-input:-ms-input-placeholder {
+ color: darken($text-placeholder-color, 20%);
+ }
+
+ .md-input::-webkit-input-placeholder {
+ color: darken($text-placeholder-color, 20%);
+ }
+}
+// sass-lint:enable no-vendor-prefixes
+// sass-lint:enable force-pseudo-nesting
+// sass-lint:enable no-mergeable-selectors
diff --git a/src/sass/components/_emoji_picker.scss b/src/sass/components/_emoji_picker.scss
index 7382c4d04..47120cd21 100644
--- a/src/sass/components/_emoji_picker.scss
+++ b/src/sass/components/_emoji_picker.scss
@@ -1,11 +1,11 @@
.twemoji-picker {
margin: 0;
- background-color: $background-grey;
+ background-color: $background-emoji-picker-color;
padding: 0 8px;
min-height: 216px;
&.outline {
- border: 2px solid #dbdbdb;
+ border: 2px solid $border-light-color;
border-radius: 4px;
width: 366px;
}
@@ -27,8 +27,8 @@
position: relative;
left: 1px;
margin-left: -1px;
- border: 1px solid $background-grey;
- background: $background-grey;
+ border: 1px solid $background-emoji-picker-color;
+ background: $background-emoji-picker-color;
cursor: pointer;
padding: 4px;
height: 32px;
@@ -46,13 +46,13 @@
right: 0;
bottom: 0;
left: 0;
- border: 1px solid #ffffff;
- background: #ffffff;
+ border: 1px solid $background-light-color;
+ background: $background-light-color;
padding: 10px 10px 0;
overflow-y: scroll;
&::selection {
- background: rgba(255, 255, 255, 0);
+ background: $white-zero;
}
}
@@ -61,8 +61,8 @@
&:checked ~ label {
z-index: 2;
- border-bottom: 1px solid #ffffff;
- background: #ffffff;
+ border-bottom: 1px solid $background-light-color;
+ background: $background-light-color;
}
&:checked ~ label ~ .content {
diff --git a/src/sass/components/_placeholder.scss b/src/sass/components/_placeholder.scss
index acdfdd778..248c92e71 100644
--- a/src/sass/components/_placeholder.scss
+++ b/src/sass/components/_placeholder.scss
@@ -5,7 +5,7 @@
&:not(:focus) {
&::before {
cursor: text;
- color: $material-grey;
+ // color: $material-grey-color;
content: attr(data-placeholder);
}
}
diff --git a/src/sass/components/_scrollbars.scss b/src/sass/components/_scrollbars.scss
index a8e74494c..7d8c4ab79 100644
--- a/src/sass/components/_scrollbars.scss
+++ b/src/sass/components/_scrollbars.scss
@@ -1,13 +1,13 @@
// Webkit specific global styling
// sass-lint:disable no-vendor-prefixes
::-webkit-scrollbar {
- border: 1px solid #f9f9f9;
- background-color: $scrollbar-color-bg;
+ border: 1px solid $border-light-color;
+ background-color: $scrollbar-background-color;
width: 6px;
}
::-webkit-scrollbar-thumb {
- background-color: $scrollbar-color-thumb;
+ background-color: $scrollbar-thumb-color;
}
// sass-lint:enable no-vendor-prefixes
@@ -16,6 +16,6 @@
// Note: Linter doesn't recognise 'scrollbar-*' properties, yet.
// sass-lint:disable no-misspelled-properties
scrollbar-width: thin; // Unfortunately Firefox does not support exact pixel values
- scrollbar-color: $scrollbar-color-thumb $scrollbar-color-bg;
+ scrollbar-color: $scrollbar-thumb-color $scrollbar-background-color;
// sass-lint:enable no-misspelled-properties
}
diff --git a/src/sass/components/_spinner.scss b/src/sass/components/_spinner.scss
index e6e6dd869..13449cfe0 100644
--- a/src/sass/components/_spinner.scss
+++ b/src/sass/components/_spinner.scss
@@ -16,15 +16,13 @@
}
}
-@mixin loading-spinner(
- $diameter,
+@mixin loading-spinner($diameter,
$thickness: 5px,
- $color-fg: $material-grey-dark,
- $color-bg: rgba(0, 0, 0, .1)
-) {
+ $color-fg: $material-grey-dark-color,
+ $color-bg: rgba(0, 0, 0, .1)) {
display: block;
- > :first-child {
+ &>:first-child {
display: block;
animation: rotater 1.1s infinite linear;
border: $thickness solid $color-bg;
diff --git a/src/sass/components/_verification_level.scss b/src/sass/components/_verification_level.scss
index 713e6c45b..e60ec252e 100644
--- a/src/sass/components/_verification_level.scss
+++ b/src/sass/components/_verification_level.scss
@@ -1,5 +1,7 @@
-// Verification level
+// Verification level.
+
.verification-dots {
+
div {
display: inline-block;
margin-right: 2px;
@@ -7,12 +9,12 @@
background-color: #e0e0e0;
width: 11px;
height: 11px;
- }
+ }
&.level1 {
div {
&:nth-of-type(1) {
- background-color: $status-error;
+ background-color: $status-error-color;
}
}
}
@@ -21,7 +23,7 @@
div {
&:nth-of-type(1),
&:nth-of-type(2) {
- background-color: $status-warning;
+ background-color: $status-warning-color;
}
}
@@ -29,7 +31,7 @@
div {
&:nth-of-type(1),
&:nth-of-type(2) {
- background-color: $work-blue;
+ background-color: $work-blue-color;
}
}
}
@@ -37,7 +39,7 @@
&.level3 {
div {
- background-color: $status-ok;
+ background-color: $status-ok-color;
}
&.work {
@@ -45,7 +47,7 @@
&:nth-of-type(1),
&:nth-of-type(2),
&:nth-of-type(3) {
- background-color: $work-blue;
+ background-color: $work-blue-color;
}
}
}
diff --git a/src/sass/helpers/_colors.scss b/src/sass/helpers/_colors.scss
index 0dc925aea..e322e9b63 100644
--- a/src/sass/helpers/_colors.scss
+++ b/src/sass/helpers/_colors.scss
@@ -2,8 +2,8 @@ $status-ok: #4caf50;
$status-warning: #ff9800;
$status-error: #f44336;
$status-starred: #ffc107;
-$border-grey: #dddddd;
-$background-grey: lighten($border-grey, 7%);
+// $border-dark-color: #dddddd;
+$background-grey: lighten($border-dark-color, 7%);
$material-grey: rgba(0, 0, 0, .54);
$material-grey-dark: rgba(0, 0, 0, .87);
$url-light-blue: #63a6cf;
@@ -17,7 +17,7 @@ $scrollbar-color-thumb: #cccccc;
$material-card-shadow: 0 2px 5px 0 rgba(0, 0, 0, .16), 0 2px 5px 0 rgba(0, 0, 0, .23);
-$active-placeholder-color: #d3d3d3;
+// $active-placeholder-color: #d3d3d3;
.color-status-ok {
color: $status-ok;
@@ -31,5 +31,5 @@ $active-placeholder-color: #d3d3d3;
color: $status-error;
}
-.material-icons.md-medium-dark { color: rgba(0, 0, 0, .54); }
-.material-icons.md-medium-dark.md-inactive { color: rgba(0, 0, 0, .26); }
+// .material-icons.md-medium-dark { color: rgba(0, 0, 0, .54); }
+// .material-icons.md-medium-dark.md-inactive { color: rgba(0, 0, 0, .26); }
diff --git a/src/sass/helpers/_message_bubble.scss b/src/sass/helpers/_message_bubble.scss
index 8b9b6c786..59bd1b69b 100644
--- a/src/sass/helpers/_message_bubble.scss
+++ b/src/sass/helpers/_message_bubble.scss
@@ -1,5 +1,3 @@
-$message-bubble-color-in: #f7f7f7;
-$message-bubble-color-out: #dff0d8;
$bubble-triangle-size: $main-padding;
@mixin message-bubble-shadow {
diff --git a/src/sass/layout/_main.scss b/src/sass/layout/_main.scss
index e4a1419f8..577ea2b31 100644
--- a/src/sass/layout/_main.scss
+++ b/src/sass/layout/_main.scss
@@ -29,9 +29,8 @@
// Set min height of messenger
&.wide #main {
min-height: 272px;
- }
+ } // Set min height of welcome screen
- // Set min height of welcome screen
&:not(.wide) #main {
height: auto;
min-height: 620px;
@@ -40,7 +39,7 @@
#main {
border-radius: $material-radius;
- box-shadow: $material-card-shadow;
+ box-shadow: $material-card-shadow-color;
width: 100%;
height: 100%;
overflow: hidden;
@@ -67,9 +66,10 @@
height: 100%;
> div.ng-scope {
- background-color: #efebe9;
+ background-color: $background-dark-color;
height: 100%;
+
// Default style for the header in the detail
.detail-header {
display: flex;
@@ -77,13 +77,14 @@
align-items: center;
z-index: 18;
box-shadow: 1px 2px 4px -2px rgba(0, 0, 0, .2);
- background-color: $dark-background-color;
+ background-color: $conversation-header-color;
padding: 0 $main-padding;
min-height: 68px;
- > * {
+ >* {
margin-left: 8px;
- }
+ color: $text-medium-color;
+ } // TODO This might need renaming to header-detail
.header-detail {
display: flex;
@@ -113,10 +114,16 @@
md-progress-circular {
display: inline-block;
}
+
+ .material-icons {
+ color: $icon-dark-color;
+ }
}
}
.detail-content {
+ background-color: $background-dark-color;
+ color: $text-medium-color;
ul {
margin: 0;
@@ -130,8 +137,18 @@
}
}
+ md-card md-card-content {
+ background-color: $background-light-color;
+ color: $text-medium-color;
+ }
+
+ md-card-title {
+ background-color: $background-light-color;
+ color: $text-medium-color;
+ }
+
// editable or readonly detail content
- > .form {
+ >.form {
display: flex;
flex-direction: column;
height: 100%;
@@ -142,6 +159,8 @@
overflow: hidden;
eee-avatar {
+ background-color: $background-light-color;
+
img {
position: relative;
top: calc(-100%);
@@ -177,6 +196,7 @@
// be sure the avatar has always the same height as width
&.avatar {
position: relative;
+ background-color: $background-light-color;
width: 50%;
overflow: hidden;
@@ -195,7 +215,6 @@
border-radius: 0;
width: 100%;
height: auto;
-
}
}
}
@@ -228,7 +247,7 @@
&.show-detail {
#navigation {
flex: 1 6 30%;
- border-right: 1px solid #dddddd;
+ border-right: 1px solid $border-dark-color;
min-width: 200px;
max-width: 30%;
}
@@ -238,27 +257,64 @@
max-width: 70%;
}
}
-
}
.material-icons {
+ color: $icon-material-grey-color;
+
&.as-button {
opacity: .5;
cursor: pointer;
+ color: $icon-material-grey-color;
&:hover {
opacity: 1;
}
}
+ .md-dark {
+ color: $icon-dark-color;
+ }
+
+ .md-light {
+ color: $icon-light-color;
+ }
+
+ .md-primary {
+ color: $icon-material-grey-color;
+ }
+}
+
+md-dialog.md-default-theme,
+md-dialog {
+ background-color: $background-medium-color;
}
.md-dialog-content {
+ background-color: $background-medium-color;
+ color: $text-medium-color;
+
&.center {
text-align: center;
}
}
+md-checkbox {
+ .md-default-theme {
+ :note(.md-checked) {
+ .md-icon {
+ border-color: $text-medium-color;
+ }
+ }
+ }
+
+ :not(.md-checked) {
+ .md-icon {
+ border-color: $text-medium-color;
+ }
+ }
+}
+
input.threema-id {
text-transform: uppercase;
}
@@ -300,7 +356,7 @@ a.click-action {
.key-values {
dt {
padding-bottom: ($main-padding);
- color: rgb(0, 150, 136);
+ color: $primary-500;
}
dd {
@@ -331,16 +387,41 @@ a.click-action {
}
}
+md-input-container {
-// angular material extensions
-md-toast.md-center {
- left: 50%;
- transform: translate3d(-50%, 0, 0);
+ .md-input {
+ border-color: $text-dark-color;
+ color: $text-dark-color;
+ }
+
+ label {
+ color: $text-medium-color;
+ }
+
+ .md-input-has-value label {
+ color: $text-medium-color;
+ }
+
+ :not(.md-input-invalid).md-input-focused {
+ border-color: $text-dark-color;
+ }
+
+ :not(.md-input-invalid).md-input-focused label {
+ color: $text-dark-color;
+ }
}
-md-toast .md-toast-content {
- span {
- text-align: center;
+// angular material extensions
+md-toast {
+ .md-center {
+ left: 50%;
+ transform: translate3d(-50%, 0, 0);
+ }
+
+ .md-toast-content {
+ span {
+ text-align: center;
+ }
}
}
@@ -348,7 +429,7 @@ md-toast .md-toast-content {
background-color: transparent;
}
-md-list-item .md-list-item-inner > md-checkbox .md-label {
+md-list-item .md-list-item-inner>md-checkbox .md-label {
display: inline-block;
white-space: inherit;
}
@@ -375,3 +456,20 @@ md-list-item .md-list-item-inner > md-checkbox .md-label {
font-size: .8em;
font-weight: 300;
}
+
+md-toolbar {
+ :not(.md-menu-toolbar) {
+ background-color: $md-toolbar-color;
+ color: $md-toolbar-text-color;
+
+ .md-button {
+ .md-icon-button md-icon {
+ color: $md-toolbar-button-color;
+ }
+ }
+ }
+}
+
+md-menu-content {
+ background-color: $background-light-color;
+}
diff --git a/src/sass/sections/_compose_area.scss b/src/sass/sections/_compose_area.scss
index 140f7baa6..286cf751e 100644
--- a/src/sass/sections/_compose_area.scss
+++ b/src/sass/sections/_compose_area.scss
@@ -1,6 +1,7 @@
compose-area {
> div {
box-shadow: 0 -2px 4px -2px rgba(0, 0, 0, .2);
+ color: $text-dark-color;
&:first-child {
display: flex;
@@ -18,9 +19,9 @@ compose-area {
&:nth-of-type(2) {
flex-grow: 1;
- border: 1px solid $border-grey;
+ border: 1px solid $border-dark-color;
border-radius: $material-radius;
- background-color: $light-background-color;
+ background-color: $background-medium-color;
overflow-x: hidden;
overflow-y: auto;
@@ -86,7 +87,7 @@ compose-area {
// On dragover
&.is-dragover div.compose {
- border: 4px dashed #d3d3d3;
+ border: 4px dashed $background-dark-color;
text-align: center;
line-height: $footer-height - 20px;
font-size: 1.4em;
diff --git a/src/sass/sections/_conversation.scss b/src/sass/sections/_conversation.scss
index f8b72c5fb..10622f361 100644
--- a/src/sass/sections/_conversation.scss
+++ b/src/sass/sections/_conversation.scss
@@ -2,11 +2,14 @@
display: flex;
position: relative;
flex-direction: column;
- background-image: url('../img/wallpaper_light.png?');
+ background-image: url($wallpaper);
height: 100%;
.detail-header {
+ background-color: $conversation-header-color;
+ color: $button-text-dark-color;
+
.header-avatar {
cursor: pointer;
}
@@ -25,9 +28,19 @@
margin: 0 0 4px;
padding: 0;
font-size: 120%;
+
+ }
+
+ .conversation-header-details-detail {
+ display: inherit;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ line-height: 1.3;
+ white-space: nowrap;
+ color: $text-dark-color;
}
- .conversation-header-details-detail,
.conversation-header-details-name {
display: inherit;
max-width: 100%;
@@ -35,6 +48,7 @@
text-overflow: ellipsis;
line-height: 1.3;
white-space: nowrap;
+ color: $conversation-header-text-color;
}
}
@@ -57,7 +71,7 @@
flex-direction: row;
align-items: center;
justify-content: space-between;
- background-color: #ffffff;
+ background-color: $background-medium-color;
padding: 8px;
.message-quote {
@@ -87,12 +101,17 @@
}
#conversation-footer {
- background-color: $dark-background-color;
+ background-color: $background-dark-color;
.chat-input {
margin: 0;
padding: 0;
width: 100%;
+
+ .compose {
+ color: $text-dark-color;
+ caret-color: $text-medium-color;
+ }
}
#mention-selector {
@@ -104,14 +123,15 @@
list-style-type: none;
> li {
+
cursor: pointer;
&:not(:hover) {
- &:not(.selected) {
- background-color: #ffffff;
+ :not(.selected) {
+ background-color: $background-medium-color;
}
}
- }
+ }
}
.contact-badge {
@@ -147,13 +167,15 @@
// Quoting
.message-quote-content {
- border-left: 5px solid #0000ff;
+ border-left: 5px solid $message-quote-color;
padding-left: 5px;
+ color: $text-dark-color;
font-size: .9em;
.message-name {
display: block;
margin-bottom: 8px;
+ color: $message-quote-color;
font-size: .9em;
font-weight: bold;
}
@@ -183,7 +205,7 @@
@include message-bubble-shadow;
display: inline-block;
border-radius: 50%;
- background-color: #ffffff;
+ background-color: $spinner-background-color;
padding: 6px;
width: 30px;
height: 30px;
@@ -193,6 +215,7 @@
&.typing-indicator {
.message-body {
min-width: 15px;
+ color: $text-dark-color;
}
}
}
@@ -257,6 +280,7 @@
padding: 8px;
min-width: 64px;
max-width: 85%;
+ color: $text-dark-color;
&:not(.text-message-body) {
// set fixed height to thumbnails
@@ -308,6 +332,7 @@
span {
white-space: pre-wrap;
+ color: $text-dark-color;
}
}
@@ -353,7 +378,7 @@
align-items: flex-end;
margin-top: 4px;
height: 16px;
- color: #808080;
+ color: $text-dark-color;
font-size: .8em;
.message-state {
@@ -362,10 +387,19 @@
i {
position: relative;
top: 2px;
+ color: $text-dark-color;
+ }
+
+ i.user-ack {
+ color: #4caf50;
+ }
+
+ i.user-dec {
+ color: #ff9800;
}
i.timeout {
- color: $status-error;
+ color: $status-error-color;
}
}
@@ -375,6 +409,7 @@
height: 1em;
min-height: 1em;
vertical-align: top;
+ color: $text-dark-color;
font-size: 1em;
}
}
@@ -384,6 +419,7 @@
.message-body {
position: relative;
background-color: $message-bubble-color-in;
+ color: $text-dark-color;
}
// arrow
@@ -410,6 +446,7 @@
.message-body {
position: relative;
background-color: $message-bubble-color-out;
+ color: $text-dark-color;
}
// arrow
@@ -441,7 +478,7 @@
justify-content: center;
.message-body {
- background-color: #fcf8e3;
+ background-color: $ctrl-message-bubble-color;
.message-text {
margin-top: 0;
@@ -460,7 +497,7 @@
.line {
flex-grow: 1;
- border-bottom: 1px solid #bbbbbb;
+ border-bottom: 1px solid $text-dark-color;
text-align: center;
line-height: 0;
}
@@ -468,7 +505,8 @@
.text {
margin: 0;
padding: 0 10px;
- text-shadow: 0 0 4px #bbbbbb;
+ text-shadow: 0 0 4px $text-dark-color;
+ color: $text-dark-color;
font-size: .9em;
font-weight: 300;
}
@@ -489,6 +527,6 @@
md-menu-item {
a span {
- color: $material-grey-dark;
+ color: $material-grey-dark-color;
}
}
diff --git a/src/sass/sections/_navigation.scss b/src/sass/sections/_navigation.scss
index c4e135dbf..1e13020d6 100644
--- a/src/sass/sections/_navigation.scss
+++ b/src/sass/sections/_navigation.scss
@@ -1,6 +1,6 @@
// Separator for navigation items
@mixin nav-item-separator {
- border-bottom: 1px solid $border-grey;
+ border-bottom: 1px solid $border-dark-color;
}
#navigation-topheader {
@@ -10,10 +10,14 @@
justify-content: center;
z-index: 21;
box-shadow: -2px 2px 4px -2px rgba(0, 0, 0, .8);
- background-color: #424242;
+ background-color: $navigation-header-color;
padding: 0 $main-padding 0 (2 * $main-padding);
min-height: 68px;
- color: #ffffff;
+ color: $secondary-000;
+
+ .material-icons.md-light {
+ color: $icon-light-color !important;
+ }
.my-identity {
flex-grow: 1;
@@ -37,7 +41,11 @@
#navigation-header {
z-index: 18;
box-shadow: -2px 2px 4px -2px rgba(0, 0, 0, .4);
- background-color: #f5f5f5;
+ background-color: $background-medium-color;
+
+ .material-icons.md-dark {
+ color: $icon-dark-color;
+ }
.main {
display: flex;
@@ -57,24 +65,28 @@
}
.search {
+
$duration-opacity: .1s;
$duration-height: .2s;
+
+ // Animation when disappearing
transition: opacity $duration-opacity linear,
max-height $duration-height ease-out $duration-opacity,
visibility 0s linear $duration-height;
+
visibility: hidden;
opacity: 0;
- // Animation when disappearing
max-height: 0;
input {
&[type='text'] {
margin: 0;
- border: 1px solid #dddddd;
+ border: 1px solid $text-medium-color;
border-radius: $material-radius;
- background-color: #ffffff;
+ background-color: $background-light-color;
padding: $main-padding;
width: 100%;
+ color: $text-dark-color;
box-sizing: border-box;
.md-errors-spacer {
@@ -100,11 +112,12 @@
#navigation-conversations,
#navigation-contacts {
@include scrollbar;
+ background-color: $navigation-background-color;
.empty {
margin-top: 1em;
margin-left: 16px;
- color: $material-grey;
+ color: $text-dark-color;
font-size: 1.2em;
font-weight: 300;
}
@@ -114,7 +127,6 @@
$border-width: 4px;
$border-count: 2;
flex: 1;
- background-color: #ffffff;
overflow-y: auto;
.loading-element {
@@ -124,23 +136,28 @@
.conversation-wrapper {
@include nav-item-separator;
display: block;
+ background-color: $navigation-background-color;
.conversation {
display: flex;
flex-direction: row;
align-items: center;
- border-left: $border-width * $border-count solid #ffffff;
+ border-left: $border-width * $border-count solid $navigation-background-color;
padding: (1.5 * $main-padding) $main-padding - ($border-width * ($border-count - 1));
- &.active,
- &:hover {
- border-color: $dark-background-color;
- background-color: $dark-background-color;
+ &.active {
+ border-color: $navigation-background-highlight-color;
+ background-color: $navigation-background-highlight-color;
+ &:hover {
+ border-color: $navigation-background-highlight-color;
+ background-color: $navigation-background-highlight-color;
+ }
}
&:hover {
&:not(.active) {
- cursor: pointer;
+ border-color: $navigation-background-highlight-color;
+ background-color: $navigation-background-highlight-color;
}
}
@@ -150,8 +167,8 @@
to right,
$status-error,
$status-error 50%,
- #ffffff 50%,
- #ffffff 100%
+ $navigation-background-color 50%,
+ $navigation-background-color 100%
) 1 100%;
&.active {
@@ -159,8 +176,18 @@
to right,
$status-error,
$status-error 50%,
- $dark-background-color 50%,
- $dark-background-color 100%
+ $navigation-background-highlight-color 50%,
+ $navigation-background-highlight-color 100%
+ ) 1 100%;
+ }
+
+ &:hover {
+ border-image: linear-gradient(
+ to right,
+ $status-error,
+ $status-error 50%,
+ $navigation-background-highlight-color 50%,
+ $navigation-background-highlight-color 100%
) 1 100%;
}
}
@@ -171,8 +198,8 @@
to right,
$status-starred,
$status-starred 50%,
- #ffffff 50%,
- #ffffff 100%
+ $navigation-background-color 50%,
+ $navigation-background-color 100%
) 1 100%;
&.active {
@@ -180,8 +207,18 @@
to right,
$status-starred,
$status-starred 50%,
- $dark-background-color 50%,
- $dark-background-color 100%
+ $navigation-background-highlight-color 50%,
+ $navigation-background-highlight-color 100%
+ ) 1 100%;
+ }
+
+ &:hover {
+ border-image: linear-gradient(
+ to right,
+ $status-starred,
+ $status-starred 50%,
+ $navigation-background-highlight-color 50%,
+ $navigation-background-highlight-color 100%
) 1 100%;
}
}
@@ -224,7 +261,7 @@
flex-direction: row;
flex-grow: 1;
justify-content: space-between;
- color: #000000;
+ color: $text-medium-color;
font-weight: bold;
.title {
@@ -254,7 +291,7 @@
flex-direction: row;
align-items: center;
justify-content: space-between;
- color: $material-grey;
+ color: $latest-message-text-color;
.left {
display: flex;
@@ -274,12 +311,11 @@
}
}
- .message-name,
- .message-text {
- line-height: 1.7em;
- }
-
.message-name {
+ .message-text {
+ line-height: 1.7em;
+ }
+
&::after {
margin-right: 4px;
content: ':';
@@ -327,6 +363,10 @@
display: none;
}
}
+
+ .message-date {
+ color: $latest-message-text-color;
+ }
}
}
}
@@ -336,7 +376,7 @@
border-radius: 5px;
background-color: $status-error;
padding: 0 5px;
- color: #ffffff;
+ color: $secondary-000;
font-size: .9em;
}
}
@@ -345,19 +385,22 @@
#navigation-contacts {
flex: 1;
+ background-color: $navigation-background-color;
overflow-y: scroll;
.contact {
@include nav-item-separator;
+
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
- border-left: 4px solid #ffffff;
+ border-left: 4px solid $background-light-color;
padding: 12px 8px;
.avatar-box {
padding-right: 8px;
+ color: $text-medium-color;
}
.left {
@@ -373,16 +416,31 @@
}
.name {
+ color: $text-medium-color;
font-weight: bold;
}
+ .identity {
+ color: $text-medium-color;
+ }
+
.nickname {
+ color: $text-medium-color;
font-size: .8em;
}
&:hover {
- background-color: #f5f5f5;
+ background-color: $background-dark-color;
cursor: pointer;
+
+ &.active {
+ border-color: $navigation-background-highlight-color;
+ background-color: $navigation-background-highlight-color;
+ }
+
+ &:not(.active) {
+ background-color: $navigation-background-highlight-color;
+ }
}
}
@@ -399,7 +457,7 @@
position: relative;
md-icon {
- color: #ffffff;
+ color: $button-text-light-color;
}
.fab-button-add-contact {
diff --git a/src/sass/sections/_noscript.scss b/src/sass/sections/_noscript.scss
index 5bb6e70ca..c6d27984c 100644
--- a/src/sass/sections/_noscript.scss
+++ b/src/sass/sections/_noscript.scss
@@ -2,7 +2,7 @@ noscript {
div {
display: block;
margin: 16px auto 0;
- background-color: #ffffff;
+ background-color: $background-medium-color;
padding: 16px;
width: 50%;
text-align: center;
diff --git a/src/sass/sections/_status_bar.scss b/src/sass/sections/_status_bar.scss
index 6b71d3dc6..6d93edb0e 100644
--- a/src/sass/sections/_status_bar.scss
+++ b/src/sass/sections/_status_bar.scss
@@ -51,31 +51,31 @@ body {
.status-ok {
#status-bar {
- background-color: $status-ok;
+ background-color: $status-ok-color;
}
#title .dot {
- fill: $status-ok;
+ fill: $status-ok-color;
}
}
.status-warning {
#status-bar {
- background-color: $status-warning;
+ background-color: $status-warning-color;
}
#title .dot {
- fill: $status-warning;
+ fill: $status-warning-color;
}
}
.status-error {
#status-bar {
- background-color: $status-error;
+ background-color: $status-error-color;
}
#title .dot {
- fill: $status-error;
+ fill: $status-error-color;
}
}
@@ -83,7 +83,7 @@ body {
// the relayed data task (because connection loss on iOS is expected).
.status-task-relayed-data {
&.status-warning #status-bar {
- background-color: $status-ok;
+ background-color: $status-ok-color;
}
}
diff --git a/src/sass/sections/_welcome.scss b/src/sass/sections/_welcome.scss
index 054617394..d9027486a 100644
--- a/src/sass/sections/_welcome.scss
+++ b/src/sass/sections/_welcome.scss
@@ -1,4 +1,5 @@
#welcome {
+ background-color: $secondary-000;
padding: 16px 32px;
line-height: 1.4em;
@@ -6,7 +7,7 @@
margin-top: 1em;
&.error {
- color: $status-error;
+ color: $status-error-color;
}
}
@@ -18,6 +19,7 @@
&,
input {
text-align: center;
+ color: $secondary-999;
}
md-input-container {
diff --git a/src/sass/themes/app-dark.scss b/src/sass/themes/app-dark.scss
new file mode 100644
index 000000000..0451b2411
--- /dev/null
+++ b/src/sass/themes/app-dark.scss
@@ -0,0 +1,147 @@
+$wallpaper: '../../public/img/wallpaper_dark.png?'; // TODO: Use a darker wallpaper
+
+// Define colors
+$threema-green-color: rgb(0, 150, 136);
+$dark-gray: rgb(83, 83, 83);
+$middle-gray: rgb(112, 112, 112);
+$light-gray: rgb(235, 235, 235);
+
+// Primary
+$primary-900: #004d3f;
+$primary-800: #00695b;
+$primary-700: #00796a;
+$primary-600: #00897a;
+$primary-500: $threema-green-color; // Standard
+$primary-400: #26a699;
+$primary-300: #4db6ab;
+$primary-200: #80cbc3;
+$primary-100: #b2dfdb;
+$primary-050: #e0f2f1;
+$primary: $primary-500;
+
+// Secondary
+$secondary-999: rgba(0, 0, 0, 1);
+$secondary-900: rgba(32, 32, 32, 1);
+$secondary-850: rgba(48, 48, 48, 1);
+$secondary-800: rgba(65, 65, 65, 1);
+$secondary-750: rgba(76, 76, 76, 1);
+$secondary-700: rgba(96, 96, 96, 1);
+$secondary-600: rgba(116, 116, 116, 1);
+$secondary-570: rgba(148, 148, 148, 1);
+$secondary-500: rgba(157, 157, 157, 1); // Standard
+$secondary-400: rgba(188, 188, 188, 1);
+$secondary-320: rgba(219, 219, 219, 1);
+$secondary-300: rgba(223, 223, 223, 1);
+$secondary-200: rgba(237, 237, 237, 1);
+$secondary-100: rgba(245, 245, 245, 1);
+$secondary-050: rgba(250, 250, 250, 1);
+$secondary-000: rgba(255, 255, 255, 1);
+$secondary: $secondary-500;
+
+// Terniary
+
+// Material colors
+$material-grey-color: $secondary-900;
+$material-grey-inactive-color: lighter($material-grey-color, 50%);
+$material-grey-dark-color: darken($secondary-900, 25%);
+$material-card-shadow-color: px 2px 5px px $material-grey-dark-color,
+px 2px 5px px $material-grey-dark-color;
+
+// Status colors
+$status-ok-color: #4caf50;
+$status-warning-color: #ff9800;
+$status-error-color: #f44336;
+$status-error-disabled-color: #f44336;
+$status-starred-color: #ffc107;
+
+// Border colors
+$border-dark-color: $secondary-750;
+$border-light-color: $secondary-320;
+
+// Scrollbar colors
+$scrollbar-thumb-color: $secondary-800;
+$scrollbar-background-color: $secondary-600;
+
+// Background colors
+$background-dark-color: $secondary-900;
+$background-medium-color: $secondary-850;
+$background-light-color: $secondary-750;
+
+$background-emoji-picker-color: $background-medium-color;
+$navigation-background-color: $background-medium-color;
+$navigation-background-highlight-color: $background-dark-color;
+$spinner-background-color: $background-medium-color;
+
+$avatar-background-color: $primary-900;
+
+// Header colors
+$navigation-header-color: $secondary-800;
+$conversation-header-color: $secondary-800;
+
+// Text colors
+$text-dark-color: $secondary-500;
+$text-medium-color: $secondary-300;
+$text-light-color: $secondary-050;
+
+$conversation-header-text-color: $secondary-050;
+
+$latest-message-text-color: $secondary-500;
+$text-placeholder-color: $secondary-500;
+$text-protocol-updated-color: #9dd0f2;
+
+// md-toolbar colors
+$md-toolbar-color: $background-dark-color;
+$md-toolbar-text-color: $secondary-800;
+$md-toolbar-button-color: $secondary-800;
+
+// Icon colors
+$icon-color: $secondary-000;
+$icon-dark-color: $secondary-400;
+$icon-light-color: $secondary-400;
+$icon-material-grey-color: $secondary-500;
+
+// Message bubble colors
+$message-quote-color: $threema-green-color;
+$message-bubble-color-in: rgb(31, 25, 25);
+$message-bubble-color-out: rgb(27, 39, 52);
+$ctrl-message-bubble-color: #0c2298; //TODO: Check what this color is in the Android App
+
+// Button colors
+$button-color: $material-grey-color;
+$button-color-enabled: $secondary-600;
+$button-color-disabled: lighten($secondary-700, 65%);
+$button-color-pressed: $secondary-750;
+$button-hover-color: lighten($background-light-color, 15%);
+$button-selected-color: darken($background-light-color, 10%);
+
+$button-text-dark-color: $secondary-000;
+$button-text-light-color: $secondary-900;
+
+// Misc colors
+$active-placeholder-color: $text-dark-color;
+$url-light-blue-color: #63a6cf;
+$work-blue-color: #3b84df;
+$white-zero: rgba(255, 255, 255, 0);
+
+
+.color-status-ok {
+ color: $status-ok-color;
+}
+
+.color-status-warning {
+ color: $status-warning-color;
+}
+
+.color-status-error {
+ color: $status-error-color;
+}
+
+.material-icons.md-medium-dark {
+ color: $secondary-400;
+}
+
+.material-icons.md-medium-dark.md-inactive {
+ color: $material-grey-inactive-color;
+}
+
+@import '../app';
diff --git a/src/sass/themes/app-light.scss b/src/sass/themes/app-light.scss
new file mode 100644
index 000000000..93562e8b9
--- /dev/null
+++ b/src/sass/themes/app-light.scss
@@ -0,0 +1,145 @@
+$wallpaper: '../../public/img/wallpaper_light.png?';
+
+// Define colors
+$threema-green-color: rgb(0, 150, 136);
+$dark-gray: rgb(83, 83, 83);
+$middle-gray: rgb(112, 112, 112);
+$light-gray: rgb(235, 235, 235);
+
+// Primary
+$primary-900: #004d3f;
+$primary-800: #00695b;
+$primary-700: #00796a;
+$primary-600: #00897a;
+$primary-500: $threema-green-color; // Standard
+$primary-400: #26a699;
+$primary-300: #4db6ab;
+$primary-200: #80cbc3;
+$primary-100: #b2dfdb;
+$primary-050: #e0f2f1;
+$primary: $primary-500;
+
+// Secondary
+$secondary-999: rgba(0, 0, 0, 1);
+$secondary-900: rgba(32, 32, 32, 1);
+$secondary-800: rgba(66, 66, 66, 1);
+$secondary-700: rgba(96, 96, 96, 1);
+$secondary-600: rgba(116, 116, 116, 1);
+$secondary-570: rgba(148, 148, 148, 1);
+$secondary-500: rgba(157, 157, 157, 1); // Standard
+$secondary-400: rgba(188, 188, 188, 1);
+$secondary-320: rgba(219, 219, 219, 1);
+$secondary-300: rgba(223, 223, 223, 1);
+$secondary-200: rgba(237, 237, 237, 1);
+$secondary-100: rgba(245, 245, 245, 1);
+$secondary-050: rgba(250, 250, 250, 1);
+$secondary-000: rgba(255, 255, 255, 1);
+$secondary: $secondary-500;
+
+// Terniary
+
+// Material colors
+$material-grey-color: $secondary-900;
+$material-grey-inactive-color: lighter($material-grey-color, 50%);
+$material-grey-dark-color: darken($secondary-900, 25%);
+$material-card-shadow-color: px 2px 5px px $material-grey-dark-color,
+px 2px 5px px $material-grey-dark-color;
+
+// Status colors
+$status-ok-color: #4caf50;
+$status-warning-color: #ff9800;
+$status-error-color: #f44336;
+$status-error-disabled-color: #f44336;
+$status-starred-color: #ffc107;
+
+// Border colors
+$border-dark-color: #ddd;
+$border-light-color: $secondary-320;
+
+// Background colors
+$background-dark-color: #efebe9;
+$background-medium-color: #f5f5f5;
+$background-light-color: $secondary-000;
+
+$background-emoji-picker-color: #efefef;
+$navigation-background-color: $secondary-000;
+$navigation-background-highlight-color: $background-medium-color;
+$spinner-background-color: $background-light-color;
+
+$avatar-background-color: rgb(198, 198, 198);
+
+// Scrollbar colors
+$scrollbar-thumb-color: #cccccc;
+$scrollbar-background-color: $background-dark-color;
+
+// Header colors
+$navigation-header-color: $secondary-800;
+$conversation-header-color: #f6f6f6;
+
+// Text colors
+$text-dark-color: $secondary-999;
+$text-medium-color: $secondary-999;
+$text-light-color: $secondary-300;
+
+$conversation-header-text-color: $material-grey-dark-color;
+
+$latest-message-text-color: $material-grey-color;
+$text-placeholder-color: $middle-gray;
+$text-protocol-updated-color: #9dd0f2;
+
+// md-toolbar colors
+$md-toolbar-color: $navigation-header-color;
+$md-toolbar-text-color: $secondary-000;
+$md-toolbar-button-color: $secondary-000;
+
+
+// Icon colors
+$icon-color: $secondary-900;
+$icon-dark-color: $material-grey-dark-color;
+$icon-light-color: $secondary-000;
+$icon-material-grey-color: $material-grey-color;
+
+// Message bubble colors
+$message-quote-color: $threema-green-color;
+$message-bubble-color-in: #f7f7f7;
+$message-bubble-color-out: #dff0d8;
+$ctrl-message-bubble-color: #fcf8e3;
+
+// Button colors
+$button-color: $material-grey-color;
+$button-color-enabled: rgb(250, 250, 250);
+$button-color-disabled: rgba(255, 254, 254, .65);
+$button-color-pressed: rgb(155, 155, 155);
+$button-hover-color: rgba(158, 158, 158, .2);
+$button-selected-color: rgba(158, 158, 158, 1);
+
+$button-text-dark-color: $secondary-999;
+$button-text-light-color: $secondary-000;
+
+// Misc colors
+$active-placeholder-color: #d3d3d3;
+$url-light-blue-color: #288ac7;
+$work-blue-color: #3b84df;
+$white-zero: rgba(255, 255, 255, 0);
+
+.color-status-ok {
+ color: $status-ok-color;
+}
+
+.color-status-warning {
+ color: $status-warning-color;
+}
+
+.color-status-error {
+ color: $status-error-color;
+}
+
+.material-icons.md-medium-dark {
+ color: $material-grey-color;
+}
+
+.material-icons.md-medium-dark.md-inactive {
+ color: $material-grey-inactive-color;
+}
+
+@import '../app';
diff --git a/src/services.ts b/src/services.ts
index 4ead3ea9c..ca4a740c9 100644
--- a/src/services.ts
+++ b/src/services.ts
@@ -32,6 +32,7 @@ import {ReceiverService} from './services/receiver';
import {SettingsService} from './services/settings';
import {StateService} from './services/state';
import {StringService} from './services/string';
+import {ThemeService} from './services/theme';
import {TimeoutService} from './services/timeout';
import {TitleService} from './services/title';
import {UriService} from './services/uri';
@@ -54,6 +55,7 @@ angular.module('3ema.services', [])
.service('StateService', StateService)
.service('TimeoutService', TimeoutService)
.service('TitleService', TitleService)
+.service('ThemeService', ThemeService)
.service('TrustedKeyStore', TrustedKeyStoreService)
.service('WebClientService', WebClientService)
.service('MimeService', MimeService)
diff --git a/src/services/notification.ts b/src/services/notification.ts
index a9d1087e5..a08014e4b 100644
--- a/src/services/notification.ts
+++ b/src/services/notification.ts
@@ -221,7 +221,6 @@ export class NotificationService {
this.storeSetting(NotificationService.SETTINGS_NOTIFICATIONS, 'false');
}
}
-
/**
* Sets if the user wants a message preview
*/
diff --git a/src/services/theme.ts b/src/services/theme.ts
new file mode 100644
index 000000000..30f9a679a
--- /dev/null
+++ b/src/services/theme.ts
@@ -0,0 +1,93 @@
+/**
+ * This file is part of Threema Web.
+ *
+ * Threema Web is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Threema Web. If not, see .
+ */
+import {SettingsService} from './settings';
+
+export class ThemeService {
+
+ public currentTheme: number;
+
+ public themeOptions: string[] = ['THEME_LIGHT_WHITE', 'THEME_DARK_BLACK'];
+ private themeFilenames: string[] = ['app-light.css', 'app-dark.css'];
+
+ private settingsService: SettingsService;
+ public static $inject = ['SettingsService'];
+
+ constructor(settingsService: SettingsService) {
+ this.settingsService = settingsService;
+ this.currentTheme = this.getThemeID();
+ }
+
+ /*
+ * Returns a string with _dark added at the end.
+ * Used to get the correct icons depending on the theme.
+ */
+ public themedFilename(fn: string): string {
+ if (fn == null) {
+ return null;
+ }
+ const ext = (this.currentTheme === 1) ? '_dark' : '';
+ const fnl = fn.length;
+ return fn.substring(0, fnl - 4) + ext + fn.substring(fnl - 4, fnl);
+ }
+
+ public setThemeID(themeID: number) {
+ this.currentTheme = themeID;
+ this.storeSetting('ThemeService.THEME_SETTING', themeID.toString());
+ }
+
+ public getThemeIDS(): number[] {
+ const list: number[] = [];
+ for (let i = 0; i < this.themeOptions.length; i++) {
+ list.push(i);
+ }
+ return list;
+ }
+
+ public getThemeID(): number {
+ const theme: number = Number(this.retrieveSetting('ThemeService.THEME_SETTING'));
+ this.currentTheme = theme;
+ return isNaN(theme) ? 0 : theme;
+ }
+
+ private getNameForThemeID(themeID: number): string {
+ if (themeID < this.themeOptions.length) {
+ return this.themeOptions[themeID];
+ }
+ return this.themeOptions[0];
+ }
+
+ public getCSSForThemeID(themeID: number): string {
+ if (themeID < this.themeFilenames.length) {
+ return this.themeFilenames[themeID];
+ }
+ return this.themeFilenames[0];
+ }
+
+ /**
+ * Stores the given key/value pair in local storage
+ */
+ private storeSetting(key: string, value: string): void {
+ this.settingsService.storeUntrustedKeyValuePair(key, value);
+ }
+
+ /**
+ * Retrieves the value for the given key from local storage
+ */
+ private retrieveSetting(key: string): string {
+ return this.settingsService.retrieveUntrustedKeyValuePair(key);
+ }
+}
diff --git a/src/services/webclient.ts b/src/services/webclient.ts
index f84ad750e..5aa1c6662 100644
--- a/src/services/webclient.ts
+++ b/src/services/webclient.ts
@@ -3155,7 +3155,7 @@ export class WebClientService {
canChangeFirstName: true,
canChangeLastName: true,
},
- color: '#000000',
+ color: 'rgb(112, 112, 112)',
});
this.registerInitializationStep(InitializationStep.Profile);
diff --git a/tests/ui/compose_area.html b/tests/ui/compose_area.html
index e05eecf6f..e0e53f407 100644
--- a/tests/ui/compose_area.html
+++ b/tests/ui/compose_area.html
@@ -20,7 +20,7 @@
-
+
diff --git a/tools/spinner/1-export.py b/tools/spinner/1-export.py
index 3601e3fe1..21bbe7b06 100755
--- a/tools/spinner/1-export.py
+++ b/tools/spinner/1-export.py
@@ -31,12 +31,12 @@ def create_png(rotation: int) -> None:
'-e', filename,
'-w', '30',
'-h', '30',
+ # '-b', 'rgb(48, 48, 48)',
'-b', 'white',
f.name,
])
result.check_returncode()
-
if __name__ == '__main__':
with open('spinner.svg', 'r') as f:
svg = f.read()
diff --git a/tools/spinner/spinner_dark.svg b/tools/spinner/spinner_dark.svg
new file mode 100644
index 000000000..ff96dbc65
--- /dev/null
+++ b/tools/spinner/spinner_dark.svg
@@ -0,0 +1,83 @@
+
+
+
+
diff --git a/troubleshoot/index.html b/troubleshoot/index.html
index c44fc3d70..d7cac8bab 100644
--- a/troubleshoot/index.html
+++ b/troubleshoot/index.html
@@ -58,7 +58,7 @@
}
#wrapper {
- background-color: white;
+ background-color: $background-medium-color;
margin: 0 auto;
padding: 16px 32px 32px;
min-width: 400px;