forked from containers/podman-desktop
-
Notifications
You must be signed in to change notification settings - Fork 1
/
PullImage.svelte
198 lines (182 loc) · 6.85 KB
/
PullImage.svelte
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<script lang="ts">
import { onMount, tick } from 'svelte';
import { router } from 'tinro';
import type { ProviderContainerConnectionInfo } from '../../../../main/src/plugin/api/provider-info';
import type { PullEvent } from '../../../../main/src/plugin/api/pull-event';
import { providerInfos } from '../../stores/providers';
import NoContainerEngineEmptyScreen from './NoContainerEngineEmptyScreen.svelte';
import NavPage from '../ui/NavPage.svelte';
import ErrorMessage from '../ui/ErrorMessage.svelte';
import TerminalWindow from '../ui/TerminalWindow.svelte';
import type { Terminal } from 'xterm';
let logsPull: Terminal;
let pullError = '';
let pullInProgress = false;
let pullFinished = false;
export let imageToPull: string = undefined;
$: providerConnections = $providerInfos
.map(provider => provider.containerConnections)
.flat()
.filter(providerContainerConnection => providerContainerConnection.status === 'started');
let selectedProviderConnection: ProviderContainerConnectionInfo;
const lineNumberPerId = new Map<string, number>();
let lineIndex = 0;
function callback(event: PullEvent) {
let lineIndexToWrite;
if (event.status && event.id) {
const lineNumber = lineNumberPerId.get(event.id);
if (lineNumber) {
lineIndexToWrite = lineNumber;
} else {
lineIndex++;
lineIndexToWrite = lineIndex;
lineNumberPerId.set(event.id, lineIndex);
}
}
// no index, append
if (!lineIndexToWrite) {
lineIndex++;
lineIndexToWrite = lineIndex;
}
if (event.status) {
// move cursor to the home
logsPull.write(`\u001b[${lineIndexToWrite};0H`);
// erase the line
logsPull.write('\u001B[2K');
// do we have id ?
if (event.id) {
logsPull.write(`${event.id}: `);
}
logsPull.write(event.status);
// do we have progress ?
if (event.progress && event.progress !== '') {
logsPull.write(event.progress);
} else if (event?.progressDetail?.current && event?.progressDetail?.total) {
logsPull.write(` ${Math.round((event.progressDetail.current / event.progressDetail.total) * 100)}%`);
}
// write end of line
logsPull.write('\n\r');
} else if (event.error) {
logsPull.write(event.error.replaceAll('\n', '\n\r') + '\n\r');
}
}
async function pullImage() {
lineNumberPerId.clear();
lineIndex = 0;
await tick();
logsPull?.reset();
pullInProgress = true;
try {
await window.pullImage(selectedProviderConnection, imageToPull.trim(), callback);
pullInProgress = false;
pullFinished = true;
} catch (error) {
const errorMessage = error.message ? error.message : error;
pullError = 'Could not connect to ' + selectedProviderConnection.name + ': ' + errorMessage;
pullInProgress = false;
}
}
async function pullImageFinished() {
router.goto('/images');
}
async function gotoManageRegistries() {
router.goto('/preferences/registries');
}
onMount(() => {
if (!selectedProviderConnection) {
selectedProviderConnection = providerConnections.length > 0 ? providerConnections[0] : undefined;
}
});
let imageNameInvalid = undefined;
function validateImageName(event): void {
imageToPull = event.target.value;
if (imageToPull === undefined || imageToPull.trim() === '') {
imageNameInvalid = 'Please enter a value';
} else {
imageNameInvalid = '';
}
}
</script>
<NavPage title="Pull Image From a Registry" searchEnabled="{false}">
<div slot="additional-actions" class="space-x-2 flex flex-nowrap">
<button on:click="{() => gotoManageRegistries()}" class="pf-c-button pf-m-primary" type="button">
<span class="pf-c-button__icon pf-m-start">
<i class="fas fa-cog" aria-hidden="true"></i>
</span>
Manage registries
</button>
</div>
<div slot="content" class="p-5 min-w-full h-fit">
{#if providerConnections.length === 0}
<NoContainerEngineEmptyScreen />
{:else}
<div class="bg-charcoal-900 pt-5 space-y-6 px-8 sm:pb-6 xl:pb-8 rounded-lg">
<div class="w-full">
<label for="imageName" class="block mb-2 text-sm font-bold text-gray-400">Image to Pull</label>
<input
id="imageName"
class="w-full p-2 outline-none text-sm bg-charcoal-600 rounded-sm text-gray-700 placeholder-gray-700"
type="text"
name="serverUrl"
disabled="{pullFinished || pullInProgress}"
on:input="{event => validateImageName(event)}"
bind:value="{imageToPull}"
aria-invalid="{imageNameInvalid && imageNameInvalid !== ''}"
placeholder="Image name"
aria-label="imageName"
required />
{#if imageNameInvalid}
<ErrorMessage error="{imageNameInvalid}" />
{/if}
{#if providerConnections.length > 1}
<div class="pt-4">
<div class="block mb-2 text-sm font-bold text-gray-400">
<label for="providerChoice">Container Engine:</label>
<select
id="providerChoice"
class="w-auto border text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 bg-gray-900 border-gray-900 placeholder-gray-700 text-white"
name="providerChoice"
bind:value="{selectedProviderConnection}">
{#each providerConnections as providerConnection}
<option value="{providerConnection}">{providerConnection.name}</option>
{/each}
</select>
</div>
</div>
{/if}
{#if providerConnections.length === 1}
<input type="hidden" name="providerChoice" readonly bind:value="{selectedProviderConnection}" />
{/if}
</div>
<footer>
<div class="w-full flex flex-col justify-end">
{#if !pullFinished}
<button
class="pf-c-button pf-m-primary"
disabled="{!imageToPull || imageToPull.trim() === '' || pullInProgress}"
type="submit"
on:click="{() => pullImage()}">
{#if pullInProgress === true}
<i class="pf-c-button__progress">
<span class="pf-c-spinner pf-m-md" role="progressbar">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</i>
{/if}
Pull image</button>
{:else}
<button class="pf-c-button pf-m-primary" type="button" on:click="{() => pullImageFinished()}">
Done</button>
{/if}
{#if pullError}
<ErrorMessage error="{pullError}" />
{/if}
</div>
</footer>
<TerminalWindow bind:terminal="{logsPull}" />
</div>
{/if}
</div>
</NavPage>