Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Commit

Permalink
Merge pull request #676 from OpenBazaar/follow-refactor
Browse files Browse the repository at this point in the history
Follow refactor
  • Loading branch information
jjeffryes authored Aug 18, 2017
2 parents d3d1f5e + 2700fea commit 42108a2
Show file tree
Hide file tree
Showing 19 changed files with 615 additions and 266 deletions.
52 changes: 29 additions & 23 deletions js/collections/Followers.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
import { Collection } from 'backbone';
import UserShort from '../models/UserCard';
/* Used for lists of followers and following */

import app from '../app';
import { Collection } from 'backbone';
import Follower from '../models/Follower';

export default class extends Collection {
constructor(models = [], options = {}) {
super(models, options);

module.exports = Collection.extend({
/* we have to use the older style for this collection, the ES6 style creates a bug where models
cannot be removed using their ids */
const types = ['followers', 'following'];
if (types.indexOf(options.type) === -1) {
throw new Error(`Please provide a type as one of ${types.join(', ')}`);
}

initialize(models, options) {
if (!options.type) {
throw new Error('You must provide a type to the collection');
if (!options.peerId) {
throw new Error('Please provide a peerId');
}

this.guid = options.guid;
this.type = options.type;
},
this.options = options;
}

url() {
return app.getServerUrl(this.guid === app.profile.id || !this.guid ?
`ob/${this.type}` : `ipns/${this.guid}/${this.type}`);
},
model(attrs, options) {
return new Follower(attrs, options);
}

modelId(attrs) {
return attrs.peerId;
}

model: UserShort,
url() {
return app.getServerUrl(`ob/${this.options.type === 'followers' ? 'followers' : 'following'}` +
`${app.profile.id === this.options.peerId ? '' : `/${this.options.peerId}`}`);
}

parse(response) {
return response.map((guid) => {
// if a plain guid was passed in, convert it to an object
if (typeof guid === 'string') return { guid };
return guid;
});
},
});
return response.map(peerId => ({ peerId }));
}
}
22 changes: 14 additions & 8 deletions js/languages/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
"statusPublishFailed": "Publishing failed. %{retryLink}",
"statusPublishComplete": "Publishing complete."
},
"follow": {
"typeFollow": "following",
"typeUnfollow": "unfollowing",
"followErrorTitle": "There was an error %{type} %{user}."
},
"restartNow": "Restart Now",
"restartLater": "Restart Later",
"langChangeRestartTitle": "Restart needed for language change",
Expand Down Expand Up @@ -162,10 +167,15 @@
"website": "Website",
"email": "Email",
"noLocation": "No Location",
"noFollowers": "No One is Following %{name} Yet",
"noFollowing": "%{name} is Not Following Anyone Yet",
"noOwnFollowers": "You Don't Have Any Followers Yet",
"noOwnFollowing": "You Aren't Following Anyone Yet",
"followTab": {
"noFollowers": "No One is Following %{name} Yet",
"noFollowing": "%{name} is Not Following Anyone Yet",
"noOwnFollowers": "You Don't Have Any Followers Yet",
"noOwnFollowing": "You Aren't Following Anyone Yet",
"followersFetchError": "Unable to fetch the followers list.",
"followingFetchError": "Unable to fetch the following list.",
"btnRetry": "Retry"
},
"getFollowingError": "There was an error when determining if this user follows you.",
"modAddError": "There was an error adding this moderator. \n %{errMsg}",
"modRemoveError": "There was an error removing this moderator. \n %{errMsg}",
Expand Down Expand Up @@ -1408,10 +1418,6 @@
"provideValidPortRange": "Please provide a number between 0 and 65535.",
"invalidTorProxy": "The value does not appear to be in the right format. It should be in the format ip-address:port, e.g. 127.0.0.1:9150. The port must be a number between 0 and 65535."
},
"errors": {
"saveError": "There was an error saving your data.",
"badResult": "The server returned an error."
},
"shippingAddressModelErrors": {
"provideName": "Please provide a name.",
"provideCountry": "Please provide a country."
Expand Down
6 changes: 3 additions & 3 deletions js/models/UserCard.js → js/models/Follower.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* Used as a list item of both follower and following lists */

import BaseModel from './BaseModel';

export default class extends BaseModel {
// this model will only be { guid: exampleguid }

get idAttribute() {
return 'guid';
return 'peerId';
}
}

27 changes: 7 additions & 20 deletions js/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,10 @@ let walletBalanceFetch;
let walletBalanceFetchFailed;

function fetchStartupData() {
ownFollowingFetch = !ownFollowingFetch || ownFollowingFetch ?
ownFollowingFetch = !ownFollowingFetch || ownFollowingFailed ?
app.ownFollowing.fetch() : ownFollowingFetch;
exchangeRatesFetch = exchangeRatesFetch || fetchExchangeRates();
walletBalanceFetch = !walletBalanceFetch || walletBalanceFetch ?
walletBalanceFetch = !walletBalanceFetch || walletBalanceFetchFailed ?
app.walletBalance.fetch() : walletBalanceFetch;

$.whenAll(ownFollowingFetch, exchangeRatesFetch, walletBalanceFetch)
Expand Down Expand Up @@ -344,7 +344,7 @@ function onboardIfNeeded() {
// let's go onboard
onboard().done(() => onboardIfNeededDeferred.resolve());
} else {
fetchStartupData().done(() => onboardIfNeededDeferred.resolve());
onboardIfNeededDeferred.resolve();
}
});

