Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Commit 76cc49c

Browse files
author
vikasrohit
committed
Merge pull request #597 from appirio-tech/feature/sup-2754-allow-hiding-external-links
Feature/sup 2754 allow hiding external links
2 parents 8fe25ff + c114361 commit 76cc49c

15 files changed

+594
-67
lines changed

app/directives/external-account/external-link-data.directive.jade

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
.external-link-list
2-
div.external-link-tile(ng-repeat="account in linkedAccountsData")
2+
div.external-link-tile(ng-repeat="account in linkedAccountsData", ng-class="{'external-link-tile--editable' : editable}")
33
.top
4+
.ext-link-tile_edit-header(ng-show="editable && account.provider === 'weblink'")
5+
.ext-link-tile_edit-header_delete(ng-click="confirmDeletion(account)", ng-class="{'ext-link-tile_edit-header_delete--disabled': account.deletingAccount || account.status === 'PENDING'}")
46
div.logo
57
i.fa(ng-class="(account|providerData:'className') || 'fa-globe'")
68
h2 {{account|providerData:"displayName"}}
79

8-
div.bottom(ng-switch="account.provider")
10+
div.bottom(ng-if="account.deletingAccount")
11+
.section-loading
12+
div.bottom(ng-switch="account.provider", ng-if="!account.deletingAccount")
913

