Skip to content

Commit

Permalink
Fixed IP Detection on Team Selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
mwilkinson22 committed Feb 13, 2024
1 parent af0f851 commit c080e23
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 90 deletions.
9 changes: 8 additions & 1 deletion src/client/actions/teamSelectorActions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FETCH_TEAM_SELECTOR, FETCH_TEAM_SELECTOR_LIST, DELETE_TEAM_SELECTOR } from "./types";
import { FETCH_TEAM_SELECTOR, FETCH_TEAM_SELECTOR_LIST, FETCH_TEAM_SELECTOR_CHOICES, DELETE_TEAM_SELECTOR } from "./types";
import { toast } from "react-toastify";

export const fetchTeamSelector = id => async (dispatch, getState, api) => {
Expand All @@ -8,6 +8,13 @@ export const fetchTeamSelector = id => async (dispatch, getState, api) => {
}
};

export const fetchTeamSelectorChoices = id => async (dispatch, getState, api) => {
const res = await api.get(`/teamSelectors/${id}/choices`);
if (res.data) {
dispatch({ type: FETCH_TEAM_SELECTOR_CHOICES, payload: { id, choices: res.data } });
}
};

export const fetchTeamSelectorForGame = id => async (dispatch, getState, api) => {
const res = await api.get(`/teamSelectors/game/${id}`);
dispatch({ type: FETCH_TEAM_SELECTOR, payload: res.data });
Expand Down
1 change: 1 addition & 0 deletions src/client/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export const SEND_ERROR = "send_error";
//Team Selectors
export const FETCH_TEAM_SELECTOR_LIST = "fetch_team_selector_list";
export const FETCH_TEAM_SELECTOR = "fetch_team_selector";
export const FETCH_TEAM_SELECTOR_CHOICES = "fetch_team_selector_choices";
export const DELETE_TEAM_SELECTOR = "delete_team_selector";

//OAuth
Expand Down
68 changes: 18 additions & 50 deletions src/client/components/social/ShareDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,7 @@ class ShareDialog extends Component {
}

renderDialog() {
const {
authorisedAccounts,
fetchingPreview,
images,
isSubmitting,
service,
services,
submittedPost
} = this.state;
const { authorisedAccounts, fetchingPreview, images, isSubmitting, service, services, submittedPost } = this.state;

if (service) {
let content;
Expand All @@ -153,11 +145,7 @@ class ShareDialog extends Component {
content = (
<div>
{this.renderImagePreview()}
<button
className="navigator-share-btn"
type="button"
onClick={() => this.shareViaNavigator()}
>
<button className="navigator-share-btn" type="button" onClick={() => this.shareViaNavigator()}>
Share
</button>
</div>
Expand All @@ -176,12 +164,7 @@ class ShareDialog extends Component {
let url, link;
switch (service) {
case "twitter": {
url = [
"https://twitter.com",
authorisedAccounts[service].screen_name,
"status",
submittedPost.id
].join("/");
url = ["https://twitter.com", authorisedAccounts[service].screen_name, "status", submittedPost.id].join("/");
}
}

Expand Down Expand Up @@ -245,13 +228,12 @@ class ShareDialog extends Component {
includeDisclaimer = true;
content = [
<p key="1">
To share to Twitter, you will need to click the button below and authorise posts from this
website. Granting authorisation simply allows <strong>you</strong> to post to your own account
from this website.
To share to Twitter, you will need to click the button below and authorise posts from this website. Granting authorisation simply allows{" "}
<strong>you</strong> to post to your own account from this website.
</p>,
<p key="2">
No personal data is ever saved by us, and your password is never exposed by Twitter, meaning it
is impossible for us (or anyone else) to access your account or post without your consent.
No personal data is ever saved by us, and your password is never exposed by Twitter, meaning it is impossible for us (or anyone else) to
access your account or post without your consent.
</p>
];
break;
Expand Down Expand Up @@ -297,25 +279,18 @@ class ShareDialog extends Component {
<div>
<h6>Sharing to Twitter</h6>
<p>
Upon clicking the Share button, a Twitter window will pop up asking you to grant access
to {site_name} (the website, not the page/group). If you accept, an access token will be
saved to your web browser.
Upon clicking the Share button, a Twitter window will pop up asking you to grant access to {site_name} (the website, not the
page/group). If you accept, an access token will be saved to your web browser.
</p>
<p>
This access token is a long string of letters and numbers, generated by Twitter to allow
other apps to post to your account without accessing twitter.com directly. It is
accessible only within your web browser, so at no point can this be accessed or used by
the 4Fs team (or anyone else).
This access token is a long string of letters and numbers, generated by Twitter to allow other apps to post to your account
without accessing twitter.com directly. It is accessible only within your web browser, so at no point can this be accessed or
used by the 4Fs team (or anyone else).
</p>
<p>
The access token can easily be deleted, if you wish, either by clicking the{" "}
<strong>Disconnect</strong> link above the Tweet editor, or by revoking it directly on
Twitter. If you have any more questions,{" "}
<a
href={`https://twitter.com/${site_social}`}
target="_blank"
rel="noopener noreferrer"
>
The access token can easily be deleted, if you wish, either by clicking the <strong>Disconnect</strong> link above the Tweet
editor, or by revoking it directly on Twitter. If you have any more questions,{" "}
<a href={`https://twitter.com/${site_social}`} target="_blank" rel="noopener noreferrer">
just let us know
</a>
</p>
Expand All @@ -325,9 +300,7 @@ class ShareDialog extends Component {
}

if (content) {
return (
<PopUpDialog onDestroy={() => this.setState({ isShowingDisclaimer: false })}>{content}</PopUpDialog>
);
return <PopUpDialog onDestroy={() => this.setState({ isShowingDisclaimer: false })}>{content}</PopUpDialog>;
}
}
}
Expand All @@ -353,9 +326,7 @@ class ShareDialog extends Component {
//Firefox disables twitter images by default via its
//tracker. Easier to just hide it
if (browser !== "Firefox") {
userImage = (
<img src={account.profile_image_url_https} className="profile-pic" alt="Profile Picture" />
);
userImage = <img src={account.profile_image_url_https} className="profile-pic" alt="Profile Picture" />;
}
userInfo = (
<div className="user-info twitter">
Expand Down Expand Up @@ -383,12 +354,9 @@ class ShareDialog extends Component {

renderImagePreview() {
const { onFetchImage } = this.props;
console.log("HELLO");
const { images, fetchingPreview } = this.state;
if (images.length) {
const list = images.map((src, i) => (
<img key={i} src={src} onClick={() => window.open(src)} alt="Preview" />
));
const list = images.map((src, i) => <img key={i} src={src} onClick={() => window.open(src)} alt="Preview" />);
return <div className="image-previews">{list}</div>;
} else if (fetchingPreview) {
return <LoadingPage />;
Expand Down
54 changes: 33 additions & 21 deletions src/client/components/teamselectors/ShareableTeamSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import ShareDialog from "../social/ShareDialog";
import LoadingPage from "../LoadingPage";

//Actions
import { fetchPreviewImage, saveTeamSelectorChoices, shareTeamSelector } from "~/client/actions/teamSelectorActions";
import { fetchPreviewImage, saveTeamSelectorChoices, shareTeamSelector, fetchTeamSelectorChoices } from "~/client/actions/teamSelectorActions";
import { fetchTeam } from "~/client/actions/teamsActions";

class ShareableTeamSelector extends Component {
Expand All @@ -21,7 +21,7 @@ class ShareableTeamSelector extends Component {
}

static getDerivedStateFromProps(nextProps, prevState) {
const { fetchTeam, fullTeams, selector } = nextProps;
const { fetchTeam, fullTeams, selector, fetchTeamSelectorChoices } = nextProps;
const newState = { selector };

//Ensure we have all dependencies
Expand All @@ -48,14 +48,19 @@ class ShareableTeamSelector extends Component {
//from the selector, and then revisits the page), we
//enforce edit mode
const { activeUserChoices, players } = newState.selector;
if (activeUserChoices) {
if (activeUserChoices != null) {
const missingPlayers = _.difference(
activeUserChoices,
players.map(p => p._id)
);

if (missingPlayers.length) {
newState.editMode = true;
newState.enforceEditMode = true;
}
newState.isLoadingChoices = false;
} else if (!prevState.isLoadingChoices) {
fetchTeamSelectorChoices(newState.selector._id);
newState.isLoadingChoices = true;
}

return newState;
Expand All @@ -67,30 +72,35 @@ class ShareableTeamSelector extends Component {

saveTeamSelectorChoices(selector._id, values);

this.setState({ editMode: false });
this.setState({ enforceEditMode: false });
}

selectionIsComplete() {
const { enforceEditMode, selector } = this.state;
return !enforceEditMode && selector.activeUserChoices != null && selector.activeUserChoices.length > 0;
}

renderSecondColumn() {
const { baseUrl, fetchPreviewImage, shareTeamSelector, site_social, urlFormatter } = this.props;
const { editMode, selector } = this.state;
if (this.selectionIsComplete()) {
const { baseUrl, fetchPreviewImage, shareTeamSelector, site_social, urlFormatter } = this.props;
const { selector } = this.state;

//Get Initial Share Values
let initialContent = selector.defaultSocialText || "";
//Get Initial Share Values
let initialContent = selector.defaultSocialText || "";

//Get Tokens
const url = `${baseUrl}/${urlFormatter(selector)}`;
//Get Tokens
const url = `${baseUrl}/${urlFormatter(selector)}`;

//Replace tokens
initialContent = initialContent.replace(/{url}/gi, url).replace(/@*{site_social}/gi, "@" + site_social);
//Replace tokens
initialContent = initialContent.replace(/{url}/gi, url).replace(/@*{site_social}/gi, "@" + site_social);

if (selector.activeUserChoices && !editMode) {
return (
<div>
<div className="confirmation">
<p>Thank you, your choices have been saved!</p>
<p>
{"Want to make a change? "}
<span className="pseudo-link" onClick={() => this.setState({ editMode: true })}>
<span className="pseudo-link" onClick={() => this.setState({ enforceEditMode: true })}>
Click Here
</span>
{" to edit your team"}
Expand All @@ -109,9 +119,9 @@ class ShareableTeamSelector extends Component {

render() {
const { fullTeams, localTeam } = this.props;
const { editMode, isLoadingTeam, selector, squadNumbers } = this.state;
const { isLoadingTeam, isLoadingChoices, selector, squadNumbers } = this.state;

if (isLoadingTeam) {
if (isLoadingTeam || isLoadingChoices) {
return <LoadingPage />;
}

Expand All @@ -129,7 +139,7 @@ class ShareableTeamSelector extends Component {

//Get current squad
let currentSquad = {};
if (selector.activeUserChoices) {
if (selector.activeUserChoices && selector.activeUserChoices.length) {
currentSquad = _.chain(selector.activeUserChoices)
.map((_player, i) => {
//Get Position
Expand All @@ -156,7 +166,7 @@ class ShareableTeamSelector extends Component {
players={players}
requireFullTeam={true}
submitText="Save Choices"
readOnly={selector.activeUserChoices && !editMode}
readOnly={this.selectionIsComplete()}
team={fullTeams[localTeam]}
usesExtraInterchange={selector.usesExtraInterchange}
/>
Expand All @@ -175,14 +185,16 @@ ShareableTeamSelector.defaultProps = {
urlFormatter: s => `team-selectors/${s.slug}`
};

function mapStateToProps({ config, teams }) {
function mapStateToProps({ config, teams, teamSelectors }) {
const { baseUrl, localTeam, site_social } = config;
const { fullTeams } = teams;
return { baseUrl, fullTeams, localTeam, site_social };
const { selectors } = teamSelectors;
return { baseUrl, fullTeams, localTeam, site_social, selectors };
}

export default connect(mapStateToProps, {
fetchPreviewImage,
fetchTeamSelectorChoices,
saveTeamSelectorChoices,
shareTeamSelector,
fetchTeam
Expand Down
16 changes: 14 additions & 2 deletions src/client/reducers/teamSelectorReducer.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import _ from "lodash";
import { FETCH_TEAM_SELECTOR, FETCH_TEAM_SELECTOR_LIST, DELETE_TEAM_SELECTOR } from "../actions/types";
import { FETCH_TEAM_SELECTOR, FETCH_TEAM_SELECTOR_LIST, DELETE_TEAM_SELECTOR, FETCH_TEAM_SELECTOR_CHOICES } from "../actions/types";

import { teamSelectors as listProperties } from "~/constants/listProperties";

export default function (state = { selectors: {}, haveLoadedAll: false }, action) {
export default function(state = { selectors: {}, haveLoadedAll: false }, action) {
switch (action.type) {
case FETCH_TEAM_SELECTOR: {
return {
Expand All @@ -18,6 +18,18 @@ export default function (state = { selectors: {}, haveLoadedAll: false }, action
}
};
}
case FETCH_TEAM_SELECTOR_CHOICES: {
return {
...state,
selectors: {
...state.selectors,
[action.payload.id]: {
...state.selectors[action.payload.id],
activeUserChoices: action.payload.choices
}
}
};
}

case FETCH_TEAM_SELECTOR_LIST: {
return {
Expand Down
29 changes: 14 additions & 15 deletions src/controllers/teamSelectorController.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,7 @@ export async function getTeamSelector(req, res) {
const selector = await fetchTeamSelector(_id, res);

if (selector) {
//Set choices for active user
const activeUserChoices = selector.choices.find(({ ip }) => ip == req.ipAddress);
if (activeUserChoices) {
selector.activeUserChoices = activeUserChoices.squad;
}

//Remove other choices for non-admins
//Remove choices for non-admins
if (!req.user || !req.user.isAdmin) {
delete selector.choices;
}
Expand All @@ -74,6 +68,17 @@ export async function getTeamSelector(req, res) {
}
}

export async function getTeamSelectorChoicesByIp(req, res) {
// We do this separately to the main call as IP detection just doesn't work with SSR.
const { _id } = req.params;

const selector = await fetchTeamSelector(_id, res);
if (selector) {
const activeUserChoices = selector.choices.find(({ ip }) => ip == req.ipAddress);
res.send(activeUserChoices ? activeUserChoices.squad : []);
}
}

export async function getPreviewImage(req, res) {
const { _id } = req.params;
const selector = await fetchTeamSelector(_id, res);
Expand Down Expand Up @@ -190,10 +195,7 @@ export async function submitUserChoices(req, res) {
const squad = _.values(req.body);

if (currentVote) {
await TeamSelector.updateOne(
{ _id, "choices._id": currentVote._id },
{ $set: { "choices.$.squad": squad } }
);
await TeamSelector.updateOne({ _id, "choices._id": currentVote._id }, { $set: { "choices.$.squad": squad } });
} else {
selector.choices.push({ ip: ipAddress, squad });
await selector.save();
Expand Down Expand Up @@ -243,10 +245,7 @@ export async function generateImage(req, res, selector) {
const { squad } = activeUserChoices;

//Get main player info
const playerData = await Person.find(
{ _id: { $in: squad } },
"name images nickname displayNicknameInCanvases squadNameWhenDuplicate gender"
).lean();
const playerData = await Person.find({ _id: { $in: squad } }, "name images nickname displayNicknameInCanvases squadNameWhenDuplicate gender").lean();

//Get squad numbers
let squadNumbers = [];
Expand Down
2 changes: 1 addition & 1 deletion src/images/SquadImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ export default class SquadImage extends Canvas {
}

this.textBuilder(bannerText, sideBarWidth * 0.5, bannerY, {
lineHeight: 2.8
lineHeight: 2.9
});

//Interchanges Header, for normal interchange display
Expand Down
Loading

0 comments on commit c080e23

Please sign in to comment.