tangled
alpha
login
or
join now
byarielm.fyi
/
atlast
16
fork
atom
ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto
16
fork
atom
overview
issues
1
pulls
pipelines
allow opt-out of data save
byarielm.fyi
3 months ago
1d957063
23e93cea
+78
-22
5 changed files
expand all
collapse all
unified
split
netlify
functions
save-results.ts
src
App.tsx
components
HistoryTab.tsx
hooks
useFileUpload.ts
types
index.ts
+23
-1
netlify/functions/save-results.ts
···
35
35
uploadId: string;
36
36
sourcePlatform: string;
37
37
results: SearchResult[];
38
38
+
saveData?: boolean;
38
39
}
39
40
40
41
export const handler: Handler = async (
···
75
76
76
77
// Parse request body
77
78
const body: SaveResultsRequest = JSON.parse(event.body || "{}");
78
78
-
const { uploadId, sourcePlatform, results } = body;
79
79
+
const { uploadId, sourcePlatform, results, saveData } = body;
79
80
80
81
if (!uploadId || !sourcePlatform || !Array.isArray(results)) {
81
82
return {
···
83
84
headers: { "Content-Type": "application/json" },
84
85
body: JSON.stringify({
85
86
error: "uploadId, sourcePlatform, and results are required",
87
87
+
}),
88
88
+
};
89
89
+
}
90
90
+
91
91
+
// Server-side validation for saveData flag, controlled by frontend
92
92
+
if (saveData === false) {
93
93
+
console.log(
94
94
+
`User ${userSession.did} has data storage disabled - skipping save`,
95
95
+
);
96
96
+
return {
97
97
+
statusCode: 200,
98
98
+
headers: { "Content-Type": "application/json" },
99
99
+
body: JSON.stringify({
100
100
+
success: true,
101
101
+
message: "Data storage disabled - results not saved",
102
102
+
uploadId,
103
103
+
totalUsers: results.length,
104
104
+
matchedUsers: results.filter((r) => r.atprotoMatches.length > 0)
105
105
+
.length,
106
106
+
unmatchedUsers: results.filter((r) => r.atprotoMatches.length === 0)
107
107
+
.length,
86
108
}),
87
109
};
88
110
}
+24
-17
src/App.tsx
···
83
83
84
84
searchAllUsers(initialResults, setStatusMessage, () => {
85
85
setCurrentStep("results");
86
86
-
// Prevent duplicate saves
87
87
-
if (saveCalledRef.current !== uploadId) {
88
88
-
saveCalledRef.current = uploadId;
89
89
-
// Need to wait for React to finish updating searchResults state
90
90
-
// Use a longer delay and access via setSearchResults callback to get final state
91
91
-
setTimeout(() => {
92
92
-
setSearchResults((currentResults) => {
93
93
-
if (currentResults.length > 0) {
94
94
-
apiClient
95
95
-
.saveResults(uploadId, platform, currentResults)
96
96
-
.catch((err) => {
97
97
-
console.error("Background save failed:", err);
98
98
-
});
99
99
-
}
100
100
-
return currentResults; // Don't modify, just return as-is
101
101
-
});
102
102
-
}, 1000); // Longer delay to ensure all state updates complete
86
86
+
87
87
+
// CONDITIONAL SAVE: Only save if user has enabled data storage
88
88
+
if (userSettings.saveData) {
89
89
+
// Prevent duplicate saves
90
90
+
if (saveCalledRef.current !== uploadId) {
91
91
+
saveCalledRef.current = uploadId;
92
92
+
// Need to wait for React to finish updating searchResults state
93
93
+
// Use a longer delay and access via setSearchResults callback to get final state
94
94
+
setTimeout(() => {
95
95
+
setSearchResults((currentResults) => {
96
96
+
if (currentResults.length > 0) {
97
97
+
apiClient
98
98
+
.saveResults(uploadId, platform, currentResults)
99
99
+
.catch((err) => {
100
100
+
console.error("Background save failed:", err);
101
101
+
});
102
102
+
}
103
103
+
return currentResults; // Don't modify, just return as-is
104
104
+
});
105
105
+
}, 1000); // Longer delay to ensure all state updates complete
106
106
+
}
107
107
+
} else {
108
108
+
console.log("Data storage disabled - skipping save to database");
103
109
}
104
110
});
105
111
},
106
112
setStatusMessage,
113
113
+
userSettings, // Pass userSettings to hook
107
114
);
108
115
109
116
// Load previous upload handler
+21
-3
src/components/HistoryTab.tsx
···
1
1
-
import { Upload, Sparkles, ChevronRight } from "lucide-react";
1
1
+
import { Upload, Sparkles, ChevronRight, Database } from "lucide-react";
2
2
import { ATPROTO_APPS } from "../constants/atprotoApps";
3
3
import type { Upload as UploadType } from "../types";
4
4
import type { UserSettings } from "../types/settings";
···
77
77
</div>
78
78
</div>
79
79
80
80
+
{/* Data Storage Disabled Notice */}
81
81
+
{!userSettings.saveData && (
82
82
+
<div className="mb-4 p-4 border-2 rounded-xl border-orange-650/50 dark:border-amber-400/50 bg-purple-100/50 dark:bg-slate-900/50">
83
83
+
<div className="flex items-start space-x-3">
84
84
+
<Database className="w-5 h-5 text-orange-600 dark:text-amber-400 flex-shrink-0 mt-0.5" />
85
85
+
<div>
86
86
+
<h3 className="font-semibold text-purple-950 dark:text-cyan-50 mb-1">
87
87
+
Data Storage Disabled
88
88
+
</h3>
89
89
+
<p className="text-sm text-purple-900 dark:text-cyan-100">
90
90
+
You've disabled data storage in your settings. Enable "Save my
91
91
+
data" in the Settings tab to save your upload history.
92
92
+
</p>
93
93
+
</div>
94
94
+
</div>
95
95
+
</div>
96
96
+
)}
97
97
+
80
98
{isLoading ? (
81
99
<div className="space-y-6">
82
100
{[...Array(3)].map((_, i) => (
···
94
112
</div>
95
113
) : uploads.length === 0 ? (
96
114
<div className="text-center py-12">
97
97
-
<Upload className="w-16 h-16 text-purple-300 dark:text-slate-600 mx-auto mb-4" />
115
115
+
<Upload className="w-16 h-16 text-purple-900 dark:text-cyan-100 mx-auto mb-4" />
98
116
<p className="text-purple-750 dark:text-cyan-250 font-medium">
99
117
No previous uploads yet
100
118
</p>
101
101
-
<p className="text-sm text-purple-750/70 dark:text-cyan-250/70 mt-2">
119
119
+
<p className="text-sm text-purple-950 dark:text-cyan-50 mt-2">
102
120
Upload your first file to get started
103
121
</p>
104
122
</div>
+2
-1
src/hooks/useFileUpload.ts
···
1
1
import { parseDataFile } from "../lib/fileExtractor";
2
2
-
import type { SearchResult } from "../types";
2
2
+
import type { SearchResult, UserSettings } from "../types";
3
3
4
4
export function useFileUpload(
5
5
onSearchStart: (results: SearchResult[], platform: string) => void,
6
6
onStatusUpdate: (message: string) => void,
7
7
+
userSettings: UserSettings,
7
8
) {
8
9
async function handleFileUpload(
9
10
e: React.ChangeEvent<HTMLInputElement>,
+8
src/types/index.ts
···
81
81
matchedUsers: number;
82
82
unmatchedUsers: number;
83
83
}
84
84
+
85
85
+
// Re-export settings types for convenience
86
86
+
export type {
87
87
+
UserSettings,
88
88
+
PlatformDestinations,
89
89
+
AtprotoApp,
90
90
+
AtprotoAppId,
91
91
+
} from "./settings";