Skip to content

Commit 9fed8f7

Browse files
committed
feat: screen resolution stuffs
1 parent 85aaf16 commit 9fed8f7

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

apps/api/src/query/simple-builder.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
} from './types';
88
import { FilterOperators } from './types';
99
import { applyPlugins } from './utils';
10+
import { mapScreenResolutionToDeviceType, type DeviceType } from './screen-resolution-to-device-type';
1011

1112
export class SimpleQueryBuilder {
1213
private config: SimpleQueryConfig;
@@ -23,6 +24,60 @@ export class SimpleQueryBuilder {
2324
this.websiteDomain = websiteDomain;
2425
}
2526

27+
private getDeviceTypeFilterCondition(deviceType: DeviceType): string {
28+
// Create SQL condition that matches the same logic as mapScreenResolutionToDeviceType
29+
// This replicates the heuristics from screen-resolution-to-device-type.ts in SQL
30+
31+
// First, get common/known resolutions for exact matches
32+
const commonResolutions: Record<string, DeviceType> = {
33+
'896x414': 'mobile', '844x390': 'mobile', '932x430': 'mobile', '800x360': 'mobile',
34+
'780x360': 'mobile', '736x414': 'mobile', '667x375': 'mobile', '640x360': 'mobile', '568x320': 'mobile',
35+
'1366x1024': 'tablet', '1280x800': 'tablet', '1180x820': 'tablet', '1024x768': 'tablet', '1280x720': 'tablet',
36+
'1366x768': 'laptop', '1440x900': 'laptop', '1536x864': 'laptop',
37+
'1920x1080': 'desktop', '2560x1440': 'desktop', '3840x2160': 'desktop',
38+
'3440x1440': 'ultrawide', '3840x1600': 'ultrawide', '5120x1440': 'ultrawide',
39+
};
40+
41+
const exactMatches = Object.entries(commonResolutions)
42+
.filter(([_, type]) => type === deviceType)
43+
.map(([resolution, _]) => `'${resolution}'`)
44+
.join(', ');
45+
46+
// SQL for parsing resolution dimensions
47+
const widthExpr = "toFloat64(substring(screen_resolution, 1, position(screen_resolution, 'x') - 1))";
48+
const heightExpr = "toFloat64(substring(screen_resolution, position(screen_resolution, 'x') + 1))";
49+
const longSideExpr = `greatest(${widthExpr}, ${heightExpr})`;
50+
const shortSideExpr = `least(${widthExpr}, ${heightExpr})`;
51+
const aspectExpr = `${longSideExpr} / ${shortSideExpr}`;
52+
53+
// Device type heuristics (matching screen-resolution-to-device-type.ts logic)
54+
const heuristicCondition = (() => {
55+
switch (deviceType) {
56+
case 'mobile':
57+
return `(${shortSideExpr} <= 480)`;
58+
case 'tablet':
59+
return `(${shortSideExpr} <= 900 AND ${shortSideExpr} > 480)`;
60+
case 'laptop':
61+
return `(${longSideExpr} <= 1600 AND ${shortSideExpr} > 900)`;
62+
case 'desktop':
63+
return `(${longSideExpr} <= 3000 AND ${longSideExpr} > 1600)`;
64+
case 'ultrawide':
65+
return `(${aspectExpr} >= 2.0 AND ${longSideExpr} >= 2560)`;
66+
case 'watch':
67+
return `(${longSideExpr} <= 400 AND ${aspectExpr} >= 0.85 AND ${aspectExpr} <= 1.15)`;
68+
case 'unknown':
69+
default:
70+
return '1 = 0'; // Never matches
71+
}
72+
})();
73+
74+
// Combine exact matches and heuristics
75+
if (exactMatches) {
76+
return `(screen_resolution IN (${exactMatches}) OR ${heuristicCondition})`;
77+
}
78+
return heuristicCondition;
79+
}
80+
2681
private buildFilter(filter: Filter, index: number) {
2782
if (
2883
this.config.allowedFilters &&
@@ -94,6 +149,18 @@ export class SimpleQueryBuilder {
94149
};
95150
}
96151

152+
// Special handling for device_type filters - convert to screen_resolution filters
153+
if (filter.field === 'device_type' && typeof filter.value === 'string') {
154+
const deviceType = filter.value as DeviceType;
155+
const condition = this.getDeviceTypeFilterCondition(deviceType);
156+
157+
// Return the condition directly without parameters since it's self-contained
158+
return {
159+
clause: condition,
160+
params: {},
161+
};
162+
}
163+
97164
if (filter.op === 'like') {
98165
return {
99166
clause: `${filter.field} ${operator} {${key}:String}`,

0 commit comments

Comments
 (0)