Expand Down Expand Up @@ -383,8 +383,10 @@ function start() {
app.localSettings.save('language', getValidLanguage(lang));
});

app.ownFollowing = new Followers(null, { type: 'following' });
app.ownFollowers = new Followers(null, { type: 'followers' });
app.ownFollowing = new Followers([], {
type: 'following',
peerId: app.profile.id,
});

app.walletBalance = new WalletBalance();

Expand Down Expand Up @@ -759,20 +761,5 @@ ipcRenderer.on('close-attempt', (e) => {
}
});

// update ownFollowers based on follow socket communication
serverConnectEvents.on('connected', (connectedEvent) => {
connectedEvent.socket.on('message', (e) => {
if (e.jsonData) {
if (e.jsonData.notification) {
if (e.jsonData.notification.type === 'follow') {
app.ownFollowers.unshift({ guid: e.jsonData.notification.peerId });
} else if (e.jsonData.notification.type === 'unfollow') {
app.ownFollowers.remove(e.jsonData.notification.peerId); // remove by id
}
}
}
});
});

// initialize our listing delete handler
listingDeleteHandler();
6 changes: 4 additions & 2 deletions js/templates/connectedPeersPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ <h1 class="flexExpand"><%= ob.polyT('connectedPeersPage.heading') %></h1>
<div class="tx4 border clrBr clrP pad"><%= ob.polyT('connectedPeersPage.totalPeers', { totalPeers: ob.peers.length }) %></div>
</div>
</div>
<div class="userPageFollow flexRow js-peerWrapper"></div>
<div class="userPageFollow">
<div class="userCardsContainer flexRow js-peerWrapper"></div>
</div>

<div class="js-morePeers hide">
<hr class="clrBr">
<a class="btn clrBr clrP js-morePeersBtn">Load More</a>
</div>
</div>
</div>
2 changes: 2 additions & 0 deletions js/templates/userPage/follow.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<div class="js-userCardsContainer userCardsContainer flexRow"></div>
<div class="js-followLoadingContainer followLoadingContainer"></div>
13 changes: 13 additions & 0 deletions js/templates/userPage/followLoading.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<% if (ob.isFetching) { %>
<div class="loadingSpinnerWrap">
<% print(ob.spinner({ className: 'spinnerMd' })) %>
</div>
<% } else if (ob.fetchFailed) { %>
<p><%= ob.fetchErrorTitle %></p>
<% if (ob.fetchErrorMsg) { %>
<p><%= ob.fetchErrorMsg %></p>
<% } %>
<button class="btn normalBtn clrP clrBr js-retry"><%= ob.polyT('userPage.followTab.btnRetry') %></button>
<% } else if (ob.noResults) { %>
<p><%= ob.noResultsMsg %></p>
<% } %>
4 changes: 2 additions & 2 deletions js/templates/userPage/userPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<% if (ob.vendor || ob.ownPage) { %> <% // the store tab is only visible to the user if they have vendor set to false %>
<a class="btn tab clrBr js-tab" data-tab="store">Store<span class="clrTEmph1 margLSm js-listingsCount"><%= ob.stats.listingCount %></span></a>
<% } %>
<a class="btn tab clrBr js-tab" data-tab="following">Following<span class="clrTEmph1 margLSm js-followingCount"><%= ob.stats.followingCount %></span></a>
<a class="btn tab clrBr js-tab" data-tab="followers">Followers<span class="clrTEmph1 margLSm js-followerCount"><%= ob.stats.followerCount %></span></a>
<a class="btn tab clrBr js-tab" data-tab="following">Following<span class="clrTEmph1 margLSm js-followingCount"><%= ob.followingCount %></span></a>
<a class="btn tab clrBr js-tab" data-tab="followers">Followers<span class="clrTEmph1 margLSm js-followerCount"><%= ob.followerCount %></span></a>
</div>
</div>
</div>
Expand Down
19 changes: 14 additions & 5 deletions js/utils/follow.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import $ from 'jquery';
import { capitalize } from '../utils/string';
import app from '../app';
import Dialog from '../views/modals/Dialog';

