index.jsx 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import React, { Component } from "react";
  2. import async from "async";
  3. import PropTypes from "prop-types";
  4. import { connect } from "react-redux";
  5. import { NavLink } from "react-router-dom";
  6. import { translate } from "react-i18next";
  7. import config from "config";
  8. import "settings.scss";
  9. import CustomInput from "components/CustomInput.jsx";
  10. import CustomMessages from "components/CustomMessages.jsx";
  11. import io from "io";
  12. @connect(state => ({
  13. user: {
  14. userId: state.user.get("userId"),
  15. },
  16. }))
  17. @translate(["settings"], { wait: true })
  18. export default class Settings extends Component {
  19. static propTypes = {
  20. user: PropTypes.object,
  21. t: PropTypes.func,
  22. };
  23. static defaultProps = {
  24. user: {
  25. userId: "",
  26. },
  27. t: () => {},
  28. };
  29. constructor(props) {
  30. super(props);
  31. CustomInput.initialize(this);
  32. this.state = {
  33. passwordLinked: false,
  34. gitHubLinked: false,
  35. };
  36. io.getSocket(socket => {
  37. socket.emit("users.findBySession", res => {
  38. if (res.status === "success") {
  39. this.input.email.setValue(res.data.email.address, true);
  40. this.input.username.setValue(res.data.username, true);
  41. this.setState({
  42. passwordLinked: res.data.password,
  43. gitHubLinked: res.data.github,
  44. });
  45. } else {
  46. this.messages.addError(this.props.t("general:notLoggedInError"));
  47. }
  48. });
  49. socket.on("event:user.username.changed", username => {
  50. this.input.username.setValue(username, true);
  51. });
  52. // TODO Email changed event?
  53. socket.on("event:user.linkPassword", () => {
  54. this.setState({
  55. passwordLinked: true,
  56. });
  57. });
  58. socket.on("event:user.linkGitHub", () => {
  59. this.setState({
  60. gitHubLinked: true,
  61. });
  62. });
  63. socket.on("event:user.unlinkPassword", () => {
  64. this.setState({
  65. passwordLinked: false,
  66. });
  67. });
  68. socket.on("event:user.unlinkGitHub", () => {
  69. this.setState({
  70. gitHubLinked: false,
  71. });
  72. });
  73. });
  74. }
  75. /* githubRedirect() {
  76. localStorage.setItem("github_redirect", window.location.pathname);
  77. } */
  78. saveChanges = () => {
  79. this.messages.clearErrorSuccess();
  80. async.waterfall([
  81. (next) => {
  82. if (this.input.username.isPristine()) this.input.username.validate(next);
  83. else next();
  84. },
  85. (next) => {
  86. if (this.input.email.isPristine()) this.input.email.validate(next);
  87. else next();
  88. },
  89. ], () => {
  90. if (CustomInput.hasInvalidInput(this.input, ["username", "email"])) {
  91. this.messages.clearAddError(this.props.t("general:someFieldsAreIncorrectError"));
  92. } else if (this.input.username.isOriginal() && this.input.email.isOriginal()) {
  93. this.messages.clearAddError(this.props.t("settings:usernameOrEmailHasntChanged"));
  94. } else {
  95. const email = this.input.email.getValue();
  96. const username = this.input.username.getValue();
  97. io.getSocket(socket => {
  98. if (!this.input.email.isOriginal()) {
  99. socket.emit("users.updateEmail", this.props.user.userId, email, res => {
  100. if (res.status === "success") {
  101. this.messages.clearAddSuccess(this.props.t("settings:successfullyUpdatedEmail"));
  102. } else {
  103. this.messages.addError(res.message);
  104. }
  105. });
  106. }
  107. if (!this.input.username.isOriginal()) {
  108. socket.emit("users.updateUsername", this.props.user.userId, username, res => {
  109. if (res.status === "success") {
  110. this.messages.clearAddSuccess(this.props.t("settings:successfullyUpdatedUsername"));
  111. } else {
  112. this.messages.addError(res.message);
  113. }
  114. });
  115. }
  116. });
  117. }
  118. });
  119. };
  120. changePassword = () => {
  121. this.messages.clearErrorSuccess();
  122. if (CustomInput.hasInvalidInput(this.input, ["newPassword"])) {
  123. this.messages.clearAddError(this.props.t("general:someFieldsAreIncorrectError"));
  124. } else if (!this.state.passwordLinked) {
  125. this.messages.clearAddError("You don't have a password set.");
  126. } else {
  127. io.getSocket(socket => {
  128. socket.emit("users.updatePassword", this.input.newPassword.getValue(), res => {
  129. if (res.status === "success") {
  130. this.messages.clearAddSuccess("Successfully changed password.");
  131. } else {
  132. this.messages.addError(res.message);
  133. }
  134. });
  135. });
  136. }
  137. };
  138. logOutEverywhere = () => {
  139. this.messages.clearErrorSuccess();
  140. io.getSocket(socket => {
  141. socket.emit("users.removeSessions", this.props.user.userId, res => {
  142. if (res.status === "success") {
  143. this.messages.clearAddSuccess(this.props.t("settings:successfullyLoggedOutEverywhere"));
  144. } else {
  145. this.messages.addError(res.message);
  146. }
  147. });
  148. });
  149. };
  150. unlinkGitHub = () => {
  151. this.messages.clearErrorSuccess();
  152. io.getSocket(socket => {
  153. socket.emit("users.unlinkGitHub", res => {
  154. if (res.status === "success") {
  155. this.messages.clearAddSuccess(this.props.t("settings:successfullyUnlinkedGitHub"));
  156. } else {
  157. this.messages.addError(res.message);
  158. }
  159. });
  160. });
  161. };
  162. unlinkPassword = () => {
  163. this.messages.clearErrorSuccess();
  164. io.getSocket(socket => {
  165. socket.emit("users.unlinkPassword", res => {
  166. if (res.status === "success") {
  167. this.messages.clearAddSuccess(this.props.t("settings:successfullyUnlinkedPassword"));
  168. } else {
  169. this.messages.addError(res.message);
  170. }
  171. });
  172. });
  173. };
  174. linkButtons = () => {
  175. const newPassword = <CustomInput key="newPassword" type="password" name="newPassword" label={ this.props.t("general:newPasswordInput") } placeholder={ this.props.t("general:newPasswordInput") } onRef={ ref => (this.input.newPassword = ref) } />;
  176. const changePasswordButton = <button key="changePassword" onClick={ this.changePassword }>{ this.props.t("settings:changePassword") }</button>;
  177. const linkPassword = <NavLink key="linkPassword" className="button" to="/settings/setpassword" >{ this.props.t("settings:addAPasswordToAccount") }</NavLink>;
  178. const linkGitHub = <a key="linkGitHub" className="gray-button" href={ config.serverDomain + "/auth/github/link" }>{ this.props.t("settings:linkGitHubToAccount") }</a>;
  179. const unlinkGitHub = (
  180. <button key="unlinkGitHub" className="red-button" onClick={ this.unlinkGitHub }>
  181. { this.props.t("settings:removeLoggingInWithGitHub") }
  182. </button>
  183. );
  184. const unlinkPassword = (
  185. <button key="unlinkPassword" className="red-button" onClick={ this.unlinkPassword }>
  186. { this.props.t("settings:removeLoggingInWithPassword") }
  187. </button>
  188. );
  189. const toReturn = [];
  190. if (this.state.passwordLinked) {
  191. toReturn.push(newPassword);
  192. toReturn.push(changePasswordButton);
  193. }
  194. if (this.state.passwordLinked && this.state.gitHubLinked) {
  195. toReturn.push(unlinkGitHub);
  196. toReturn.push(unlinkPassword);
  197. } else if (!this.state.passwordLinked) {
  198. toReturn.push(linkPassword);
  199. } else toReturn.push(linkGitHub);
  200. return toReturn;
  201. };
  202. render() {
  203. const { t } = this.props;
  204. return (
  205. <main>
  206. <h1>{ t("settings:title") }</h1>
  207. <CustomMessages onRef={ ref => (this.messages = ref) } />
  208. <div className="sections">
  209. <div className="section general-section">
  210. <h2>{ this.props.t("settings:general") }</h2>
  211. <CustomInput type="email" name="email" label={ this.props.t("general:emailInput") } placeholder={ this.props.t("general:emailInput") } onRef={ ref => (this.input.email = ref) } />
  212. <CustomInput type="username" name="username" label={ this.props.t("general:usernameInput") } placeholder={ this.props.t("general:usernameInput") } onRef={ ref => (this.input.username = ref) } />
  213. <button onClick={ this.saveChanges }>{ this.props.t("settings:saveChanges") }</button>
  214. </div>
  215. <div className="section security-section">
  216. <h2>{ this.props.t("settings:security") }</h2>
  217. { this.linkButtons() }
  218. <button className="red-button" onClick={ this.logOutEverywhere }>{ this.props.t("settings:logOutEverywhere") }</button>
  219. </div>
  220. </div>
  221. </main>
  222. );
  223. }
  224. }