Skip to content

[Reference PR] VRL - Ember FE Part #2553

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

Draft
wants to merge 11 commits into
base: develop
Choose a base branch
from
Draft
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
10 changes: 10 additions & 0 deletions app/adapters/authorized-link-account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AddonServiceAdapter from './addon-service';

export default class AuthorizedLinkAccountAdapter extends AddonServiceAdapter {
}

declare module 'ember-data/types/registries/adapter' {
export default interface AdapterRegistry {
'authorized-link-account': AuthorizedLinkAccountAdapter;
} // eslint-disable-line semi
}
10 changes: 10 additions & 0 deletions app/adapters/configured-link-addon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AddonServiceAdapter from './addon-service';

export default class ConfiguredLinkAddonAdapter extends AddonServiceAdapter {
}

declare module 'ember-data/types/registries/adapter' {
export default interface AdapterRegistry {
'configured-link-addon': ConfiguredLinkAddonAdapter;
} // eslint-disable-line semi
}
10 changes: 10 additions & 0 deletions app/adapters/external-link-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AddonServiceAdapter from './addon-service';

export default class ExternalLinkServiceAdapter extends AddonServiceAdapter {
}

declare module 'ember-data/types/registries/adapter' {
export default interface AdapterRegistry {
'external-link-service': ExternalLinkServiceAdapter;
} // eslint-disable-line semi
}
11 changes: 11 additions & 0 deletions app/guid-node/addons/index/controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import Media from 'ember-responsive';
import { tracked } from 'tracked-built-ins';

