feat: Allow filtering searches by 'source'
fix: Do not show API searches in recent list in app
This commit is contained in:
@@ -11,7 +11,9 @@ export default function SearchActions() {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!searches.isLoaded) {
|
if (!searches.isLoaded) {
|
||||||
void searches.fetchPage({});
|
void searches.fetchPage({
|
||||||
|
source: "app",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [searches]);
|
}, [searches]);
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,16 @@ import Model from "./base/Model";
|
|||||||
class SearchQuery extends Model {
|
class SearchQuery extends Model {
|
||||||
static modelName = "Search";
|
static modelName = "Search";
|
||||||
|
|
||||||
id: string;
|
/**
|
||||||
|
* The query string, automatically truncated to 255 characters.
|
||||||
|
*/
|
||||||
query: string;
|
query: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where the query originated.
|
||||||
|
*/
|
||||||
|
source: "api" | "app" | "slack";
|
||||||
|
|
||||||
delete = async () => {
|
delete = async () => {
|
||||||
this.isSaving = true;
|
this.isSaving = true;
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ function RecentSearches(
|
|||||||
const [isPreloaded] = React.useState(searches.recent.length > 0);
|
const [isPreloaded] = React.useState(searches.recent.length > 0);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
void searches.fetchPage({});
|
void searches.fetchPage({
|
||||||
|
source: "app",
|
||||||
|
});
|
||||||
}, [searches]);
|
}, [searches]);
|
||||||
|
|
||||||
const content = searches.recent.length ? (
|
const content = searches.recent.length ? (
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ export default class SearchesStore extends Store<SearchQuery> {
|
|||||||
|
|
||||||
@computed
|
@computed
|
||||||
get recent(): SearchQuery[] {
|
get recent(): SearchQuery[] {
|
||||||
return uniqBy(this.orderedData, "query").slice(0, 8);
|
return uniqBy(this.orderedData, "query")
|
||||||
|
.filter((search) => search.source === "app")
|
||||||
|
.slice(0, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export default function presentSearchQuery(searchQuery: SearchQuery) {
|
|||||||
return {
|
return {
|
||||||
id: searchQuery.id,
|
id: searchQuery.id,
|
||||||
query: searchQuery.query,
|
query: searchQuery.query,
|
||||||
|
source: searchQuery.source,
|
||||||
createdAt: searchQuery.createdAt,
|
createdAt: searchQuery.createdAt,
|
||||||
answer: searchQuery.answer,
|
answer: searchQuery.answer,
|
||||||
score: searchQuery.score,
|
score: searchQuery.score,
|
||||||
|
|||||||
@@ -21,3 +21,13 @@ export const SearchesUpdateSchema = BaseSchema.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export type SearchesUpdateReq = z.infer<typeof SearchesUpdateSchema>;
|
export type SearchesUpdateReq = z.infer<typeof SearchesUpdateSchema>;
|
||||||
|
|
||||||
|
export const SearchesListSchema = BaseSchema.extend({
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
source: z.string().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type SearchesListReq = z.infer<typeof SearchesListSchema>;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ describe("#searches.list", () => {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: user.teamId,
|
teamId: user.teamId,
|
||||||
query: "bar",
|
query: "bar",
|
||||||
|
source: "api",
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -42,6 +43,18 @@ describe("#searches.list", () => {
|
|||||||
expect(queries).toContain("foo");
|
expect(queries).toContain("foo");
|
||||||
expect(queries).toContain("bar");
|
expect(queries).toContain("bar");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should allow filtering by source", async () => {
|
||||||
|
const res = await server.post("/api/searches.list", {
|
||||||
|
body: {
|
||||||
|
token: user.getJwtToken(),
|
||||||
|
source: "api",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toHaveLength(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#searches.update", () => {
|
describe("#searches.update", () => {
|
||||||
|
|||||||
@@ -10,23 +10,32 @@ import * as T from "./schema";
|
|||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
router.post("searches.list", auth(), pagination(), async (ctx: APIContext) => {
|
router.post(
|
||||||
const { user } = ctx.state.auth;
|
"searches.list",
|
||||||
|
auth(),
|
||||||
|
validate(T.SearchesListSchema),
|
||||||
|
pagination(),
|
||||||
|
async (ctx: APIContext<T.SearchesListReq>) => {
|
||||||
|
const { user } = ctx.state.auth;
|
||||||
|
const source = ctx.input.body?.source;
|
||||||
|
|
||||||
const searches = await SearchQuery.findAll({
|
const searches = await SearchQuery.findAll({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
...(source ? { source } : {}),
|
||||||
},
|
teamId: user.teamId,
|
||||||
order: [["createdAt", "DESC"]],
|
userId: user.id,
|
||||||
offset: ctx.state.pagination.offset,
|
},
|
||||||
limit: ctx.state.pagination.limit,
|
order: [["createdAt", "DESC"]],
|
||||||
});
|
offset: ctx.state.pagination.offset,
|
||||||
|
limit: ctx.state.pagination.limit,
|
||||||
|
});
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
pagination: ctx.state.pagination,
|
pagination: ctx.state.pagination,
|
||||||
data: searches.map(presentSearchQuery),
|
data: searches.map(presentSearchQuery),
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
"searches.update",
|
"searches.update",
|
||||||
|
|||||||
Reference in New Issue
Block a user