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:
1 parent
e36ee56de1
commit
204d2537d7
13 files changed
+131
-87
No files matched your search
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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.
|
||||
|
||||
+5
-12
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
+29
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
+1
-54
@@ -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>
|
||||
|
||||
+48
@@ -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}}
|
||||
+9
@@ -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');
|
||||
|
||||
+16
@@ -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);
|
||||
});
|
||||
}
|
||||
);
|
||||
Reference in new issue
Block a user