1014
div(ng-switch-when="github")
1115
.handle {{account.data.handle}}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.deletion-confirmation
2+
.deletion-confirmation-title Heads Up!
3+
.deletion-confirmation-message Are you sure you want to delete the external link #[span.deletion-confirmation-account-title "{{vm.account.title}}"]? This action can't be undone later.
4+
.deletion-confirmation-buttons
5+
.deletion-confirmation-button-yes
6+
button.tc-btn.tc-btn-s.tc-btn-ghost(ng-click="vm.deleteAccount() && closeThisDialog()") Yes, Delete Link
7+
.deletion-confirmation-button-no
8+
button.tc-btn.tc-btn-s(ng-click="closeThisDialog()") Cancel
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
(function () {
2+
3+
angular
4+
.module('tcUIComponents')
5+
.controller('ExternalLinkDeletionController', ExternalLinkDeletionController);
6+
7+
ExternalLinkDeletionController.$inject = ['ExternalWebLinksService', '$q', '$log', 'toaster', 'ngDialog', 'userHandle', 'account', 'linkedAccountsData'];
8+
9+
function ExternalLinkDeletionController(ExternalWebLinksService, $q, $log, toaster, ngDialog, userHandle, account, linkedAccountsData) {
10+
var vm = this;
11+
vm.account = account;
12+
$log = $log.getInstance("ExternalLinkDeletionController");
13+
14+
vm.deleteAccount = function() {
15+
$log.debug('Deleting Account...');
16+
if (account && account.deletingAccount) {
17+
$log.debug('Another deletion is already in progress.');
18+
return;
19+
}
20+
if (account && account.provider === 'weblink') {
21+
account.deletingAccount = true;
22+
$log.debug('Deleting weblink...');
23+
return ExternalWebLinksService.removeLink(userHandle, account.key).then(function(data) {
24+
account.deletingAccount = false;
25+
$log.debug("Web link removed: " + JSON.stringify(data));
26+
var toRemove = _.findIndex(linkedAccountsData, function(la) {
27+
return la.provider === 'weblink' && la.key === account.key;
28+
});
29+
if (toRemove > -1) {
30+
// remove from the linkedAccountsData array
31+
linkedAccountsData.splice(toRemove, 1);
32+
}
33+
toaster.pop('success', "Success", "Your link has been removed.");
34+
})
35+
.catch(function(resp) {
36+
var msg = resp.msg;
37+
if (resp.status === 'WEBLINK_NOT_EXIST') {
38+
$log.info("Weblink does not exist");
39+
msg = "Weblink is not linked to your account. If you think this is an error please contact <a href=\"mailTo:[email protected]\">[email protected]</a>.";
40+
} else {
41+
$log.error("Fatal error: _unlink: " + msg);
42+
msg = "Sorry! We are unable to remove your weblink. If problem persists, please contact <a href=\"mailTo:[email protected]\">[email protected]</a>";
43+
}
44+
45+
account.deletingAccount = false;
46+
toaster.pop('error', "Whoops!", msg);
47+
});
48+
}
49+
}
50+
}
51+
})();
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/* jshint -W117, -W030 */
2+
describe('External Link Deletion Controller', function() {
3+
var scope;
4+
var element;
5+
var toasterSvc, extLinkSvc, ngDialogSvc;
6+
var mockLinkedAccounts = [
7+
{
8+
provider: 'github',
9+
data: {
10+
handle: "github-handle",
11+
followers: 1,
12+
publicRepos: 1
13+
}
14+
},
15+
{ provider: 'stackoverflow',
16+
data: {
17+
handle: 'so-handle',
18+
reputation: 2,
19+
answers: 2
20+
}
21+
},
22+
{
23+
provider: 'behance',
24+
data: {
25+
name: 'behance name',
26+
projectViews: 3,
27+
projectAppreciations: 3
28+
}
29+
},
30+
{
31+
provider: 'dribbble',
32+
data: {
33+
handle: 'dribbble-handle',
34+
followers: 4,
35+
likes: 4
36+
}
37+
},
38+
{
39+
provider: 'bitbucket',
40+
data: {
41+
username: 'bitbucket-username',
42+
followers: 5,
43+
repositories: 5
44+
}
45+
},
46+
{
47+
provider: 'twitter',
48+
data: {
49+
handle: 'twitter-handle',
50+
noOfTweets: 6,
51+
followers: 6
52+
}
53+
},
54+
{
55+
provider: 'linkedin',
56+
data: {
57+
status: 'pending'
58+
}
59+
},
60+
{
61+
provider: 'weblink',
62+
key: 'somekey'
63+
}
64+
];
65+
var createController = function(toDelete, linkedAccounts) {
66+
return $controller('ExternalLinkDeletionController', {
67+
ExternalWebLinksService : extLinkSvc,
68+
toaster: toasterSvc,
69+
userHandle: 'test',
70+
account: toDelete,
71+
linkedAccountsData: linkedAccounts
72+
});
73+
}
74+
75+
beforeEach(function() {
76+
bard.appModule('topcoder');
77+
bard.inject(this, '$compile', '$rootScope', 'toaster', 'ExternalWebLinksService', '$q', 'ngDialog', '$controller');
78+
scope = $rootScope.$new();
79+
80+
extLinkSvc = ExternalWebLinksService;
81+
82+
sinon.stub(extLinkSvc, 'removeLink', function(handle, key) {
83+
var $deferred = $q.defer();
84+
if (key === 'throwNotExistsError') {
85+
$deferred.reject({
86+
status: 'WEBLINK_NOT_EXIST',
87+
msg: 'profile not exists'
88+
});
89+
} else if(key === 'throwFatalError') {
90+
$deferred.reject({
91+
status: 'FATAL_ERROR',
92+
msg: 'fatal error'
93+
});
94+
} else {
95+
$deferred.resolve({
96+
status: 'SUCCESS'
97+
});
98+
}
99+
return $deferred.promise;
100+
});
101+
102+
toasterSvc = toaster;
103+
bard.mockService(toaster, {
104+
pop: $q.when(true),
105+
default: $q.when(true)
106+
});
107+
108+
ngDialogSvc = ngDialog;
109+
sinon.stub(ngDialog, 'open', function() {
110+
ngDialog.deferredClose = $q.defer();
111+
return { closePromise : ngDialog.deferredClose.promise };
112+
});
113+
sinon.stub(ngDialog, 'close', function() {
114+
ngDialog.deferredClose.resolve('closing');
115+
return
116+
})
117+
});
118+
119+
bard.verifyNoOutstandingHttpRequests();
120+
121+
describe('Linked external accounts', function() {
122+
var linkedAccounts = null;
123+
var externalLinksData;
124+
125+
beforeEach(function() {
126+
linkedAccounts = angular.copy(mockLinkedAccounts);
127+
});
128+
129+
afterEach(function() {
130+
linkedAccounts = angular.copy(mockLinkedAccounts);
131+
});
132+
133+
it('should remove weblink ', function() {
134+
var toDelete = {key: 'somekey', provider: 'weblink'};
135+
ctrl = createController(toDelete, linkedAccounts);
136+
ctrl.deleteAccount();
137+
$rootScope.$apply();
138+
expect(toasterSvc.pop).to.have.been.calledWith('success').calledOnce;
139+
expect(linkedAccounts).to.have.length(7);
140+
});
141+
142+
it('should show success if controller doesn\'t have weblink but API returns success ', function() {
143+
var toDelete = {key: 'somekey1', provider: 'weblink'};
144+
ctrl = createController(toDelete, linkedAccounts);
145+
ctrl.deleteAccount();
146+
$rootScope.$apply();
147+
expect(toasterSvc.pop).to.have.been.calledWith('success').calledOnce;
148+
expect(linkedAccounts).to.have.length(8);
149+
});
150+
151+
it('should NOT remove weblink with fatal error ', function() {
152+
var toDelete = {key: 'throwFatalError', provider: 'weblink'};
153+
ctrl = createController(toDelete, linkedAccounts);
154+
ctrl.deleteAccount();
155+
$rootScope.$apply();
156+
expect(toasterSvc.pop).to.have.been.calledWith('error', "Whoops!", sinon.match('Sorry!')).calledOnce;
157+
expect(linkedAccounts).to.have.length(8);
158+
});
159+
160+
it('should NOT remove weblink with already removed weblink ', function() {
161+
var toDelete = {key: 'throwNotExistsError', provider: 'weblink'};
162+
ctrl = createController(toDelete, linkedAccounts);
163+
ctrl.deleteAccount();
164+
$rootScope.$apply();
165+
expect(toasterSvc.pop).to.have.been.calledWith('error', "Whoops!", sinon.match('not linked')).calledOnce;
166+
expect(linkedAccounts).to.have.length(8);
167+
});
168+
169+
it('should not do any thing when already a deletion is in progress ', function() {
170+
var toDelete = {key: 'somekey', provider: 'weblink', deletingAccount: true};
171+
ctrl = createController(toDelete, linkedAccounts);
172+
ctrl.deleteAccount();
173+
$rootScope.$apply();
174+
expect(extLinkSvc.removeLink).not.to.be.called;
175+
expect(toasterSvc.pop).not.to.be.called;
176+
expect(linkedAccounts).to.have.length(8);
177+
});
178+
179+
it('should not do any thing for non weblink provider ', function() {
180+
var toDelete = {key: 'somekey', provider: 'stackoverflow'};
181+
ctrl = createController(toDelete, linkedAccounts);
182+
ctrl.deleteAccount();
183+
$rootScope.$apply();
184+
expect(extLinkSvc.removeLink).not.to.be.called;
185+
expect(toasterSvc.pop).not.to.be.called;
186+
expect(linkedAccounts).to.have.length(8);
187+
});
188+
189+
});
190+
});

