Merge pull request #53850 from nextcloud/feat/noid/add-busy-status

pull/54138/head
Maksim Sukharev 2025-08-01 09:09:44 +07:00 committed by GitHub
commit 1675aa4f45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 103 additions and 55 deletions

@ -141,7 +141,7 @@ class StatusService {
$this->logger->debug("Found $count applicable event(s), changing user status", ['user' => $userId]);
$this->userStatusService->setUserStatus(
$userId,
IUserStatus::AWAY,
IUserStatus::BUSY,
IUserStatus::MESSAGE_CALENDAR_BUSY,
true
);

@ -20,6 +20,7 @@ use OCP\UserStatus\IUserStatus;
* @package OCA\UserStatus\Service
*/
class PredefinedStatusService {
private const BE_RIGHT_BACK = 'be-right-back';
private const MEETING = 'meeting';
private const COMMUTING = 'commuting';
private const SICK_LEAVE = 'sick-leave';
@ -64,6 +65,15 @@ class PredefinedStatusService {
'time' => 1800,
],
],
[
'id' => self::BE_RIGHT_BACK,
'icon' => '⏳',
'message' => $this->getTranslatedStatusForId(self::BE_RIGHT_BACK),
'clearAt' => [
'type' => 'period',
'time' => 900,
],
],
[
'id' => self::REMOTE_WORK,
'icon' => '🏡',
@ -143,6 +153,9 @@ class PredefinedStatusService {
case self::REMOTE_WORK:
return '🏡';
case self::BE_RIGHT_BACK:
return '⏳';
case self::CALL:
return '💬';
@ -179,6 +192,9 @@ class PredefinedStatusService {
case self::CALL:
return $this->l10n->t('In a call');
case self::BE_RIGHT_BACK:
return $this->l10n->t('Be right back');
default:
return null;
}
@ -195,6 +211,7 @@ class PredefinedStatusService {
self::SICK_LEAVE,
self::VACATIONING,
self::OUT_OF_OFFICE,
self::BE_RIGHT_BACK,
self::REMOTE_WORK,
IUserStatus::MESSAGE_CALL,
IUserStatus::MESSAGE_AVAILABILITY,

@ -14,6 +14,7 @@
:value="option"
:clearable="false"
placement="top"
label-outside
@option:selected="select" />
</div>
</template>
@ -72,12 +73,9 @@ export default {
<style lang="scss" scoped>
.clear-at-select {
display: flex;
margin-bottom: 10px;
gap: calc(2 * var(--default-grid-baseline));
align-items: center;
&__label {
margin-inline-end: 12px;
}
margin-block: 0 calc(2 * var(--default-grid-baseline));
&__select {
flex-grow: 1;

@ -11,9 +11,10 @@
name="user-status-online"
@change="onChange">
<label :for="id" class="user-status-online-select__label">
{{ label }}
<NcUserStatusIcon :status="type"
class="user-status-online-select__icon"
aria-hidden="true" />
{{ label }}
<em class="user-status-online-select__subline">{{ subline }}</em>
</label>
</div>
@ -63,45 +64,42 @@ export default {
</script>
<style lang="scss" scoped>
@use 'sass:math';
$icon-size: 24px;
$label-padding: 8px;
.user-status-online-select {
&__label {
position: relative;
display: block;
margin: $label-padding;
padding: $label-padding;
padding-inline-start: $icon-size + $label-padding * 2;
border: 2px solid var(--color-main-background);
box-sizing: inherit;
display: grid;
grid-template-columns: var(--default-clickable-area) 1fr 2fr;
align-items: center;
gap: var(--default-grid-baseline);
min-height: var(--default-clickable-area);
padding: var(--default-grid-baseline);
border-radius: var(--border-radius-large);
background-color: var(--color-background-hover);
background-position: $label-padding center;
background-size: $icon-size;
span,
& {
&, & * {
cursor: pointer;
}
span {
position: absolute;
top: calc(50% - 10px);
inset-inline-start: 10px;
display: block;
width: $icon-size;
height: $icon-size;
&:hover {
background-color: var(--color-background-dark);
}
}
&__icon {
flex-shrink: 0;
max-width: 34px;
max-height: 100%;
}
&__input:checked + &__label {
outline: 2px solid var(--color-main-text);
background-color: var(--color-background-dark);
box-shadow: 0 0 0 4px var(--color-main-background);
}
&__input:focus-visible + &__label {
outline: 2px solid var(--color-primary-element) !important;
background-color: var(--color-background-dark);
}
&__subline {

@ -81,10 +81,19 @@ export default {
flex-basis: 100%;
border-radius: var(--border-radius);
align-items: center;
min-height: 44px;
min-height: var(--default-clickable-area);
padding-inline: var(--default-grid-baseline);
&, & * {
cursor: pointer;
}
&:hover {
background-color: var(--color-background-dark);
}
&--icon {
flex-basis: 40px;
flex-basis: var(--default-clickable-area);
text-align: center;
}
@ -106,11 +115,13 @@ export default {
&__label:active {
outline: 2px solid var(--color-main-text);
box-shadow: 0 0 0 4px var(--color-main-background);
background-color: var(--color-background-dark);
border-radius: var(--border-radius-large);
}
&__input:focus-visible + &__label {
outline: 2px solid var(--color-primary-element) !important;
background-color: var(--color-background-dark);
border-radius: var(--border-radius-large);
}
}

@ -78,6 +78,7 @@ export default {
.predefined-statuses-list {
display: flex;
flex-direction: column;
margin-bottom: 10px;
gap: var(--default-grid-baseline);
margin-block: 0 calc(2 * var(--default-grid-baseline));
}
</style>

@ -65,7 +65,8 @@ export default {
flex-basis: 100%;
border-radius: var(--border-radius);
align-items: center;
min-height: 44px;
min-height: var(--default-clickable-area);
padding-inline: var(--default-grid-baseline);
&:hover,
&:focus {
@ -77,7 +78,7 @@ export default {
}
&__icon {
flex-basis: 40px;
flex-basis: var(--default-clickable-area);
text-align: center;
}

@ -5,8 +5,8 @@
<template>
<NcModal size="normal"
:name="$t('user_status', 'Set status')"
aria-labelledby="user_status-set-dialog"
label-id="user_status-set-dialog"
dark
:set-return-focus="setReturnFocus"
@close="closeModal">
<div class="set-status-modal">
@ -336,6 +336,10 @@ export default {
.set-status-modal {
padding: 8px 20px 20px 20px;
&, & * {
box-sizing: border-box;
}
&__header {
font-size: 21px;
text-align: center;
@ -343,12 +347,14 @@ export default {
min-height: var(--default-clickable-area);
line-height: var(--default-clickable-area);
overflow-wrap: break-word;
margin-block: 0 12px;
margin-block: 0 calc(2 * var(--default-grid-baseline));
}
&__online-status {
display: grid;
grid-template-columns: 1fr 1fr;
display: flex;
flex-direction: column;
gap: calc(2 * var(--default-grid-baseline));
margin-block: 0 calc(2 * var(--default-grid-baseline));
}
&__custom-input {
@ -357,13 +363,14 @@ export default {
align-items: center;
gap: var(--default-grid-baseline);
width: 100%;
margin-bottom: 10px;
padding-inline-start: var(--default-grid-baseline);
margin-block: 0 calc(2 * var(--default-grid-baseline));
}
&__automation-hint {
display: flex;
width: 100%;
margin-bottom: 10px;
margin-block: 0 calc(2 * var(--default-grid-baseline));
color: var(--color-text-maxcontrast);
}

@ -17,6 +17,9 @@ const getAllStatusOptions = () => {
}, {
type: 'away',
label: t('user_status', 'Away'),
}, {
type: 'busy',
label: t('user_status', 'Busy'),
}, {
type: 'dnd',
label: t('user_status', 'Do not disturb'),

@ -136,7 +136,7 @@ class StatusServiceIntegrationTest extends TestCase {
);
$this->service->setUserStatus(
'test123',
IUserStatus::AWAY,
IUserStatus::BUSY,
IUserStatus::MESSAGE_CALENDAR_BUSY,
true,
);
@ -147,12 +147,12 @@ class StatusServiceIntegrationTest extends TestCase {
$this->service->setUserStatus(
'test123',
IUserStatus::AWAY,
IUserStatus::BUSY,
IUserStatus::MESSAGE_CALL,
true,
);
self::assertSame(
IUserStatus::AWAY,
IUserStatus::BUSY,
$this->service->findByUserId('test123')->getStatus(),
);
@ -182,7 +182,7 @@ class StatusServiceIntegrationTest extends TestCase {
$nostatus = $this->service->setUserStatus(
'test123',
IUserStatus::AWAY,
IUserStatus::BUSY,
IUserStatus::MESSAGE_CALENDAR_BUSY,
true,
);

@ -26,7 +26,7 @@ class PredefinedStatusServiceTest extends TestCase {
}
public function testGetDefaultStatuses(): void {
$this->l10n->expects($this->exactly(7))
$this->l10n->expects($this->exactly(8))
->method('t')
->willReturnCallback(function ($text, $parameters = []) {
return vsprintf($text, $parameters);
@ -52,6 +52,15 @@ class PredefinedStatusServiceTest extends TestCase {
'time' => 1800,
],
],
[
'id' => 'be-right-back',
'icon' => '⏳',
'message' => 'Be right back',
'clearAt' => [
'type' => 'period',
'time' => 900,
],
],
[
'id' => 'remote-work',
'icon' => '🏡',
@ -106,6 +115,7 @@ class PredefinedStatusServiceTest extends TestCase {
['sick-leave', '🤒'],
['vacationing', '🌴'],
['remote-work', '🏡'],
['be-right-back', '⏳'],
['call', '💬'],
['unknown-id', null],
];
@ -127,6 +137,7 @@ class PredefinedStatusServiceTest extends TestCase {
['sick-leave', 'Out sick'],
['vacationing', 'Vacationing'],
['remote-work', 'Working remotely'],
['be-right-back', 'Be right back'],
['call', 'In a call'],
['unknown-id', null],
];
@ -145,13 +156,14 @@ class PredefinedStatusServiceTest extends TestCase {
['sick-leave', true],
['vacationing', true],
['remote-work', true],
['be-right-back', true],
['call', true],
['unknown-id', false],
];
}
public function testGetDefaultStatusById(): void {
$this->l10n->expects($this->exactly(7))
$this->l10n->expects($this->exactly(8))
->method('t')
->willReturnCallback(function ($text, $parameters = []) {
return vsprintf($text, $parameters);

4
dist/core-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long