Skip to content
Open
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
5 changes: 5 additions & 0 deletions projects/start-os/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ file tracks notable changes since the move to the monorepo.
image would be installed without complaint. It now verifies whenever
`CHECKSUM` is set.

- **Large QR codes render instead of coming up blank.** The encoder was pinned
to correction level `M`, which has no version left past about 2.3 kB — so a
longer value threw and the dialog opened empty, with only a console error to
say why. Those codes now encode at level `L`, which carries about 2.9 kB.

- **The login banner reports system status for every user, not just the first
one to log in.** It staged its database snapshot at a fixed path in `/tmp`,
which `pam_motd` created as root at login — so any subsequent non-root run
Expand Down
35 changes: 35 additions & 0 deletions projects/start-os/web/ui/src/app/components/qr.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
ChangeDetectionStrategy,
Component,
computed,
input,
} from '@angular/core'
import { QrCodeComponent } from 'ng-qrcode'

const utf8 = new TextEncoder()

/**
* ng-qrcode encodes at correction level `M`, which has no version left past
* what that level can hold: the encoder throws "The amount of data is too big
* to be stored in a QR Code" and the modal comes up empty, with only a console
* error to say why. Level `L` carries the payload instead.
*/
@Component({
selector: 'app-qr',
template: `
<qr-code [value]="value()" [errorCorrectionLevel]="level()" size="350" />
`,
imports: [QrCodeComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class QRComponent {
readonly value = input.required<string>()

/**
* Byte-mode capacity of a version-40 code, which anything with a lowercase
* letter in it encodes as: 2331 bytes at `M`, 2953 at `L`.
*/
readonly level = computed(() =>
utf8.encode(this.value()).length > 2331 ? 'L' : 'M',
)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Component } from '@angular/core'
import { TuiDialogContext } from '@taiga-ui/core'
import { injectContext } from '@taiga-ui/polymorpheus'
import { QrCodeModule } from 'ng-qrcode'
import { QRComponent } from 'src/app/components/qr.component'

@Component({
selector: 'qr',
template: '<qr-code [value]="context.data" size="350"></qr-code>',
imports: [QrCodeModule],
template: '<app-qr [value]="context.data" />',
imports: [QRComponent],
})
export class QRModal {
readonly context = injectContext<TuiDialogContext<void, string>>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
TuiTitle,
TuiInput,
} from '@taiga-ui/core'
import { QrCodeComponent } from 'ng-qrcode'
import { QRComponent } from 'src/app/components/qr.component'

@Component({
selector: 'app-action-success-member',
Expand Down Expand Up @@ -77,10 +77,9 @@ import { QrCodeComponent } from 'ng-qrcode'
</label>
}
<ng-template #qr>
<qr-code
<app-qr
[value]="member.value"
[style.filter]="member.masked && masked ? 'blur(0.5rem)' : null"
size="350"
/>
@if (member.masked && masked) {
<button
Expand All @@ -107,14 +106,7 @@ import { QrCodeComponent } from 'ng-qrcode'
text-align: center;
}
`,
imports: [
FormsModule,
TuiInput,
TuiButton,
QrCodeComponent,
TuiTitle,
i18nPipe,
],
imports: [FormsModule, TuiInput, TuiButton, QRComponent, TuiTitle, i18nPipe],
})
export class ActionSuccessMemberComponent {
@ViewChild(TuiInputDirective, { read: ElementRef })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Component, inject, Input } from '@angular/core'
import { FormsModule } from '@angular/forms'
import { CopyService, i18nPipe } from '@start9labs/shared'
import { TuiButton, TuiInput } from '@taiga-ui/core'
import { QrCodeComponent } from 'ng-qrcode'
import { QRComponent } from 'src/app/components/qr.component'
import { SingleResult } from './types'

@Component({
Expand Down Expand Up @@ -49,10 +49,9 @@ import { SingleResult } from './types'
}
</tui-textfield>
<ng-template #qr>
<qr-code
<app-qr
[value]="single.value"
[style.filter]="single.masked && masked ? 'blur(0.5rem)' : null"
size="350"
/>
@if (single.masked && masked) {
<button
Expand Down Expand Up @@ -84,7 +83,7 @@ import { SingleResult } from './types'
FormsModule,
TuiInput,
TuiButton,
QrCodeComponent,
QRComponent,
i18nPipe,
],
})
Expand Down
19 changes: 19 additions & 0 deletions projects/start-os/web/ui/src/app/services/api/api.fixures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1506,6 +1506,25 @@ Full changelog: https://github.com/Kixunil/btc-rpc-proxy/blob/master/CHANGELOG.m
result: null,
}

/**
* A payload longer than correction level `M` can hold in a version-40 code
* (3391 alphanumeric characters), which is the size a hardware-wallet BC-UR
* code reaches once it carries a certificate. Renders the largest QR the
* modal has to draw — 3618 characters, still inside level `L`'s 4296.
*/
export const ActionResBigQr: ActionRes = {
version: '1',
title: 'Enrollment Code',
message: 'Scan this with your device.',
result: {
type: 'single',
copyable: true,
qr: true,
masked: false,
value: `UR:JADE-UPDPS/${'ABCDEFGHJKLMNPQRSTUVWXYZ0123456789'.repeat(106)}`,
},
}

export const ActionResSingle: ActionRes = {
version: '1',
title: 'New Password',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1278,8 +1278,10 @@ export class MockApiService extends ApiService {
this.mockRevision(patch)

// return Mock.ActionResGroup
return Mock.ActionResMessage
// return Mock.ActionResSingle
return params.actionId === 'big-qr'
? Mock.ActionResBigQr
: Mock.ActionResMessage
}

async clearTask(params: T.ClearTaskParams): Promise<null> {
Expand Down
10 changes: 10 additions & 0 deletions projects/start-os/web/ui/src/app/services/api/mock-patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,16 @@ export const mockPatchData: DataModel = {
hasInput: false,
group: null,
},
'big-qr': {
name: 'Show Large QR',
description:
'Returns a QR payload too long for correction level M, the size a hardware-wallet code reaches',
warning: null,
visibility: 'enabled',
allowedStatuses: 'any',
hasInput: false,
group: null,
},
test: {
name: 'Do Another Thing',
description:
Expand Down