Expand All @@ -19,6 +20,11 @@ export function followUnfollow(guid, type = 'follow') {
throw new Error('You must provide a valid guid.');
}

const types = ['follow', 'unfollow'];
if (types.indexOf(type) === -1) {
throw new Error(`type must be one of ${types.join(', ')}`);
}

if (guid === app.profile.id) {
throw new Error('You can not follow or unfollow your own guid');
}
Expand All @@ -32,15 +38,18 @@ export function followUnfollow(guid, type = 'follow') {
.done(() => {
// if the call succeeds, add or remove the guid from the collection
if (type === 'follow') {
app.ownFollowing.unshift({ guid });
app.ownFollowing.unshift({ peerId: guid });
} else {
app.ownFollowing.remove(guid); // remove via id
app.ownFollowing.remove(guid);
}
})
.fail((data) => {
.fail(data => {
new Dialog({
title: app.polyglot.t('errors.badResult'),
message: data.responseJSON.reason,
title: app.polyglot.t('follow.followErrorTitle', {
type: app.polyglot.t(`follow.type${capitalize(type)}`),
user: guid,
}),
message: data.responseJSON && data.responseJSON.reason || '',
})
.render()
.open();
Expand Down
33 changes: 15 additions & 18 deletions js/views/UserCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import BaseVw from './baseVw';
import loadTemplate from '../utils/loadTemplate';
import app from '../app';
import { followedByYou, followUnfollow } from '../utils/follow';
import Profile from '../models/profile/Profile';
import UserCard from '../models/UserCard';
import Profile, { getCachedProfiles } from '../models/profile/Profile';
import { launchModeratorDetailsModal } from '../utils/modalManager';
import { openSimpleMessage } from './modals/SimpleMessage';

Expand All @@ -17,11 +16,7 @@ export default class extends BaseVw {
if (this.model instanceof Profile) {
this.guid = this.model.id;
this.fetched = true;
} else if (this.model) {
this.guid = this.model.get('guid');
this.fetched = false;
} else {
this.model = new UserCard({ guid: options.guid });
this.guid = options.guid;
this.fetched = false;
}
Expand Down Expand Up @@ -49,10 +44,14 @@ export default class extends BaseVw {
this.$modBtn.attr('data-tip', this.getModTip());
});

this.listenTo(app.ownFollowing, 'sync update', () => {
this.followedByYou = followedByYou(this.guid);
this.$followBtn.toggleClass('active', this.followedByYou);
this.$followBtn.attr('data-tip', this.getFollowTip());
this.listenTo(app.ownFollowing, 'update', (cl, updateOpts) => {
const updatedModels = updateOpts.changes.added.concat(updateOpts.changes.removed);

if (updatedModels.filter(md => md.id === this.guid).length) {
this.followedByYou = followedByYou(this.guid);
this.$followBtn.toggleClass('active', this.followedByYou);
this.$followBtn.attr('data-tip', this.getFollowTip());
}
});
}

Expand All @@ -73,19 +72,16 @@ export default class extends BaseVw {
}

loadUser(guid = this.guid) {
let profile;
this.fetched = true;

if (guid === app.profile.id) {
// don't fetch our this user's own profile, since we have it already
this.profileFetch = $.Deferred().resolve();
profile = app.profile;
// don't fetch this user's own profile, since we have it already
this.profileFetch = $.Deferred().resolve(app.profile);
} else {
profile = new Profile({ peerID: guid });
this.profileFetch = profile.fetch();
this.profileFetch = getCachedProfiles([guid])[0];
}

this.profileFetch.done(() => {
this.profileFetch.done(profile => {
if (this.isRemoved()) return;
this.loading = false;
this.notFound = false;
Expand Down Expand Up @@ -197,7 +193,7 @@ export default class extends BaseVw {
getModTip: this.getModTip,
getFollowTip: this.getFollowTip,
...this.options,
...this.model.toJSON(),
...(this.model && this.model.toJSON() || {}),
}));

this._$followBtn = null;
Expand All @@ -213,5 +209,6 @@ export default class extends BaseVw {

remove() {
if (this.profileFetch && this.profileFetch.abort) this.profileFetch.abort();
super.remove();
}
}
2 changes: 1 addition & 1 deletion js/views/modals/BaseModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class BaseModal extends baseVw {
dismissOnOverlayClick: false,
dismissOnEscPress: true,
showCloseButton: true,
closeButtonClass: 'cornerTR iconBtn clrP clrBr clrSh3 toolTipNoWrap',
closeButtonClass: 'cornerTR iconBtn clrP clrBr clrSh3 toolTipNoWrap modalCloseBtn',
innerButtonClass: 'ion-ios-close-empty',
closeButtonTip: app.polyglot.t('pageNav.toolTip.close'),
modelContentClass: 'modalContent',
Expand Down
Loading

0 comments on commit 42108a2

Please sign in to comment.