Selaa lähdekoodia

Worked on converting homepage to use Redux.

KrisVos130 7 vuotta sitten
vanhempi
sitoutus
e30c28aa43

+ 69 - 28
frontend/app/js/ducks/homepage.js

@@ -1,9 +1,25 @@
 import { Map, List } from "immutable";
 
+const STATION_INDEX = "HOMEPAGE::STATION_INDEX";
+const STATION_CREATE = "HOMEPAGE::STATION_CREATE";
 const STATION_REMOVE = "HOMEPAGE::STATION_REMOVE";
 const STATION_SONG_UPDATE = "HOMEPAGE::STATION_SONG_UPDATE";
 const STATION_USER_COUNT_UPDATE = "HOMEPAGE::STATION_USER_COUNT_UPDATE";
 
+function stationIndex(stations) {
+	return {
+		type: STATION_INDEX,
+		stations,
+	}
+}
+
+function stationCreate(station) {
+	return {
+		type: STATION_CREATE,
+		station,
+	}
+}
+
 function stationRemove(stationId) {
 	return {
 		type: STATION_REMOVE,
@@ -37,49 +53,74 @@ const initialState = Map({
 });
 
 function reducer(state = initialState, action) {
-	switch (action.type) {
-	case STATION_REMOVE:
-		const { stationId } = action;
-		const indexOfficial = state.getIn(["stations", "official"]).findIndex(function (station) {
-			return station.get("stationId") === stationId;
-		});
-		if (indexOfficial > -1) {
-			state.updateIn(["stations", "official"], (list) => {
-				list.delete(indexOfficial);
+	function updateStationById(stationId, cb) {
+		function byType(type) {
+			const index = state.getIn(["stations", type]).findIndex(function (station) {
+				return station.get("stationId") === stationId;
 			});
-		}
-		const indexCommunity = state.getIn(["stations", "community"]).findIndex(function (station) {
-			return station.get("stationId") === stationId;
-		});
-		if (indexCommunity > -1) {
-			state.updateIn(["stations", "community"], (list) => {
-				list.delete(indexCommunity);
+			if (index === -1) return null;
+			state = state.updateIn(["stations", type], (list) => {
+				cb(list, index);
 			});
 		}
+		byType("official");
+		byType("community");
+	}
+
+	const { stationId, stations, station } = action;
 
+	switch (action.type) {
+	case STATION_INDEX:
+		state.setIn(["stations", "official"], List([]));
+		state.setIn(["stations", "community"], List([]));
+		stations.forEach((station) => {
+			state = state.updateIn(["stations", station.type], function(list) {
+				return list.push(station);
+			});
+		});
+		return state;
+	case STATION_CREATE:
+		state = state.updateIn(["stations", station.type], (list) => {
+			return list.push(station);
+		});
+		return state;
+	case STATION_REMOVE:
+		updateStationById(stationId, (list, index) => {
+			list.delete(index);
+		});
 		return state;
 	case STATION_SONG_UPDATE:
-		/*return state.merge({
-			muted: true,
-		});*/
+		const { thumbnail } = action;
+		updateStationById(stationId, (list, index) => {
+			list.update(index, station => {
+				return station.set("thumbnail", thumbnail);
+			});
+		});
+		return state;
 	case STATION_USER_COUNT_UPDATE:
-		/*return state.merge({
-			muted: false,
-		});*/
+		const { userCount } = action;
+		updateStationById(stationId, (list, index) => {
+			list.update(index, station => {
+				return station.set("userCount", userCount);
+			});
+		});
+		return state;
 	}
 	return state;
 }
 
 const actionCreators = {
-	changeVolumeLoudness,
-	muteVolume,
-	unmuteVolume,
+	stationIndex,
+	stationCreate,
+	stationRemove,
+	stationSongUpdate,
+	stationUserCountUpdate,
 };
 
 const actionTypes = {
-	CHANGE_VOLUME_LOUDNESS,
-	MUTE_VOLUME,
-	UNMUTE_VOLUME,
+	STATION_REMOVE,
+	STATION_SONG_UPDATE,
+	STATION_USER_COUNT_UPDATE,
 };
 
 export {

+ 2 - 0
frontend/app/js/reducers/index.js

@@ -5,10 +5,12 @@ import songPlayer from "reducers/songPlayer";
 import station from "reducers/station";
 import stationOverlay from "reducers/stationOverlay";
 import playlistQueue from "reducers/playlistQueue";
+import homepage from "../ducks/homepage";
 import volume from "../ducks/volume";
 
 export default combineReducers({
 	volume,
+	homepage,
 	user,
 	songPlayer,
 	station,

+ 41 - 0
frontend/app/js/views/Home/StationCard.jsx

@@ -0,0 +1,41 @@
+import React, { Component } from "react";
+
+import { connect } from "react-redux";
+import { translate } from "react-i18next";
+
+@translate(["home"], { wait: true })
+export default class StationCard extends Component {
+	render() {
+		const { station, isOwner } = this.props;
+		let icon = null;
+		if (station.type === "official") {
+			if (station.privacy !== "public") icon =
+				<i className="material-icons" title={ this.props.t("home:thisStationIsNotVisible") }>lock</i>;
+		} else {
+			if (isOwner) icon =
+				<i className="material-icons" title={ this.props.t("home:thisIsYourStation") }>home</i>;
+			if (station.privacy !== "public") icon =
+				<i className="material-icons" title={ this.props.t("home:thisStationIsNotVisible") }>lock</i>;
+		}
+
+		return (
+			<div className="station-card">
+				<div className="station-media">
+					<img src={(station.currentSong) ? station.currentSong.thumbnail : ""}/>
+				</div>
+				<div className="station-body">
+					<h3 className="displayName">{station.displayName}</h3>
+					<p className="description">{station.description}</p>
+				</div>
+				<div className="station-footer">
+					<div className="user-count" title={ this.props.t("home:howManyOtherUsers") }>
+						<i className="material-icons">people</i>
+						<span>{station.userCount}</span>
+					</div>
+					{ icon }
+				</div>
+				<a href={station.type + "/" + station.name}/>
+			</div>
+		);
+	}
+}

+ 24 - 58
frontend/app/js/views/Home/index.jsx

@@ -7,6 +7,10 @@ import PropTypes from "prop-types";
 import { translate, Trans } from "react-i18next";
 
 import { connect } from "react-redux";
+import { actionCreators as homepageActionCreators } from "ducks/homepage";
+import { bindActionCreators } from "redux";
+
+import StationCard from "./StationCard";
 
 import io from "io";
 import config from "config";
@@ -17,8 +21,16 @@ import config from "config";
 		role: state.user.get("role"),
 	},
 	loggedIn: state.user.get("loggedIn"),
+	officialStations: state.homepage.getIn(["stations", "official"]),
+	communityStations: state.homepage.getIn(["stations", "community"]),
+}),
+(dispatch) => ({
+	onStationIndex: bindActionCreators(homepageActionCreators.stationIndex, dispatch),
+	onStationCreate: bindActionCreators(homepageActionCreators.stationCreate, dispatch),
+	onStationRemove: bindActionCreators(homepageActionCreators.stationRemove, dispatch),
+	onStationSongUpdate: bindActionCreators(homepageActionCreators.stationSongUpdate, dispatch),
+	onStationUserCountUpdate: bindActionCreators(homepageActionCreators.stationUserCountUpdate, dispatch),
 }))
-
 @translate(["home", "createCommunityStation"], { wait: true })
 export default class Homepage extends Component {
 	static propTypes = {
@@ -35,33 +47,15 @@ export default class Homepage extends Component {
 		CustomInput.initialize(this);
 
 		this.state = {
-			stations: {
-				official: [],
-				community: [],
-			},
 			createStation: {
 				private: false,
-			}
+			},
 		};
 
 		io.getSocket(socket => {
 			socket.emit("stations.index", data => {
 				if (data.status === "success") {
-					let community = [];
-					let official = [];
-					data.stations.forEach(station => {
-						if (!station.currentSong) station.currentSong = { thumbnail: '/assets/images/notes-transparent.png' };
-						if (station.currentSong && !station.currentSong.thumbnail) station.currentSong.thumbnail = "/assets/images/notes-transparent.png";
-						if (station.type === 'official') official.push(station);
-						else community.push(station);
-					});
-
-					this.setState({
-						stations: {
-							official,
-							community,
-						}
-					});
+					this.props.onStationIndex(data.stations);
 				}
 			});
 		});
@@ -76,47 +70,15 @@ export default class Homepage extends Component {
 		return false;
 	};
 
-	listStations = (type) => {
+	/*listStations = (type) => {
 		let stations = [];
 
 		this.state.stations[type].forEach((station) => {
-			let icon = null;
-			if (station.type === "official") {
-				if (station.privacy !== "public") icon =
-					<i className="material-icons" title={ this.props.t("home:thisStationIsNotVisible") }>lock</i>;
-			} else {
-				// TODO Add isOwner function globally
-				if (this.isOwner(station.ownerId)) icon =
-					<i className="material-icons" title={ this.props.t("home:thisIsYourStation") }>home</i>;
-				if (station.privacy !== "public") icon =
-					<i className="material-icons" title={ this.props.t("home:thisStationIsNotVisible") }>lock</i>;
-			}
-
-			stations.push(
-				(
-					<div key={station._id} className="station-card">
-						<div className="station-media">
-							<img src={station.currentSong.thumbnail}/>
-						</div>
-						<div className="station-body">
-							<h3 className="displayName">{station.displayName}</h3>
-							<p className="description">{station.description}</p>
-						</div>
-						<div className="station-footer">
-							<div className="user-count" title={ this.props.t("home:howManyOtherUsers") }>
-								<i className="material-icons">people</i>
-								<span>{station.userCount}</span>
-							</div>
-							{ icon }
-						</div>
-						<a href={station.type + "/" + station.name}/>
-					</div>
-				)
-			);
+			stations.push(<StationCard station={ station }/>);
 		});
 
 		return stations;
-	};
+	};*/
 
 	togglePrivate = () => {
 		this.setState({
@@ -160,7 +122,9 @@ export default class Homepage extends Component {
 				<CustomMessages onRef={ ref => (this.messages = ref) } />
 				<h2>{ t("home:officialStations") }</h2>
 				<div className="official-stations stations">
-					{ this.listStations("official") }
+					{ this.props.officialStations.map((station) => {
+						return <StationCard station={ station } />;
+					}) }
 				</div>
 				<h2>{ t("home:communityStations") }</h2>
 				<div className="community-stations stations">
@@ -185,7 +149,9 @@ export default class Homepage extends Component {
 							</div>
 						</div>
 					) : null }
-					{ this.listStations("community") }
+					{ this.props.communityStations.map((station) => {
+						return <StationCard station={ station } isOwner={ this.isOwner(station.ownerId) } key={ station.stationId }/>;
+					}) }
 				</div>
 			</main>
 		);