Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rewrite locations panel to react (first draft so far without using location service) #2614

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions modules/users/client/components/LocationTypeahead.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Typeahead } from 'react-bootstrap-typeahead';

export default function LocationTypeahead({
id,
name,
placeholder,
minLength,
initValue,
onInputChange,
}) {
const [suggestions, setSuggestions] = useState([initValue]);
const [selected, setSelected] = useState([initValue]);

const handleInputChange = async text => {
setSuggestions([text]); // TODO use location service here
onInputChange(text);
};

return (
<Typeahead
minLength={minLength}
onChange={s => setSelected(s)}
onInputChange={handleInputChange}
name={name}
type="text"
placeholder={placeholder || 'City, Country'}
id={id}
limit-location-types="true"
selected={selected}
options={suggestions}
/>
);
}

LocationTypeahead.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
placeholder: PropTypes.string,
minLength: PropTypes.number,
initValue: PropTypes.string.isRequired,
onInputChange: PropTypes.func.isRequired,
};
132 changes: 132 additions & 0 deletions modules/users/client/components/LocationsPanel.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import * as api from '@/modules/users/client/api/users.api';
import PropTypes from 'prop-types';
import LocationTypeahead from './LocationTypeahead';
import { $broadcast } from '@/modules/core/client/services/angular-compat';

export default function LocationsPanel({
username,
successMessageCallback,
errorMessageCallback,
}) {
const { t } = useTranslation('users');

const [initLocationFrom, setInitLocationFrom] = useState(null);
const [initLocationLiving, setInitLocationLiving] = useState(null);

const [newLocationFrom, setNewLocationFrom] = useState(null);
const [newLocationLiving, setNewLocationLiving] = useState(null);

const [unsavedModifications, setUnsavedModifications] = useState(false);

useEffect(() => {
api
.fetch(username)
.then(data => {
setInitLocationLiving(data.locationLiving);
setInitLocationFrom(data.locationFrom);
})
.catch(() => {
errorMessageCallback(null);
});
}, []);

const onSubmit = async event => {
event.preventDefault();
if (unsavedModifications) {
api
.update({
locationFrom: newLocationFrom || initLocationFrom,
locationLiving: newLocationLiving || initLocationLiving,
})
.then(() => {
setUnsavedModifications(false);
$broadcast('userUpdated');
successMessageCallback();
})
.catch(() => errorMessageCallback(null));
}
};

function onInputChangeLocationFrom(newValue) {
setNewLocationFrom(newValue);
updateUnsavedModificationsState(initLocationFrom !== newValue);
}

function onInputChangeLocationLiving(newValue) {
setNewLocationLiving(newValue);
updateUnsavedModificationsState(initLocationLiving !== newValue);
}

function updateUnsavedModificationsState(unsavedModificationsPresent) {
setUnsavedModifications(unsavedModificationsPresent);
const event = unsavedModificationsPresent ? 'userChanged' : 'userUpdated';
$broadcast(event);
}

return (
<form name="userFormLocation" onSubmit={onSubmit} autoComplete="off">
<div className="panel panel-default">
<div className="panel-heading">Locations</div>
<div className="panel-body">
<div className="form-horizontal">
<div className="form-group">
<label
htmlFor="location-living"
className="col-sm-3 text-right control-label"
>
{t('Where do you live')}
</label>
<div className="col-sm-9 col-md-7 col-lg-6">
<LocationTypeahead
id="location-living"
name="locationLiving"
initValue={initLocationLiving || ''}
onInputChange={onInputChangeLocationLiving}
key={initLocationLiving}
/>
</div>
</div>

<div className="form-group">
<label
htmlFor="location-from"
className="col-sm-3 text-right control-label"
>
{t('Where are you from')}
</label>
<div className="col-sm-9 col-md-7 col-lg-6">
<LocationTypeahead
id="location-from"
name="locationFrom"
initValue={initLocationFrom || ''}
onInputChange={onInputChangeLocationFrom}
key={initLocationFrom}
/>
</div>
</div>
</div>
</div>
</div>

<p>
<button
type="submit"
className="btn btn-lg btn-primary profile-editor-save"
disabled={!unsavedModifications}
>
{t('Save')}
</button>
<br />
<br />
</p>
</form>
);
}

LocationsPanel.propTypes = {
username: PropTypes.string.isRequired,
successMessageCallback: PropTypes.func.isRequired,
errorMessageCallback: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ function ProfileEditLocationsController(
// ViewModel
const vm = this;

vm.addSuccessMessage = () => {
messageCenterService.add('success', 'Profile updated.');
};

vm.addErrorMessage = message => {
messageCenterService.add(
'danger',
message || 'Something went wrong. Please try again!',
{ timeout: 10000 },
);
};

vm.messageCenterService = messageCenterService;

// Copy user to make a temporary buffer for changes.
// Prevents changes remaining here when cancelling profile editing.
vm.user = new Users(Authentication.user);
Expand All @@ -28,14 +42,10 @@ function ProfileEditLocationsController(
function (response) {
Authentication.user = response;
$scope.$emit('userUpdated');
messageCenterService.add('success', 'Profile updated.');
vm.addSuccessMessage();
},
function (response) {
messageCenterService.add(
'danger',
response.data.message || 'Something went wrong. Please try again!',
{ timeout: 10000 },
);
vm.addErrorMessage(response.data.message);
},
);
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,67 +1,7 @@
<form
name="userFormLocation"
ng-submit="profileEditLocations.updateUserProfile(userFormLocation.$valid)"
autocomplete="off"
tr-confirm-exit
<locations-panel
username="app.user.username"
successMessageCallback="profileEditLocations.addSuccessMessage"
errorMessageCallback="profileEditLocations.addErrorMessage"
>
<div class="panel panel-default">
<div class="panel-heading">Locations</div>
<div class="panel-body">
<div class="form-horizontal">
<div class="form-group">
<label
for="location-living"
class="col-sm-3 text-right control-label"
>
Where do you live
</label>
<div class="col-sm-9 col-md-7 col-lg-6">
<input
type="text"
class="form-control"
placeholder="City, Country"
id="location-living"
limit-location-types="true"
tr-location
tr-location-center="profileEditLocations.locationLivingCenter"
ng-model="profileEditLocations.user.locationLiving"
ng-change="profileEdit.unsavedModifications = true"
/>
</div>
</div>

<div class="form-group">
<label for="location-from" class="col-sm-3 text-right control-label">
Where are you from
</label>
<div class="col-sm-9 col-md-7 col-lg-6">
<input
type="text"
class="form-control"
placeholder="City, Country"
id="location-from"
limit-location-types="true"
tr-location
tr-location-center="profileEditLocations.locationFromCenter"
ng-model="profileEditLocations.user.locationFrom"
ng-change="profileEdit.unsavedModifications = true"
/>
</div>
</div>
</div>
</div>
</div>

<p>
<button
type="submit"
class="btn btn-lg btn-primary profile-editor-save"
ng-disabled="!profileEdit.unsavedModifications"
>
Save
</button>
<br /><br />
</p>
</form>

</locations-panel>
<hosting-and-meet-panel></hosting-and-meet-panel>
Loading