Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement related-prompts multi-query #1685

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
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
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { endpointAdapterFactory, interpolate } from '@empathyco/x-adapter';
import { endpointAdapterFactory } from '@empathyco/x-adapter';
import { RelatedPromptsRequest, RelatedPromptsResponse } from '@empathyco/x-types';
import { relatedPromptsRequestMapper } from '../mappers/requests/related-prompts-request.mapper';
import { relatedPromptsResponseMapper } from '../mappers/responses/related-prompts-response.mapper';
import { getBeaconServiceUrl } from './utils';

/**
* Default adapter for the related prompt endpoint.
Expand All @@ -14,12 +12,13 @@ export const relatedPromptsEndpointAdapter = endpointAdapterFactory<
RelatedPromptsRequest,
RelatedPromptsResponse
>({
endpoint: from =>
interpolate(`${getBeaconServiceUrl(from)}/relatedprompts/{extraParams.instance}`, from),
requestMapper: relatedPromptsRequestMapper,
endpoint:
'https://api.empathy.co/relatedprompts/mymotivemarketplace?store=Labstore+London&lang=en',
requestMapper: ({ query }) => ({ query }),
responseMapper: relatedPromptsResponseMapper,
Comment on lines +15 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess those changes are not intended, are they?

defaultRequestOptions: {
id: 'related-prompts',
cancelable: false,
parameters: {
internal: true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export const relatedPromptSchema = createMutableSchema<PlatformRelatedPrompt, Re
modelName: () => 'RelatedPrompt',
nextQueries: 'nextQueries',
suggestionText: 'suggestionText',
type: 'type'
type: 'type',
id: 'uuid'
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export interface PlatformRelatedPrompt {
nextQueries: string[];
suggestionText: string;
type: 'SYNTHETIC' | 'CURATED';
uuid: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export function createRelatedPromptStub(suggestionText: string): RelatedPrompt {
suggestionText,
nextQueries: createNextQueriesArrayStub(10),
modelName: 'RelatedPrompt',
type: Math.random() < 0.5 ? 'CURATED' : 'SYNTHETIC'
type: Math.random() < 0.5 ? 'CURATED' : 'SYNTHETIC',
id: 'abc-' + Math.random().toString()
};
}

Expand Down
365 changes: 211 additions & 154 deletions packages/x-components/src/views/home/Home.vue

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div
@click="toggleSuggestion(index)"
@keydown="toggleSuggestion(index)"
@click="toggleSuggestion(promptId)"
@keydown="toggleSuggestion(promptId)"
class="x-related-prompt__button"
:class="[{ 'x-related-prompt-selected__button': isSelected }]"
role="button"
Expand All @@ -27,12 +27,12 @@
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { computed, defineComponent, PropType } from 'vue';
import { RelatedPrompt } from '@empathyco/x-types';
import { relatedPromptsXModule } from '../x-module';
import CrossTinyIcon from '../../../components/icons/cross-tiny.vue';
import PlusIcon from '../../../components/icons/plus.vue';
import { use$x } from '../../../composables/index';
import { use$x, useState } from '../../../composables/index';

/**
* This component shows a suggested related prompt.
Expand Down Expand Up @@ -61,20 +61,34 @@
type: Boolean,
default: false
},
index: {
type: Number,
promptId: {
type: String,
required: true
},
query: {
type: String,
required: true
}
},
setup() {
setup(props) {
const x = use$x();

const toggleSuggestion = (index: number): void => {
x.emit('UserSelectedARelatedPrompt', index);
const queryRelatedPrompts = useState('relatedPrompts', ['relatedPrompts']).relatedPrompts;

const relatedPrompts = computed(
(): RelatedPrompt[] => queryRelatedPrompts.value[props.query]?.relatedPromptsProducts
);

const index = relatedPrompts.value.findIndex(
relatedPrompt => relatedPrompt.id === props.promptId
);
const toggleSuggestion = (promptId: string): void => {
x.emit('UserSelectedARelatedPrompt', { promptId, query: props.query });
};

return {
toggleSuggestion
toggleSuggestion,
index
};
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { computed, ComputedRef, defineComponent, h, inject, provide, Ref } from 'vue';
import { computed, defineComponent, h, inject, provide, ref, Ref, watch } from 'vue';
import { RelatedPrompt } from '@empathyco/x-types';
import { AnimationProp } from '../../../types/animation-prop';
import { groupItemsBy } from '../../../utils/array';
Expand All @@ -13,6 +13,7 @@
import { relatedPromptsXModule } from '../x-module';
import { useState } from '../../../composables/use-state';
import { RelatedPromptsGroup } from '../types';
import { use$x } from '../../../composables/index';

/**
* Component that inserts groups of related prompts in different positions of the injected search
Expand Down Expand Up @@ -66,23 +67,40 @@
showOnlyAfterOffset: {
type: Boolean,
default: false
}
},
query: String
},
setup(props, { slots }) {
const { query, status } = useState('relatedPrompts', ['query', 'status']);

/**
* The state related prompts.
*/
const relatedPrompts: ComputedRef<RelatedPrompt[]> = useState('relatedPrompts', [
'relatedPrompts'
]).relatedPrompts;
const x = use$x();
const { status, relatedPrompts } = useState('relatedPrompts', ['status', 'relatedPrompts']);

/**
* Injected query, updated when the related request(s) have succeeded.
*/
const injectedQuery = inject<Ref<string | undefined>>(QUERY_KEY as string);

const relatedPromptsProducts: Ref<RelatedPrompt[]> = ref([]);

watch(
() => props.query,
() => {
if (props.query || props.query !== '') {
x.emit('RelatedPromptsQueryProvided', props.query);
}
}
);

