Skip to content

Commit

Permalink
feat: has the message been listened to or not
Browse files Browse the repository at this point in the history
  • Loading branch information
TinySmallM committed Feb 25, 2025
1 parent 69d8dba commit 3a847ca
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 102 deletions.
3 changes: 0 additions & 3 deletions src/assets/icons/Icon32PauseCircle.svg

This file was deleted.

24 changes: 24 additions & 0 deletions src/assets/icons/Icon32PauseCircle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineComponent, useId } from 'vue'

type Props = {
withUnlistenedDot?: boolean
}

export const Icon32PauseCircle = defineComponent<Props>((props) => {
const id = useId()

return () => (
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
{props.withUnlistenedDot && (
<mask id={id}>
<rect width="100%" height="100%" fill="white" />
<circle cx="27" cy="27" r="5" />
</mask>
)}
<path mask={`url(#${id})`} d="M32 16a16 16 0 1 1-32 0 16 16 0 0 1 32 0zm-20.9-5.45c-.1.21-.1.49-.1 1.05v8.8c0 .56 0 .84.1 1.05a1 1 0 0 0 .45.44c.21.11.49.11 1.05.11h.8c.56 0 .84 0 1.05-.1a1 1 0 0 0 .44-.45c.11-.21.11-.49.11-1.05v-8.8c0-.56 0-.84-.1-1.05a1 1 0 0 0-.45-.44c-.21-.11-.49-.11-1.05-.11h-.8c-.56 0-.84 0-1.05.1a1 1 0 0 0-.44.45zm6 0c-.1.21-.1.49-.1 1.05v8.8c0 .56 0 .84.1 1.05a1 1 0 0 0 .45.44c.21.11.49.11 1.05.11h.8c.56 0 .84 0 1.05-.1a1 1 0 0 0 .44-.45c.11-.21.11-.49.11-1.05v-8.8c0-.56 0-.84-.1-1.05a1 1 0 0 0-.45-.44c-.21-.11-.49-.11-1.05-.11h-.8c-.56 0-.84 0-1.05.1a1 1 0 0 0-.44.45z" fill="currentColor" />
{props.withUnlistenedDot && <circle cx="27" cy="27" r="3" fill="currentColor" />}
</svg>
)
}, {
props: ['withUnlistenedDot']
})
3 changes: 0 additions & 3 deletions src/assets/icons/Icon32PlayCircle.svg

This file was deleted.

24 changes: 24 additions & 0 deletions src/assets/icons/Icon32PlayCircle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineComponent, useId } from 'vue'

type Props = {
withUnlistenedDot?: boolean
}

export const Icon32PlayCircle = defineComponent<Props>((props) => {
const id = useId()

return () => (
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
{props.withUnlistenedDot && (
<mask id={id}>
<rect width="100%" height="100%" fill="white" />
<circle cx="27" cy="27" r="5" />
</mask>
)}
<path mask={`url(#${id})`} clip-rule="evenodd" d="M32 16c0 8.837-7.163 16-16 16S0 24.837 0 16 7.163 0 16 0s16 7.163 16 16zm-9.851.874a1.005 1.005 0 0 0 0-1.739l-8.644-4.994a1.003 1.003 0 0 0-1.505.87v9.988c0 .773.836 1.256 1.505.87z" fill="currentColor" fill-rule="evenodd" />
{props.withUnlistenedDot && <circle cx="27" cy="27" r="3" fill="currentColor" />}
</svg>
)
}, {
props: ['withUnlistenedDot']
})
4 changes: 2 additions & 2 deletions src/assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export { default as Icon24ViewOutline } from './Icon24ViewOutline.svg'
export { default as Icon24VolumeOutline } from './Icon24VolumeOutline.svg'
export { default as Icon28DeleteOutline } from './Icon28DeleteOutline.svg'
export { default as Icon32DonutCircleFillYellow } from './Icon32DonutCircleFillYellow.svg'
export { default as Icon32PauseCircle } from './Icon32PauseCircle.svg'
export { default as Icon32PlayCircle } from './Icon32PlayCircle.svg'
export { Icon32PauseCircle } from './Icon32PauseCircle'
export { Icon32PlayCircle } from './Icon32PlayCircle'
export { default as Icon32Spinner } from './Icon32Spinner.svg'
export { default as Icon44Spinner } from './Icon44Spinner.svg'
10 changes: 7 additions & 3 deletions src/converters/AttachConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import * as Peer from 'model/Peer'
import { insertPeers } from 'actions'
import { isNonEmptyArray } from 'misc/utils'