enum FilterTypes {
STORAGE = 'additional-storage',
CITATION_MANAGER = 'citation-manager',
VERIFIED_LINK = 'verified-link',
// CLOUD_COMPUTING = 'cloud-computing', // disabled because BOA is down
}
export default class GuidNodeAddonsController extends Controller {
@service media!: Media;
@tracked tabIndex = 0;
@tracked activeFilterType = FilterTypes.STORAGE;

queryParams = ['tabIndex', 'activeFilterType'];

get isMobile() {
return this.media.isMobile;
Expand Down
11 changes: 8 additions & 3 deletions app/guid-node/addons/index/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<div local-class='addon-page-wrapper'>
<AddonsService::Manager
@node={{this.model}}
@activeFilterType={{this.activeFilterType}}
@updateActiveFilterType={{fn (mut this.activeFilterType)}}
@updateTabIndex={{fn (mut this.tabIndex)}}
as |manager|
>
{{#if manager.selectedProvider}}
Expand Down Expand Up @@ -274,6 +277,7 @@
<AddonsService::ConfiguredAddonEdit
@configuredAddon={{manager.selectedConfiguration}}
@authorizedAccount={{manager.selectedAccount}}
@supportedResourceTypes={{manager.selectedProvider.provider.supportedResourceTypes}}
@onSave={{perform manager.saveOrCreateConfiguration}}
@onCancel={{manager.cancelSetup}}
/>
Expand All @@ -282,7 +286,8 @@
{{/let}}
{{else}}
<AriaTabs
@defaultIndex={{0}}
@selectedIndex={{this.tabIndex}}
@onSelect={{action (mut this.tabIndex)}}
local-class='tabs'
as |tab|
>
Expand Down Expand Up @@ -324,7 +329,7 @@
<Button
data-test-addon-list-filter={{type}}
data-analytics-name={{t (concat 'addons.list.filter.' type)}}
local-class='filter-button {{if (eq manager.activeFilterType type) 'active'}}'
local-class='filter-button {{if (eq this.activeFilterType type) 'active'}}'
@layout='fake-link'
{{on 'click' (fn manager.filterByAddonType type)}}
>
Expand Down Expand Up @@ -364,7 +369,7 @@
<Button
data-test-addon-list-filter={{type}}
data-analytics-name={{t (concat 'addons.list.filter.' type)}}
local-class='filter-button {{if (eq manager.activeFilterType type) 'active'}}'
local-class='filter-button {{if (eq this.activeFilterType type) 'active'}}'
@layout='fake-link'
{{on 'click' (fn manager.filterByAddonType type)}}
>
Expand Down
8 changes: 8 additions & 0 deletions app/guid-node/links/controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Controller from '@ember/controller';
import { Permission } from 'ember-osf-web/models/osf-model';

export default class GuidNodeLinksController extends Controller {
get currentUserCanEdit() {
return this.model.node.currentUserPermissions.includes(Permission.Write);
}
}
17 changes: 17 additions & 0 deletions app/guid-node/links/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Route from '@ember/routing/route';
import Store from '@ember-data/store';
import { inject as service } from '@ember/service';

export default class GuidNodeLinks extends Route {
@service store!: Store;

async model() {
const node = await this.modelFor('guid-node').taskInstance;
const resourceReferences = await this.store.query('resource-reference', {
filter: {resource_uri: node.links.iri?.toString()},
});
const resourceReference = resourceReferences.firstObject;
const configuredLinkAddons = await resourceReference.configuredLinkAddons;
return await {node, configuredLinkAddons};
}
}
29 changes: 29 additions & 0 deletions app/guid-node/links/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.table {
width: 100%;
text-align: left;
border-collapse: collapse;
}

.table-header {
padding-bottom: 10px;
padding-top: 10px;
}

.table-row {
border-bottom: 1px solid $color-border-gray;
}

.table-cell {
padding-bottom: 10px;
padding-top: 10px;
}

.logo {
max-width: 50px;
max-height: 50px;
}

.links-page-wrapper {
margin: 20px;
}

47 changes: 47 additions & 0 deletions app/guid-node/links/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<div local-class='links-page-wrapper'>
<h2>{{t 'links.linked-services'}}</h2>
{{#if this.model.configuredLinkAddons}}
<table local-class='table'>
<thead>
<tr local-class='table-row'>
<th local-class='table-header'>{{t 'links.linked-service'}}</th>
<th local-class='table-header'>{{t 'links.display-name'}}</th>
<th local-class='table-header'>{{t 'links.resource-type'}}</th>
<th local-class='table-header'>
{{#if this.currentUserCanEdit}}
<OsfLink
@route='guid-node.addons'
@queryParams={{hash
activeFilterType='verified-link'
tabIndex='1'
}}
@models={{array this.model.node.id}}
>
{{t 'links.edit'}}
</OsfLink>
{{/if}}
</th>
</tr>
</thead>
<tbody>
{{#each this.model.configuredLinkAddons as |configuredLinkAddon|}}
<tr local-class='table-row'>
<td local-class='table-cell'>
<img alt={{t 'links.logo'}} local-class='logo'
src={{configuredLinkAddon.externalLinkService.iconUrl}}>
<span>{{configuredLinkAddon.externalLinkService.displayName}}</span>
</td>
<td local-class='table-cell'>{{configuredLinkAddon.displayName}}</td>
<td local-class='table-cell'>{{configuredLinkAddon.resourceType}}</td>
<td local-class='table-cell'><a href={{configuredLinkAddon.targetUrl}}>{{t 'links.link'}}</a></td>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
<p>{{t 'links.empty-screen-message'}}</p>
{{#if this.currentUserCanEdit}}
<p> {{t 'links.point-to-addons-message'}} <a href='/{{this.model.node.id}}/addons?activeFilterType=verified-link'>{{t 'addons.heading'}}</a></p>
{{/if}}
{{/if}}
</div>
9 changes: 9 additions & 0 deletions app/guid-node/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@
/>
{{/if}}
{{/if}}
{{/unless}}
<leftNav.link
data-test-verified-links-link
data-analytics-name='Linked services'
@route='guid-node.links'
@icon='link'
@label={{t 'node.left_nav.links'}}
/>
{{#unless this.currentUser.viewOnlyToken}}
{{#if (and (not this.viewOnly) this.model.taskInstance.value.userHasReadPermission)}}
<leftNav.link
data-test-settings-link
Expand Down
6 changes: 6 additions & 0 deletions app/models/addon-operation-invocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ export enum ConnectedCitationOperationNames {
GetItemInfo = 'get_item_info',
}

export enum ConnectedLinkOperationNames {
ListRootItems = 'list_root_items',
ListChildItems = 'list_child_items',
GetItemInfo = 'get_item_info',
}

export interface OperationKwargs {
itemId?: string;
itemType?: ItemType;
Expand Down
47 changes: 47 additions & 0 deletions app/models/authorized-link-account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { AsyncBelongsTo, belongsTo } from '@ember-data/model';
import { waitFor } from '@ember/test-waiters';
import { task } from 'ember-concurrency';
import { ConnectedLinkOperationNames, OperationKwargs } from 'ember-osf-web/models/addon-operation-invocation';

import ExternalLinkServiceModel from 'ember-osf-web/models/external-link-service';
import AuthorizedAccountModel from './authorized-account';
import UserReferenceModel from './user-reference';

export default class AuthorizedLinkAccountModel extends AuthorizedAccountModel {
@belongsTo('user-reference', { inverse: 'authorizedLinkAccounts' })
readonly accountOwner!: AsyncBelongsTo<UserReferenceModel> & UserReferenceModel;

@belongsTo('external-link-service')
externalLinkService!: AsyncBelongsTo<ExternalLinkServiceModel> & ExternalLinkServiceModel;

@task
@waitFor
async getFolderItems(this: AuthorizedAccountModel, kwargs?: OperationKwargs) {
const operationKwargs = kwargs || {};
const operationName = operationKwargs.itemId ? ConnectedLinkOperationNames.ListChildItems :
ConnectedLinkOperationNames.ListRootItems;
const newInvocation = this.store.createRecord('addon-operation-invocation', {
operationName,
operationKwargs,
thruAccount: this,
});
return await newInvocation.save();
}

@task
@waitFor
async getItemInfo(this: AuthorizedAccountModel, itemId: string) {
const newInvocation = this.store.createRecord('addon-operation-invocation', {
operationName: ConnectedLinkOperationNames.GetItemInfo,
operationKwargs: { itemId },
thruAccount: this,
});
return await newInvocation.save();
}
}

declare module 'ember-data/types/registries/model' {
export default interface ModelRegistry {
'authorized-link-account': AuthorizedLinkAccountModel;
} // eslint-disable-line semi
}
3 changes: 3 additions & 0 deletions app/models/configured-addon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import { task } from 'ember-concurrency';
import UserReferenceModel from 'ember-osf-web/models/user-reference';
import { tracked } from 'tracked-built-ins';
import { taskFor } from 'ember-concurrency-ts';
import { SupportedResourceTypes } from 'ember-osf-web/models/external-link-service';
import { ConnectedStorageOperationNames, OperationKwargs } from './addon-operation-invocation';
import { ConnectedCapabilities } from './authorized-account';


export interface ConfiguredAddonEditableAttrs {
displayName: string;
rootFolder: string;
targetId: string;
resourceType: SupportedResourceTypes;
}

export default class ConfiguredAddonModel extends Model {
Expand Down
75 changes: 75 additions & 0 deletions app/models/configured-link-addon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { AsyncBelongsTo, attr, belongsTo } from '@ember-data/model';
import { waitFor } from '@ember/test-waiters';
import { task } from 'ember-concurrency';
import { ConnectedLinkOperationNames, Item, OperationKwargs } from 'ember-osf-web/models/addon-operation-invocation';
import ResourceReferenceModel from 'ember-osf-web/models/resource-reference';

import ExternalLinkServiceModel, { SupportedResourceTypes } from 'ember-osf-web/models/external-link-service';
import AuthorizedLinkAccountModel from 'ember-osf-web/models/authorized-link-account';
import { tracked } from 'tracked-built-ins';
import { taskFor } from 'ember-concurrency-ts';
import ConfiguredAddonModel from './configured-addon';


export default class ConfiguredLinkAddonModel extends ConfiguredAddonModel {
@attr('string') targetId!: string;
@attr('string') targetUrl!: string;
@attr('string') resourceType!: SupportedResourceTypes;

@belongsTo('external-link-service', { inverse: null })
externalLinkService!: AsyncBelongsTo<ExternalLinkServiceModel> & ExternalLinkServiceModel;

@belongsTo('authorized-link-account')
baseAccount!: AsyncBelongsTo<AuthorizedLinkAccountModel> & AuthorizedLinkAccountModel;

@belongsTo('resource-reference', { inverse: 'configuredLinkAddons' })
authorizedResource!: AsyncBelongsTo<ResourceReferenceModel> & ResourceReferenceModel;

get externalServiceId() {
return (this as ConfiguredLinkAddonModel).belongsTo('externalLinkService').id();
}

get hasRootFolder() {
return false;
}

@task
@waitFor
async getFolderItems(this: ConfiguredAddonModel, kwargs?: OperationKwargs) {
const operationKwargs = kwargs || {};
const operationName = operationKwargs.itemId ? ConnectedLinkOperationNames.ListChildItems :
ConnectedLinkOperationNames.ListRootItems;
const newInvocation = this.store.createRecord('addon-operation-invocation', {
operationName,
operationKwargs,
thruAddon: this,
});
return await newInvocation.save();
}

@task
@waitFor
async getItemInfo(this: ConfiguredAddonModel, itemId: string) {
const newInvocation = this.store.createRecord('addon-operation-invocation', {
operationName: ConnectedLinkOperationNames.GetItemInfo,
operationKwargs: { itemId },
thruAddon: this,
});
return await newInvocation.save();
}

@tracked targetItemName = '';

@task
@waitFor
async getTargetItemName(this: ConfiguredLinkAddonModel) {
const response = await taskFor(this.getItemInfo).perform(this.targetId);
this.targetItemName = (response.operationResult as Item).itemName;
}
}

declare module 'ember-data/types/registries/model' {
export default interface ModelRegistry {
'configured-link-addon': ConfiguredLinkAddonModel;
} // eslint-disable-line semi
}
Loading