app/directives/external-account/external-links-data.directive.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,39 @@
1414
restrict: 'E',
1515
templateUrl: 'directives/external-account/external-link-data.directive.html',
1616
scope: {
17-
linkedAccountsData: '='
18-
}
17+
linkedAccountsData: '=',
18+
editable: '=',
19+
userHandle: '@'
20+
},
21+
controller: ['$log', '$scope', 'ExternalWebLinksService', 'toaster', 'ngDialog',
22+
function($log, $scope, ExternalWebLinksService, toaster, ngDialog) {
23+
24+
$log = $log.getInstance("ExternalLinksDataCtrl");
25+
$scope.deletionDialog = null;
26+
27+
$scope.confirmDeletion = function(account) {
28+
$scope.deletionDialog = ngDialog.open({
29+
className: 'ngdialog-theme-default tc-dialog',
30+
template: 'directives/external-account/external-link-deletion-confirm.html',
31+
controller: 'ExternalLinkDeletionController',
32+
controllerAs: 'vm',
33+
resolve: {
34+
userHandle: function() {
35+
return $scope.userHandle;
36+
},
37+
account: function() {
38+
return account;
39+
},
40+
linkedAccountsData: function() {
41+
return $scope.linkedAccountsData;
42+
}
43+
}
44+
}).closePromise.then(function (data) {
45+
$log.debug('Closing deletion confirmation dialog.');
46+
});
47+
}
48+
}
49+
]
1950
};
2051
return directive;
2152
}

0 commit comments

Comments
 (0)