watch(
relatedPrompts,
() => {
if (relatedPrompts.value[props.query ?? x.query.search]) {
relatedPromptsProducts.value =
relatedPrompts.value[props.query ?? x.query.search].relatedPromptsProducts;
}
},
{ deep: true }
);

/**
* Indicates if there are more available results than the injected.
*/
Expand All @@ -93,9 +111,9 @@
*
* @returns A list of related prompts groups.
*/
const relatedPromptsGroups = computed<RelatedPromptsGroup[]>(() =>
Object.values(
groupItemsBy(relatedPrompts.value, (_, index) =>
const relatedPromptsGroups = computed<RelatedPromptsGroup[]>(() => {
return Object.values(
groupItemsBy(relatedPromptsProducts.value, (_, index) =>
Math.floor(index / props.maxRelatedPromptsPerGroup)
)
)
Expand All @@ -104,8 +122,8 @@
modelName: 'RelatedPromptsGroup' as const,
id: `related-prompts-group-${index}`,
relatedPrompts
}))
);
}));
});

/**
* It injects {@link ListItem} provided by an ancestor as injectedListItems.
Expand All @@ -118,9 +136,7 @@
* @returns True if the related prompts are outdated, false if not.
*/
const relatedPromptsAreOutdated = computed(
() =>
!!injectedQuery?.value &&
(query.value !== injectedQuery.value || status.value !== 'success')
() => !!injectedQuery?.value && status.value !== 'success'
);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
:reset-on-content-change="true"
:button-class="buttonClass"
:scroll-container-class="
selectedPrompt === -1 ? 'desktop:x-sliding-panel-fade desktop:x-sliding-panel-fade-sm' : ''
selectedPrompt === '' ? 'desktop:x-sliding-panel-fade desktop:x-sliding-panel-fade-sm' : ''
"
>
<template #sliding-panel-left-button>
Expand All @@ -22,27 +22,28 @@
>
<div
v-for="(suggestion, index) in relatedPrompts"
:key="index"
:key="suggestion.id"
:style="{
animationDelay: `${index * 0.4 + 0.05}s`
}"
class="x-related-prompt x-staggered-initial"
:class="[
{ 'x-staggered-animation': arePromptsVisible },
{ 'x-hidden': hidePrompt(index) },
{ 'x-related-prompt-selected': isSelected(index) }
{ 'x-hidden': hidePrompt(suggestion.id) },
{ 'x-related-prompt-selected': isSelected(suggestion.id) }
]"
data-test="related-prompt-item"
>
<slot
name="related-prompt-button"
v-bind="{ suggestion, index, arePromptsVisible, isSelected }"
v-bind="{ suggestion, arePromptsVisible, isSelected, relatedPromptQuery }"
>
<RelatedPrompt
:related-prompt="suggestion"
:index="index"
:promptId="suggestion.id"
:is-prompt-visible="arePromptsVisible"
:is-selected="isSelected(index)"
:is-selected="isSelected(suggestion.id)"
:query="relatedPromptQuery"
/>
</slot>
</div>
Expand All @@ -56,24 +57,34 @@
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, onUnmounted, ref } from 'vue';
import { computed, defineComponent, onMounted, onUnmounted, ref } from 'vue';
import SlidingPanel from '../../../components/sliding-panel.vue';
import { relatedPromptsXModule } from '../x-module';
import { useState } from '../../../composables/index';
import { use$x, useState } from '../../../composables/index';
import RelatedPrompt from './related-prompt.vue';

export default defineComponent({
name: 'RelatedPromptsTagList',
xModule: relatedPromptsXModule.name,
components: { RelatedPrompt, SlidingPanel },
props: {
buttonClass: String
buttonClass: String,
query: String
},
setup() {
const { relatedPrompts, selectedPrompt } = useState('relatedPrompts', [
'relatedPrompts',
'selectedPrompt'
]);
setup(props) {
const x = use$x();

const relatedPromptQuery = computed(() => props.query ?? x.query.search);

const queryRelatedPrompts = useState('relatedPrompts', ['relatedPrompts']).relatedPrompts;

const relatedPrompts = computed(
() => queryRelatedPrompts.value[relatedPromptQuery.value]?.relatedPromptsProducts
);

const selectedPrompt = computed(
() => queryRelatedPrompts.value[relatedPromptQuery.value]?.selectedPrompt
);

const slidingPanelContent = ref<Element>();
const arePromptsVisible = ref(false);
Expand All @@ -90,18 +101,19 @@
observer.disconnect();
});

const isSelected = (index: number): boolean => selectedPrompt.value === index;
const isSelected = (id: string): boolean => selectedPrompt.value === id;

const hidePrompt = (index: number): boolean =>
selectedPrompt.value !== -1 && selectedPrompt.value !== index;
const hidePrompt = (id: string): boolean =>
selectedPrompt.value !== '' && selectedPrompt.value !== id;

return {
arePromptsVisible,
hidePrompt,
isSelected,
relatedPrompts,
selectedPrompt,
slidingPanelContent
slidingPanelContent,
relatedPromptQuery
};
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ export interface RelatedPromptsXEvents {
* The selected prompt has changed.
* Payload: The index of the prompt in the RelatedPrompts list or -1 to remove selection.
*/
UserSelectedARelatedPrompt: number;
UserSelectedARelatedPrompt: { promptId: string; query: string };
/**
* The selected next query of the selected prompt has changed.
* Payload: The index of the next query in the NextQueries list or -1 to remove selection.
*/
UserSelectedARelatedPromptQuery: number;
/**
* A custom query to request has been provided.
* Payload: The query to add to the request.
*/
RelatedPromptsQueryProvided: string;
}
Loading
Loading