4 Commits

Author SHA1 Message Date
Sebastian e26ef50a9a chore(deps): update dependency phpunit/phpunit to v12.5.33
Build Test / test (pull_request) Successful in 52s
JS Unit Tests / test (pull_request) Successful in 51s
PHP Integration Tests / Integration Tests (pull_request) Failing after 56s
PHP Unit Tests / test (pull_request) Successful in 1m30s
2026-07-29 03:02:56 +00:00
Sebastian 8a18494883 feat: show disabled accounts
Signed-off-by: Sebastian <krupinski01@gmail.com>
2026-07-28 20:52:13 -04:00
Sebastian 0a2a87acfa fix: auto selected sending account when only once available
Signed-off-by: Sebastian <krupinski01@gmail.com>
2026-07-28 13:10:01 -04:00
Sebastian 436197fbfe refactor: migrate to scoped execution contexts
Signed-off-by: Sebastian <krupinski01@gmail.com>
2026-07-27 00:52:28 -04:00
4 changed files with 30 additions and 33 deletions
Generated
+7 -7
View File
@@ -589,16 +589,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "12.5.31", "version": "12.5.33",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "0608d157a284f15cc73b99a3327eff06b66a176d" "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0608d157a284f15cc73b99a3327eff06b66a176d", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b98e028a26c5c5ba7e4a54be96ccf35f2914d184",
"reference": "0608d157a284f15cc73b99a3327eff06b66a176d", "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -667,7 +667,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.31" "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.33"
}, },
"funding": [ "funding": [
{ {
@@ -675,7 +675,7 @@
"type": "other" "type": "other"
} }
], ],
"time": "2026-07-06T14:54:16+00:00" "time": "2026-07-28T13:58:09+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@@ -1698,5 +1698,5 @@
"php": ">=8.3" "php": ">=8.3"
}, },
"platform-dev": {}, "platform-dev": {},
"plugin-api-version": "2.6.0" "plugin-api-version": "2.9.0"
} }
+6 -6
View File
@@ -7,8 +7,8 @@ namespace KTXM\Mail\Controllers;
use InvalidArgumentException; use InvalidArgumentException;
use KTXC\Http\Response\JsonResponse; use KTXC\Http\Response\JsonResponse;
use KTXC\Http\Response\Response; use KTXC\Http\Response\Response;
use KTXC\SessionIdentity; use KTXC\Context\IdentityContextInterface;
use KTXC\SessionTenant; use KTXC\Context\TenantContextInterface;
use KTXF\Controller\ControllerAbstract; use KTXF\Controller\ControllerAbstract;
use KTXF\Routing\Attributes\AuthenticatedRoute; use KTXF\Routing\Attributes\AuthenticatedRoute;
use KTXM\Mail\CompositionManager; use KTXM\Mail\CompositionManager;
@@ -18,8 +18,8 @@ use Throwable;
final class CompositionController extends ControllerAbstract { final class CompositionController extends ControllerAbstract {
public function __construct( public function __construct(
private readonly SessionTenant $tenantIdentity, private readonly TenantContextInterface $tenantContext,
private readonly SessionIdentity $userIdentity, private readonly IdentityContextInterface $identityContext,
private readonly CompositionManager $compositionManager, private readonly CompositionManager $compositionManager,
private readonly LoggerInterface $logger, private readonly LoggerInterface $logger,
) {} ) {}
@@ -31,8 +31,8 @@ final class CompositionController extends ControllerAbstract {
string|null $operation = null, string|null $operation = null,
array|null $data = null, array|null $data = null,
): Response { ): Response {
$tenantId = $this->tenantIdentity->identifier(); $tenantId = $this->tenantContext->identifier();
$userId = $this->userIdentity->identifier(); $userId = $this->identityContext->identifier();
try { try {
if ($operation === null) { if ($operation === null) {
+14 -18
View File
@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ComposerSenderIdentity } from '@/types/composer'; import type { ComposerSenderIdentity } from '@/types/composer';
import { computed } from 'vue' import { computed, watch } from 'vue'
interface Props { interface Props {
modelValue: ComposerSenderIdentity | null modelValue: ComposerSenderIdentity | null
@@ -27,11 +27,6 @@ const items = computed<SenderListItem[]>(() => {
const selectedValue = computed(() => props.modelValue?.address ?? null) const selectedValue = computed(() => props.modelValue?.address ?? null)
const singleOptionLabel = computed(() => {
const onlyOption = props.options[0]
return onlyOption ? formatSenderLabel(onlyOption.label, onlyOption.address) : ''
})
const errorMessages = computed(() => { const errorMessages = computed(() => {
if (props.options.length === 0) { if (props.options.length === 0) {
return ['No send-capable account is available.'] return ['No send-capable account is available.']
@@ -40,6 +35,18 @@ const errorMessages = computed(() => {
return [] return []
}) })
// Auto-select (and keep selected) the only send-capable account, since
// there's no "from" UI shown for the user to pick it themselves.
watch(
() => props.options,
options => {
if (options.length === 1 && props.modelValue?.address !== options[0].address) {
emit('update:modelValue', options[0])
}
},
{ immediate: true },
)
function handleUpdate(value: string | null) { function handleUpdate(value: string | null) {
const match = value ? props.options.find(o => o.address === value) ?? null : null const match = value ? props.options.find(o => o.address === value) ?? null : null
emit('update:modelValue', match) emit('update:modelValue', match)
@@ -51,19 +58,8 @@ function formatSenderLabel(label: string | null | undefined, address: string): s
</script> </script>
<template> <template>
<div class="composer-sender px-4 pt-4 pb-0"> <div v-if="options.length !== 1" class="composer-sender px-4 pt-4 pb-0">
<v-text-field
v-if="options.length === 1"
:model-value="singleOptionLabel"
label="From"
variant="outlined"
density="compact"
readonly
class="mb-2"
/>
<v-select <v-select
v-else
:model-value="selectedValue" :model-value="selectedValue"
:items="items" :items="items"
item-title="title" item-title="title"
+3 -2
View File
@@ -40,10 +40,11 @@ const handleAccountSaved = async () => {
<v-list-item <v-list-item
v-for="service in servicesStore.services" v-for="service in servicesStore.services"
:key="`${service.provider}:${service.identifier}`" :key="`${service.provider}:${service.identifier}`"
:class="{ 'opacity-60': !service.enabled }"
> >
<template #prepend> <template #prepend>
<v-avatar color="primary"> <v-avatar :color="service.enabled ? 'primary' : 'grey'">
<v-icon>mdi-email</v-icon> <v-icon>{{ service.enabled ? 'mdi-email' : 'mdi-email-off' }}</v-icon>
</v-avatar> </v-avatar>
</template> </template>