export function fromApiAttaches(apiAttaches: MessagesMessageAttachment[]): Attach.Attaches {
export function fromApiAttaches(
apiAttaches: MessagesMessageAttachment[],
wasVoiceMessageListened: boolean
): Attach.Attaches {
const attaches: Attach.Attaches = {}

const addUnknown = (attach: MessagesMessageAttachment) => {
Expand Down Expand Up @@ -54,7 +57,8 @@ export function fromApiAttaches(apiAttaches: MessagesMessageAttachment[]): Attac
linkOgg: apiAttach.audio_message.link_ogg,
duration: apiAttach.audio_message.duration,
waveform: apiAttach.audio_message.waveform,
transcript: apiAttach.audio_message.transcript,
wasListened: wasVoiceMessageListened,
transcript: apiAttach.audio_message.transcript?.trim() ?? '',
transcriptState: apiAttach.audio_message.transcript_state
}
break
Expand Down Expand Up @@ -143,7 +147,7 @@ function fromApiAttachWall(apiWall: MessagesMessageAttachmentWall): Attach.Wall
accessKey: apiWall.access_key,
createdAt: (apiWall.date ?? 0) * 1000,
text: apiWall.text ?? '',
attaches: fromApiAttaches(apiWall.attachments ?? []),
attaches: fromApiAttaches(apiWall.attachments ?? [], true),
isDeleted: !!apiWall.is_deleted,
donutPlaceholder: apiWall.donut?.placeholder?.text,
repost: apiWall.copy_history?.[0]
Expand Down
6 changes: 3 additions & 3 deletions src/converters/MessageConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function fromApiMessage(message: MessagesMessage): Message.Message {
return {
kind: 'Normal',
text: message.text,
attaches: fromApiAttaches(message.attachments ?? []),
attaches: fromApiAttaches(message.attachments ?? [], !!message.was_listened),
replyMessage: message.reply_message && fromApiForeignMessage(
message.reply_message,
baseMessage.peerId,
Expand Down Expand Up @@ -164,7 +164,7 @@ function fromApiForeignMessage(
authorId: Peer.resolveOwnerId(foreignMessage.from_id),
sentAt: foreignMessage.date * 1000,
text: foreignMessage.text,
attaches: fromApiAttaches(foreignMessage.attachments ?? []),
attaches: fromApiAttaches(foreignMessage.attachments ?? [], !!foreignMessage.was_listened),
replyMessage: foreignMessage.reply_message && fromApiForeignMessage(
foreignMessage.reply_message,
rootPeerId,
Expand Down Expand Up @@ -211,7 +211,7 @@ export function fromApiPinnedMessage(pinnedMessage: MessagesPinnedMessage): Mess
authorId: Peer.resolveOwnerId(pinnedMessage.from_id),
sentAt: pinnedMessage.date * 1000,
text: pinnedMessage.text,
attaches: fromApiAttaches(pinnedMessage.attachments ?? []),
attaches: fromApiAttaches(pinnedMessage.attachments ?? [], true),
replyMessage: pinnedMessage.reply_message && fromApiForeignMessage(
pinnedMessage.reply_message,
peerId,
Expand Down
3 changes: 2 additions & 1 deletion src/lang/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ export const ru = {
me_chat_leaved_status: 'Вы вышли из чата',
me_chat_kicked_status: 'Вы были исключены из чата',

me_voice_transcription_empty: 'Слова не распознаны',
me_voice_transcription_error: 'Ошибка составления транскрипции',
me_voice_transcription_in_progress: 'Расшифровка...',
me_voice_transcript_empty: 'Слова не распознаны',

me_chat_members_count: {
one: '{count} участник',
Expand Down
3 changes: 2 additions & 1 deletion src/model/Attach.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export type Voice = {
linkOgg: string
duration: number
waveform: number[]
transcript?: string
wasListened: boolean
transcript: string
transcriptState: 'in_progress' | 'done' | 'error'
}

Expand Down
2 changes: 1 addition & 1 deletion src/model/api-types/objects/MessagesForeignMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export type MessagesForeignMessage = {
fwd_messages?: MessagesForeignMessage[]
geo?: MessagesMessageAttachmentGeo
id?: number
was_listened?: boolean
peer_id?: number
reply_message?: MessagesForeignMessage
text: string
update_time?: number
was_listened?: boolean
was_played?: boolean
payload?: string
is_expired?: boolean
Expand Down
62 changes: 23 additions & 39 deletions src/ui/messenger/attaches/AttachVoice/AttachVoice.css
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
.AttachVoice {
overflow: hidden;
}

.AttachVoice__player {
display: flex;
gap: 8px;
color: var(--vkui--color_background_accent_themed);
}

.AttachVoice__track {
display: flex;
align-items: center;
flex-grow: 1;
.AttachVoice__playButton {
color: var(--vkui--color_text_accent_themed);
}

.AttachVoice__trackContent {
flex: 1 1 auto;
.AttachVoice__track {
flex-grow: 1;
}

.AttachVoice__range {
Expand Down Expand Up @@ -47,19 +40,17 @@
box-shadow: -300px 0 0 300px var(--vkui--color_background_accent_themed);
}

.AttachVoice__button {
position: relative;
.AttachVoice__toggleTranscription {
align-self: flex-start;
border: none;
height: 20px;
width: 30px;
color: var(--vkui--color_text_accent_themed);
border-radius: 8px;
background: none;
margin-top: 2px;
margin-left: 12px;
padding: 2px 6px;
color: var(--vkui--color_icon_accent);
}

.AttachVoice__button::before {
.AttachVoice__toggleTranscription::before {
content: '';
border-radius: inherit;
position: absolute;
Expand All @@ -68,48 +59,41 @@
left: 0;
right: 0;
background: var(--vkui--color_background_accent);
opacity: 0.12;
transition: opacity 0.14s;
opacity: 0.1;
transition: opacity var(--fastTransition);
}

.AttachVoice__toggleTranscription:hover::before {
opacity: 0.2;
}

.AttachVoice__time {
display: inline-block;
font: var(--messageDateFontSize) / var(--messageDateLineHeight) var(--fontFamily);
color: var(--vkui--color_text_accent_themed);
}

.AttachVoice__transcript {
position: relative;
display: flex;
margin-top: 8px;
padding-left: 12px;
}

.AttachVoice__collapse {
position: relative;
overflow: hidden;
padding-left: 12px;
.AttachVoice__transcript--notReady {
opacity: 0.4;
}

.AttachVoice__collapse::before {
.AttachVoice__transcript::before {
content: '';
position: absolute;
top: 1px;
bottom: 0;
top: 0;
left: 0;
width: 2px;
height: 100%;
border-radius: 2px;
background: var(--vkui--color_stroke_accent);
}

.AttachVoice__collapse--open {
height: auto;
}

.AttachVoice__collapse--close {
height: 0;
white-space: pre;
}

.AttachVoice__collapse--faded {
.AttachVoice__transcript:not(.AttachVoice__transcript--notReady)::before {
opacity: 0.4;
font-size: 15px;
}
85 changes: 39 additions & 46 deletions src/ui/messenger/attaches/AttachVoice/AttachVoice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,20 @@ export const AttachVoice = defineComponent<Props>((props) => {
const range = shallowRef(0)
const isRange = shallowRef(false)
const requestId = shallowRef(-1)
const isHiddenCollapse = shallowRef(true)

const transcriptNotReady = computed(() => {
return !props.voice.transcript ||
props.voice.transcript.trim() === '' ||
props.voice.transcriptState === 'error' ||
props.voice.transcriptState === 'in_progress'
})

const text = computed(() => {
if (props.voice.transcript && props.voice.transcript.trim() === '') {
return lang.use('me_voice_transcription_empty')
}
const showTranscript = shallowRef(false)

const transcriptNotReadyStatus = computed(() => {
if (props.voice.transcriptState === 'error') {
return lang.use('me_voice_transcription_empty')
return lang.use('me_voice_transcription_error')
}

if (props.voice.transcriptState === 'in_progress') {
return lang.use('me_voice_transcription_in_progress')
}

return props.voice.transcript
if (!props.voice.transcript) {
return lang.use('me_voice_transcript_empty')
}
})

const getCurrentTime = () => {
Expand Down Expand Up @@ -90,44 +81,46 @@ export const AttachVoice = defineComponent<Props>((props) => {
<div class="AttachVoice">
<div class="AttachVoice__player">
<ButtonIcon
class="AttachVoice__playButton"
addHoverBackground={false}
onClick={toggleAudio}
icon={isPause.value ? <Icon32PauseCircle /> : <Icon32PlayCircle />}
icon={
isPause.value
? <Icon32PauseCircle withUnlistenedDot={!props.voice.wasListened} />
: <Icon32PlayCircle withUnlistenedDot={!props.voice.wasListened} />
}
/>

<div class="AttachVoice__track">
<div class="AttachVoice__trackContent">
<input
class="AttachVoice__range"
type="range"
id="track"
name="track"
value={range.value}
min="0"
max="100"
onChange={(event) => moveRange(event)}
onTouchstart={() => (isRange.value = true)}
onTouchend={() => (isRange.value = false)}
onMousedown={() => (isRange.value = true)}
onMouseup={() => (isRange.value = false)}
/>
<span class="AttachVoice__time">{getCurrentTime()}</span>
</div>
<ButtonIcon
class="AttachVoice__button"
icon={isHiddenCollapse.value ? <Icon16Text /> : <Icon20ChevronUp />}
onClick={() => (isHiddenCollapse.value = !isHiddenCollapse.value)}
<input
class="AttachVoice__range"
type="range"
step="0.1"
value={range.value}
min="0"
max="100"
onChange={(event) => moveRange(event)}
onTouchstart={() => (isRange.value = true)}
onTouchend={() => (isRange.value = false)}
onMousedown={() => (isRange.value = true)}
onMouseup={() => (isRange.value = false)}
/>
<span class="AttachVoice__time">{getCurrentTime()}</span>
</div>
<ButtonIcon
class="AttachVoice__toggleTranscription"
addHoverBackground={false}
icon={showTranscript.value ? <Icon20ChevronUp /> : <Icon16Text />}
onClick={() => (showTranscript.value = !showTranscript.value)}
/>
</div>
{!isHiddenCollapse.value && (
<div class="AttachVoice__transcript">
<div class={['AttachVoice__collapse', {
'AttachVoice__collapse--open': !isHiddenCollapse.value,
'AttachVoice__collapse--close': isHiddenCollapse.value,
'AttachVoice__collapse--faded': transcriptNotReady.value
{showTranscript.value && (
<div
class={['AttachVoice__transcript', {
'AttachVoice__transcript--notReady': transcriptNotReadyStatus.value
}]}
>
{text.value}
</div>
>
{transcriptNotReadyStatus.value ?? props.voice.transcript}
</div>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions src/ui/ui/ButtonIcon/ButtonIcon.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
flex: none;
position: relative;
border-radius: 6px;
line-height: 0;
transition: background-color var(--fastTransition), opacity var(--fastTransition);
}

Expand Down

0 comments on commit 3a847ca

Please sign in to comment.