瀏覽代碼

Added radio buttons to CustomInput, added name, displayName, description, privacy changing on station settings and station deleting.

KrisVos130 7 年之前
父節點
當前提交
aaabe0487e

+ 2 - 2
backend/logic/actions/stations.js

@@ -575,10 +575,10 @@ module.exports = {
 		], (err) => {
 			if (err) {
 				err = utils.getError(err);
-				logger.error("STATIONS_UPDATE_DISPLAY_NAME", `Updating station "${stationId}" displayName to "${newName}" failed. "${err}"`);
+				logger.error("STATIONS_UPDATE_NAME", `Updating station "${stationId}" name to "${newName}" failed. "${err}"`);
 				return cb({'status': 'failure', 'message': err});
 			}
-			logger.success("STATIONS_UPDATE_DISPLAY_NAME", `Updated station "${stationId}" displayName to "${newName}" successfully.`);
+			logger.success("STATIONS_UPDATE_NAME", `Updated station "${stationId}" name to "${newName}" successfully.`);
 			return cb({'status': 'success', 'message': 'Successfully updated the name.'});
 		});
 	}),

+ 116 - 30
frontend/app/js/components/CustomInput.jsx

@@ -23,6 +23,7 @@ const dictionary = {
 		inputType: "text",
 		minLength: 2,
 		maxLength: 32,
+		isInput: true,
 		regex: regex.azAZ09_,
 		errors: {
 			format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
@@ -32,6 +33,7 @@ const dictionary = {
 		inputType: "email",
 		minLength: 3,
 		maxLength: 254,
+		isInput: true,
 		regex: regex.emailSimple,
 		errors: {
 			format: t("general:invalidEmailFormat"),
@@ -41,6 +43,7 @@ const dictionary = {
 		inputType: "password",
 		minLength: 6,
 		maxLength: 200,
+		isInput: true,
 		regex: regex.password,
 		errors: {
 			format: t("general:invalidPasswordFormat", { characters: "$@$!%*?&" }),
@@ -50,6 +53,7 @@ const dictionary = {
 		inputType: "text",
 		minLength: 8,
 		maxLength: 8,
+		isInput: true,
 		regex: regex.azAZ09,
 		errors: {
 			length: t("general:invalidCodeLength", { length: 8 }),
@@ -60,6 +64,7 @@ const dictionary = {
 		inputType: "text",
 		minLength: 2,
 		maxLength: 16,
+		isInput: true,
 		regex: regex.az09_,
 		errors: {
 			//format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
@@ -70,6 +75,7 @@ const dictionary = {
 		inputType: "text",
 		minLength: 2,
 		maxLength: 32,
+		isInput: true,
 		regex: regex.azAZ09_,
 		errors: {
 			//format: t("general:invalidUsernameFormat", { characters: `a-z, A-Z, 0-9${ t("general:and") } _` }),
@@ -83,11 +89,29 @@ const dictionary = {
 		isTextarea: true,
 		errors: {},
 	},
+	stationPrivacy: {
+		isRadio: true,
+		options: [
+			{
+				text: "Public - Lorem ipsum lorem ipsum lorem ipsum",
+				value: "public",
+			},
+			{
+				text: "Unlisted - Lorem ipsum lorem ipsum lorem ipsum",
+				value: "unlisted",
+			},
+			{
+				text: "Private - Lorem ipsum lorem ipsum lorem ipsum",
+				value: "private",
+			},
+		],
+	},
 };
 
 export default class CustomInput extends Component {
 	static propTypes = {
 		type: PropTypes.string,
+		original: PropTypes.string,
 		name: PropTypes.string,
 		label: PropTypes.string,
 		//showLabel: PropTypes.boolean,
@@ -97,6 +121,7 @@ export default class CustomInput extends Component {
 
 	static defaultProps = {
 		type: "",
+		original: "",
 		name: "",
 		label: "",
 		//showLabel: true,
@@ -138,8 +163,11 @@ export default class CustomInput extends Component {
 		this.state = {
 			inputType: dictionary[props.type].inputType,
 			isTextarea: (typeof dictionary[props.type].isTextarea === "boolean") ? dictionary[props.type].isTextarea : false,
+			isInput: (typeof dictionary[props.type].isInput === "boolean") ? dictionary[props.type].isInput : false,
+			isRadio: (typeof dictionary[props.type].isRadio === "boolean") ? dictionary[props.type].isRadio : false,
+			options: (typeof dictionary[props.type].options === "object") ? dictionary[props.type].options : null,
 			value: "",
-			original: "",
+			original: this.props.original,
 			errors: [],
 			pristine: true,
 			disabled: false,
@@ -176,7 +204,11 @@ export default class CustomInput extends Component {
 			value,
 		};
 		if (original) state.original = value;
-		this.setState(state);
+		this.setState(state, () => {
+			if (this.state.isRadio) {
+				this.validate();
+			}
+		});
 	};
 
 	getValue = () => {
@@ -212,36 +244,90 @@ export default class CustomInput extends Component {
 	};
 
 	validate = (cb = () => {}) => {
-		const errors = [];
-		const info = dictionary[this.props.type];
-		const value = this.state.value;
-		if (!isLength(value, info.minLength, info.maxLength)) errors.push((info.errors.length) ? info.errors.length : t("general:valueMustBeBetween", { min: info.minLength, max: info.maxLength }));
-		if (info.regex && !info.regex.test(value)) errors.push(info.errors.format);
-		this.setState({
-			errors,
-			valid: errors.length === 0,
-		}, cb);
+		if (!this.state.isRadio) {
+			const errors = [];
+			const info = dictionary[this.props.type];
+			const value = this.state.value;
+			if (!isLength(value, info.minLength, info.maxLength)) errors.push((info.errors.length) ? info.errors.length : t("general:valueMustBeBetween", {
+				min: info.minLength,
+				max: info.maxLength
+			}));
+			if (info.regex && !info.regex.test(value)) errors.push(info.errors.format);
+			this.setState({
+				errors,
+				valid: errors.length === 0,
+			}, cb);
+		} else {
+			this.setState({
+				valid: this.state.value !== null,
+			}, cb);
+		}
 	};
 
+	componentWillMount() {
+		if (this.props.original) {
+			this.setValue(this.props.original, true);
+		}
+	}
+
+	componentDidUpdate(prevProps, prevState) {
+		if (this.props.original !== prevProps.original) {
+			this.setValue(this.props.original, true);
+		}
+	}
+
 	render() {
-		const ElementType = (!this.state.isTextarea) ? "input" : "textarea";
-
-		return (
-			<label htmlFor={ this.props.name }>
-				{(this.props.showLabel) ? <span>{ this.props.label }</span> : null}
-				<ElementType
-					placeholder={ this.props.placeholder }
-					type={ this.state.inputType }
-					name={ this.props.name }
-					value={ this.state.value }
-					className={ (this.state.errors.length > 0) ? "has-validation-errors" : "" }
-					onBlur={ this.onBlur }
-					onFocus={ this.onFocus }
-					onChange={ this.onChange }
-					ref={ (input) => this.inputElement = input }
-				/>
-				{ this.listErrors() }
-			</label>
-		);
+		if (this.state.isTextarea || this.state.isInput) {
+			const ElementType = (this.state.isInput) ? "input" : "textarea";
+
+			return (
+				<label htmlFor={this.props.name}>
+					{(this.props.showLabel) ? <span>{this.props.label}</span> : null}
+					<ElementType
+						placeholder={this.props.placeholder}
+						type={this.state.inputType}
+						name={this.props.name}
+						value={this.state.value}
+						className={(this.state.errors.length > 0) ? "has-validation-errors" : ""}
+						onBlur={this.onBlur}
+						onFocus={this.onFocus}
+						onChange={this.onChange}
+						ref={(input) => this.inputElement = input}
+					/>
+					{this.listErrors()}
+				</label>
+			);
+		} else {
+			let optionsArr = this.state.options.map((option) => {
+				let checked = option.value === this.state.value;
+				return (
+					<label key={ option.value }>
+						<input type="radio" name={ this.props.type } value={ option.value } checked={ checked } onChange={ this.onChange }/>
+						<span>{ option.text }</span>
+					</label>
+				);
+			});
+
+			return (
+				<label htmlFor={this.props.name}>
+					{(this.props.showLabel) ? <span>{this.props.label}</span> : null}
+					<div>
+						{ optionsArr }
+						{/*<ElementType
+							placeholder={this.props.placeholder}
+							type={this.state.inputType}
+							name={this.props.name}
+							value={this.state.value}
+							className={(this.state.errors.length > 0) ? "has-validation-errors" : ""}
+							onBlur={this.onBlur}
+							onFocus={this.onFocus}
+							onChange={this.onChange}
+							ref={(input) => this.inputElement = input}
+						/>*/}
+					</div>
+					{this.listErrors()}
+				</label>
+			);
+		}
 	}
 }

+ 1 - 1
frontend/app/js/views/Station/Views/Overlays.jsx

@@ -21,7 +21,7 @@ export default class Overlays extends Component {
 
 	getComponent = (type, key) => {
 		if (type === "settings") {
-			return <Settings key={ key }/>;
+			return <Settings t={ this.props.t } key={ key }/>;
 		} else return null;
 	};
 

+ 106 - 3
frontend/app/js/views/Station/Views/Settings.jsx

@@ -1,23 +1,30 @@
 import React, { Component } from "react";
 import PropTypes from "prop-types";
 
+import CustomInput from "components/CustomInput.jsx";
+import CustomErrors from "components/CustomMessages.jsx";
+
 import { connect } from "react-redux";
 
 import { closeOverlay1 } from "actions/stationOverlay";
 
+import io from "io";
+
 @connect(state => ({
+	stationId: state.station.get("id"),
 	name: state.station.get("name"),
 	displayName: state.station.get("displayName"),
 	description: state.station.get("description"),
+	privacy: state.station.get("privacy"),
 }))
 export default class Settings extends Component {
 	constructor(props) {
 		super(props);
 
+		CustomInput.initialize(this);
+
 		this.state = {
-			name: props.name,
-			displayName: props.displayName,
-			description: props.description,
+			privacy: props.privacy,
 		};
 	}
 
@@ -25,11 +32,107 @@ export default class Settings extends Component {
 		this.props.dispatch(closeOverlay1());
 	};
 
+	changeName = () => {
+		this.messages.clearErrorSuccess();
+		if (CustomInput.hasInvalidInput(this.input, ["name"])) {
+			this.messages.clearAddError(this.props.t("general:someFieldsAreIncorrectError"));
+		} else {
+			io.getSocket(socket => {
+				socket.emit("stations.updateName", this.props.stationId, this.input.name.getValue(), res => {
+					if (res.status === "success") {
+						this.messages.clearAddSuccess("Successfully changed name.");
+					} else {
+						this.messages.addError(res.message);
+					}
+				});
+			});
+		}
+	};
+
+	changeDisplayName = () => {
+		this.messages.clearErrorSuccess();
+		if (CustomInput.hasInvalidInput(this.input, ["displayName"])) {
+			this.messages.clearAddError(this.props.t("general:someFieldsAreIncorrectError"));
+		} else {
+			io.getSocket(socket => {
+				socket.emit("stations.updateDisplayName", this.props.stationId, this.input.displayName.getValue(), res => {
+					if (res.status === "success") {
+						this.messages.clearAddSuccess("Successfully changed display name.");
+					} else {
+						this.messages.addError(res.message);
+					}
+				});
+			});
+		}
+	};
+
+	changeDescription = () => {
+		this.messages.clearErrorSuccess();
+		if (CustomInput.hasInvalidInput(this.input, ["description"])) {
+			this.messages.clearAddError(this.props.t("general:someFieldsAreIncorrectError"));
+		} else {
+			io.getSocket(socket => {
+				socket.emit("stations.updateDescription", this.props.stationId, this.input.description.getValue(), res => {
+					if (res.status === "success") {
+						this.messages.clearAddSuccess("Successfully changed description.");
+					} else {
+						this.messages.addError(res.message);
+					}
+				});
+			});
+		}
+	};
+
+	savePrivacy = () => {
+		this.messages.clearErrorSuccess();
+		if (CustomInput.hasInvalidInput(this.input, ["privacy"])) {
+			console.log(this.props);
+			this.messages.clearAddError(this.props.t("general:someFieldsAreIncorrectError"));
+		} else {
+			io.getSocket(socket => {
+				socket.emit("stations.updatePrivacy", this.props.stationId, this.input.privacy.getValue(), res => {
+					if (res.status === "success") {
+						this.messages.clearAddSuccess("Successfully saved privacy.");
+					} else {
+						this.messages.addError(res.message);
+					}
+				});
+			});
+		}
+	};
+
+	deleteStation = () => {
+		io.getSocket(socket => {
+			socket.emit('stations.remove', this.props.stationId, res => {
+				if (res.status === "success") {
+					this.messages.clearAddSuccess("Successfully deleted station.");
+				} else {
+					this.messages.addError(res.message);
+				}
+			});
+		});
+	};
+
 	render() {
 		return (
 			<div className="overlay">
 				<button onClick={ this.close }>Back</button>
 				<h1>Settings</h1>
+				<CustomErrors onRef={ ref => (this.messages = ref) } />
+
+				<CustomInput key="name" type="stationName" name="name" label="Station name" placeholder="Station name" original={ this.props.name } onRef={ ref => (this.input.name = ref) } />
+				<button onClick={ this.changeName }>Change name</button>
+
+				<CustomInput key="displayName" type="stationDisplayName" name="displayName" label="Station display name" placeholder="Station display name" original={ this.props.displayName } onRef={ ref => (this.input.displayName = ref) } />
+				<button onClick={ this.changeDisplayName }>Change display name</button>
+
+				<CustomInput key="description" type="stationDescription" name="description" label="Station description" placeholder="Station description" original={ this.props.description } onRef={ ref => (this.input.description = ref) } />
+				<button onClick={ this.changeDescription }>Change description</button>
+
+				<CustomInput key="privacy" type="stationPrivacy" name="privacy" label="Station privacy" placeholder="Station privacy" original={ this.state.privacy } onRef={ ref => (this.input.privacy = ref) } />
+				<button onClick={ this.savePrivacy }>Save privacy</button>
+
+				<button onClick={ this.deleteStation }>Delete station</button>
 			</div>
 		);
 	}

+ 1 - 1
frontend/app/js/views/Station/index.jsx

@@ -246,7 +246,7 @@ export default class Station extends Component {
 
 		return (
 			<main id="station">
-				<Overlays />
+				<Overlays t={ this.props.t } />
 
 				<button onClick={ () => { this.props.dispatch(openOverlay1("settings")) } }>Open settings</button>
 

+ 14 - 0
frontend/app/styles/main.scss

@@ -48,6 +48,7 @@ h1 {
 	display: none;
 }
 
+// Temp Start
 .liked {
 	color: green;
 }
@@ -56,6 +57,19 @@ h1 {
 	color: red;
 }
 
+.overlay {
+	display: block;
+	position: absolute;
+	top: 64px;
+	bottom: 0;
+	right: 0;
+	left: 0;
+	height: 100%;
+	width: 100%;
+	background-color: #03a9f4;
+	z-index: 2;
+}
+// Temp End
 main {
 	margin-left: auto;
 	margin-right: auto;