refactor: 💡 Add loading substate for channels (#303)

* refactor: 💡 Add loading substate for channels

* refactor: 💡 Use conditional block for session recording player

* style: 💄 Use a unicode ellipsis instead of periods

* refactor: 💡 rename asciiCast to asciicast (#307)

---------

Co-authored-by: cameronperera <cameron.perera@hashicorp.com>
This commit is contained in:
Zhihe Liandcameronperera authored and GitHub committed 2023-06-01 14:15:38 +00:00
1 parent e36ee56de1
commit 204d2537d7
13 files changed
+131 -87

No files matched your search

+2 -2
View File
@@ -15,7 +15,7 @@ import { pickRandomStatusString } from './factories/session';
import initializeMockIPC from './scenarios/ipc';
import makeBooleanFilter from './helpers/bexpr-filter';
import { faker } from '@faker-js/faker';
import { asciiCasts } from './data/asciicasts';
import { asciicasts } from './data/asciicasts';
const isTesting = environmentConfig.environment === 'test';
@@ -725,7 +725,7 @@ function routes() {
const method = idMethod.split(':')[1];
if (method === 'download') {
return faker.helpers.arrayElement(asciiCasts);
return faker.helpers.arrayElement(asciicasts);
} else {
return sessionRecordings.find(id);
}
+3 -3
View File
@@ -1,4 +1,4 @@
export const asciiCast1 = `{"version": 2, "width": 124, "height": 36, "timestamp": 1670352594, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}}
export const asciicast1 = `{"version": 2, "width": 124, "height": 36, "timestamp": 1670352594, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}}
[2.138652, "o", "\\u001b[1m\\u001b[7m%\\u001b[27m\\u001b[1m\\u001b[0m \\r \\r\\u001b]2;ember-asciinema-player\\u0007\\u001b]1;..iinema-player\\u0007"]
[2.220877, "o", "\\r\\u001b[0m\\u001b[27m\\u001b[24m\\u001b[J\\u001b[33member-asciinema-player\\u001b[00m \\u001b[32mgit\\u001b[00m:\\u001b[36m(main*)\\u001b[00m $ \\u001b[K"]
[2.221019, "o", "\\u001b[?1h\\u001b="]
@@ -68,7 +68,7 @@ export const asciiCast1 = `{"version": 2, "width": 124, "height": 36, "timestamp
[16.52863, "o", "\\u001b[?2004h"]
[19.078794, "o", "\\u001b[?2004l\\r\\r\\n"]`;
export const asciiCast2 = `{"version": 2, "width": 112, "height": 30, "timestamp": 1672793720, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
export const asciicast2 = `{"version": 2, "width": 112, "height": 30, "timestamp": 1672793720, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[0.036928, "o", "\\u001b[?2004h\\u001b[1;32m[\\u001b]0;johan@nixos: ~/src/hashicorp/boundary-enterprise\\u0007johan@nixos:~/src/hashicorp/boundary-enterprise]$\\u001b[0m "]
[2.827983, "o", "b"]
[2.9386, "o", "o"]
@@ -148,4 +148,4 @@ export const asciiCast2 = `{"version": 2, "width": 112, "height": 30, "timestamp
[21.303896, "o", "\\r\\n"]
[21.307902, "o", "\\u001b[?2004l\\rexit\\r\\n"]`;
export const asciiCasts = [asciiCast1, asciiCast2];
export const asciicasts = [asciicast1, asciicast2];
@@ -257,6 +257,7 @@ session-recording:
description: The contents of this channel cannot be replayed.
player:
back-navigation: Back to channels
loading: Loading recording…
session:
title: Session Playback
form:
@@ -8,7 +8,11 @@
@model={{@model}}
/>
</div>
<div class='session-recording-player-theme'>
<Heap::Player @data={{@asciiCast}} @poster='npt:1:30' />
</div>
{{#if (has-block)}}
{{yield}}
{{else}}
<div class='session-recording-player-theme'>
<Heap::Player @data={{@asciicast}} @poster='npt:1:30' />
</div>
{{/if}}
</div>
-12
View File
@@ -7,7 +7,6 @@ import Route from '@ember/routing/route';
import { getOwner } from '@ember/application';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { loading } from 'ember-loading';
import { A } from '@ember/array';
/**
@@ -75,17 +74,6 @@ export default class ApplicationRoute extends Route {
this.session.invalidate();
}
/**
* Hooks into ember-loading to kick off loading indicator in the
* application template.
* @return {boolean} always returns true
*/
@action
@loading
loading() {
return true;
}
/**
* Invalidates the session if a 401 error occurs and returns false to
* prevent further error handling.
@@ -4,33 +4,26 @@ import { inject as service } from '@ember/service';
export default class ScopesScopeSessionRecordingsSessionRecordingChannelsByConnectionChannelRoute extends Route {
// =services
@service store;
@service can;
// =methods
/**
*
* @param {*} channel_id
* @returns {channelRecording: Object, sessionRecording: Object, storageBucket: Object, asciicast: Object}
* @param {string} channel_id
* @returns {channelRecording: Object, sessionRecording: Object, storageBucket: Object}
*/
async model({ channel_id }) {
let asciicast;
const { sessionRecording, storageBucket } = await this.modelFor(
model({ channel_id }) {
const { sessionRecording, storageBucket } = this.modelFor(
'scopes.scope.session-recordings.session-recording.channels-by-connection'
);
const channelRecording = await this.store.peekRecord(
const channelRecording = this.store.peekRecord(
'channel-recording',
channel_id
);
if (this.can.can('getAsciicast channel-recording', channelRecording)) {
asciicast = await channelRecording.getAsciicast();
}
return {
channelRecording,
sessionRecording,
storageBucket,
asciicast,
};
}
}
@@ -0,0 +1,29 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class ScopesScopeSessionRecordingsSessionRecordingChannelsByConnectionChannelIndexRoute extends Route {
// =services
@service can;
// =methods
/**
* @returns {channelRecording: Object, sessionRecording: Object, storageBucket: Object, asciicast: Object}
*/
async model() {
let asciicast;
const { sessionRecording, storageBucket, channelRecording } = this.modelFor(
'scopes.scope.session-recordings.session-recording.channels-by-connection.channel'
);
if (this.can.can('getAsciicast channel-recording', channelRecording)) {
asciicast = await channelRecording.getAsciicast();
}
return {
channelRecording,
sessionRecording,
storageBucket,
asciicast,
};
}
}
+9
View File
@@ -800,6 +800,15 @@
}
}
&-loading {
color: var(--token-color-palette-neutral-400);
display: flex;
gap: 1rem;
justify-content: center;
align-items: center;
height: 75vh;
}
&-theme {
.asciinema-theme-asciinema {
background-color: var(--player-background-color);
@@ -25,60 +25,7 @@
<page.body>
<Layout::Sidebar as |sb|>
<sb.Body>
{{#if @model.asciicast}}
<SessionRecording::Player
@asciiCast={{@model.asciicast}}
@route='scopes.scope.session-recordings.session-recording'
@model={{@model.sessionRecording.id}}
/>
{{else}}
<Hds::ApplicationState as |A|>
{{#if
(can 'getAsciicast channel-recording' @model.channelRecording)
}}
{{! Was not able to download asciicast }}
<A.Header
@title={{t
'resources.session-recording.channel.messages.missing.title'
}}
/>
<A.Body
@text={{t
'resources.session-recording.channel.messages.missing.description'
}}
/>
{{else}}
{{#if @model.channelRecording.isAsciicast}}
{{! Not authorized to download asciicast }}
<A.Header
@title={{t
'resources.session-recording.channel.messages.none.title'
}}
/>
<A.Body
@text={{t
'descriptions.cannot-download'
resource=(t
'resources.session-recording.channel.title_plural'
)
}}
/>
{{else}}
{{! Playback not supported }}
<A.Header
@title={{t
'resources.session-recording.channel.messages.not-supported.title'
}}
/>
<A.Body
@text={{t
'resources.session-recording.channel.messages.not-supported.description'
}}
/>
{{/if}}
{{/if}}
</Hds::ApplicationState>
{{/if}}
{{outlet}}
</sb.Body>
<sb.Sidebar>
@@ -0,0 +1,48 @@
{{#if @model.asciicast}}
<SessionRecording::Player
@asciicast={{@model.asciicast}}
@route='scopes.scope.session-recordings.session-recording'
@model={{@model.sessionRecording.id}}
/>
{{else}}
<Hds::ApplicationState as |A|>
{{#if (can 'getAsciicast channel-recording' @model.channelRecording)}}
{{! Was not able to download asciicast }}
<A.Header
@title={{t
'resources.session-recording.channel.messages.missing.title'
}}
/>
<A.Body
@text={{t
'resources.session-recording.channel.messages.missing.description'
}}
/>
{{else}}
{{#if @model.channelRecording.isAsciicast}}
{{! Not authorized to download asciicast }}
<A.Header
@title={{t 'resources.session-recording.channel.messages.none.title'}}
/>
<A.Body
@text={{t
'descriptions.cannot-download'
resource=(t 'resources.session-recording.channel.title_plural')
}}
/>
{{else}}
{{! Playback not supported }}
<A.Header
@title={{t
'resources.session-recording.channel.messages.not-supported.title'
}}
/>
<A.Body
@text={{t
'resources.session-recording.channel.messages.not-supported.description'
}}
/>
{{/if}}
{{/if}}
</Hds::ApplicationState>
{{/if}}
@@ -0,0 +1,9 @@
<SessionRecording::Player
@route='scopes.scope.session-recordings.session-recording'
@model={{@model.sessionRecording.id}}
>
<div class='session-recording-player-loading'>
<FlightIcon @name='loading' @size='24' />
<span>{{t 'resources.session-recording.channel.player.loading'}}</span>
</div>
</SessionRecording::Player>
@@ -28,7 +28,7 @@ module('Integration | Component | session-recording/player', function (hooks) {
[3.63678, "o", "t"]`;
await render(hbs`
<SessionRecording::Player @asciiCast={{this.cast}} @route='test.route' @model='t_123' />
<SessionRecording::Player @asciicast={{this.cast}} @route='test.route' @model='t_123' />
`);
assert.dom('.session-recording-player-header').hasText('Back to channels');
@@ -0,0 +1,16 @@
import { module, test } from 'qunit';
import { setupTest } from 'admin/tests/helpers';
module(
'Unit | Route | scopes/scope/session-recordings/session-recording/channels-by-connection/channel/index',
function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup(
'route:scopes/scope/session-recordings/session-recording/channels-by-connection/channel/index'
);
assert.ok(route);
});
}
);