Snapchat Mobile View for Desktop

Transform Snapchat web into mobile app experience on desktop with full mobile features and layout

Size

31.0 KB

Version

1.1.1

Created

Nov 18, 2025

Updated

3 months ago

1// ==UserScript==
2// @name		Snapchat Mobile View for Desktop
3// @description		Transform Snapchat web into mobile app experience on desktop with full mobile features and layout
4// @version		1.1.1
5// @match		https://*.snapchat.com/*
6// @icon		https://www.snapchat.com/web/version/22849690/favicon-badged.svg
7// @grant		GM.getValue
8// @grant		GM.setValue
9// @grant		GM.xmlhttpRequest
10// ==/UserScript==
11(function() {
12    'use strict';
13
14    console.log('Snapchat Mobile View Extension: Initializing...');
15
16    // Mobile User Agent Configuration
17    const MOBILE_USER_AGENTS = {
18        ios: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
19        android: 'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36'
20    };
21
22    // Override User Agent
23    function setMobileUserAgent() {
24        // Override navigator properties
25        Object.defineProperty(navigator, 'userAgent', {
26            get: function() { return MOBILE_USER_AGENTS.android; },
27            configurable: true
28        });
29        
30        Object.defineProperty(navigator, 'platform', {
31            get: function() { return 'Linux armv8l'; },
32            configurable: true
33        });
34        
35        Object.defineProperty(navigator, 'maxTouchPoints', {
36            get: function() { return 5; },
37            configurable: true
38        });
39
40        console.log('Snapchat Mobile View: User agent set to mobile');
41    }
42
43    // Apply Mobile Viewport and Styling
44    function applyMobileLayout() {
45        console.log('Snapchat Mobile View: Applying mobile layout...');
46
47        // Set viewport meta tag for mobile
48        let viewport = document.querySelector('meta[name="viewport"]');
49        if (!viewport) {
50            viewport = document.createElement('meta');
51            viewport.name = 'viewport';
52            document.head.appendChild(viewport);
53        }
54        viewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
55
56        // Add mobile-optimized CSS
57        const mobileStyles = `
58            /* Mobile Container Wrapper */
59            body {
60                margin: 0 auto !important;
61                padding: 0 !important;
62                max-width: 430px !important;
63                background: #000 !important;
64                overflow-x: hidden !important;
65            }
66
67            html {
68                background: #1a1a1a !important;
69                overflow-x: hidden !important;
70            }
71
72            /* Center the mobile view */
73            #root {
74                max-width: 430px !important;
75                margin: 0 auto !important;
76                background: #000 !important;
77                min-height: 100vh !important;
78                position: relative !important;
79            }
80
81            /* Mobile-style main container */
82            main {
83                max-width: 430px !important;
84                margin: 0 auto !important;
85            }
86
87            /* Adjust layout for mobile view */
88            .Fpg8t {
89                display: flex !important;
90                flex-direction: column !important;
91                max-width: 430px !important;
92            }
93
94            /* Left sidebar - make it full width on mobile */
95            .BL7do {
96                width: 100% !important;
97                max-width: 430px !important;
98                min-width: unset !important;
99                border-right: none !important;
100            }
101
102            /* Main content area */
103            .Vbjsg {
104                width: 100% !important;
105                max-width: 430px !important;
106                position: relative !important;
107            }
108
109            /* Hide desktop-only elements */
110            .VqwkQ {
111                display: none !important;
112            }
113
114            /* Mobile-optimized buttons */
115            button {
116                -webkit-tap-highlight-color: rgba(255, 255, 255, 0.1) !important;
117                touch-action: manipulation !important;
118            }
119
120            /* Optimize touch targets */
121            button, a, input {
122                min-height: 44px !important;
123                min-width: 44px !important;
124            }
125
126            /* Mobile-friendly scrolling */
127            * {
128                -webkit-overflow-scrolling: touch !important;
129            }
130
131            /* Camera view optimization */
132            .ChLAv {
133                width: 100% !important;
134                max-width: 430px !important;
135            }
136
137            /* Search bar mobile styling */
138            ._S446 {
139                width: 100% !important;
140                padding: 8px 12px !important;
141            }
142
143            /* Chat list mobile optimization */
144            nav.deg2K {
145                width: 100% !important;
146                max-width: 430px !important;
147            }
148
149            /* Profile and settings */
150            .XlW_1 {
151                padding: 12px !important;
152            }
153
154            /* Mobile notification banner */
155            .wHvEy {
156                width: 100% !important;
157                padding: 16px !important;
158            }
159
160            /* Responsive images */
161            img {
162                max-width: 100% !important;
163                height: auto !important;
164            }
165
166            /* Mobile-style modals and overlays */
167            [role="dialog"] {
168                max-width: 430px !important;
169                margin: 0 auto !important;
170            }
171
172            /* Bottom navigation style (if present) */
173            .toJ1p {
174                display: flex !important;
175                justify-content: space-around !important;
176                padding: 12px !important;
177            }
178
179            /* Optimize for mobile gestures */
180            .swipeable {
181                touch-action: pan-y !important;
182            }
183
184            /* Hide scrollbars for cleaner mobile look */
185            ::-webkit-scrollbar {
186                width: 0px !important;
187                background: transparent !important;
188            }
189
190            /* Mobile-optimized input fields */
191            input[type="text"], 
192            input[type="search"],
193            textarea {
194                font-size: 16px !important; /* Prevents zoom on iOS */
195                padding: 12px !important;
196            }
197
198            /* Status bar space (for mobile feel) */
199            body::before {
200                content: '';
201                display: block;
202                height: env(safe-area-inset-top, 0px);
203                background: #000;
204            }
205
206            /* Mobile-style animations */
207            * {
208                transition: transform 0.2s ease, opacity 0.2s ease !important;
209            }
210
211            /* Active state for touch feedback */
212            button:active, 
213            a:active {
214                transform: scale(0.95) !important;
215                opacity: 0.8 !important;
216            }
217
218            /* Ensure full-screen camera */
219            ._HpJ5 {
220                width: 100% !important;
221                height: auto !important;
222                object-fit: cover !important;
223            }
224
225            /* Mobile-optimized lens carousel */
226            .O7Nhq {
227                overflow-x: auto !important;
228                -webkit-overflow-scrolling: touch !important;
229            }
230
231            /* Chat bubbles mobile style */
232            .message-bubble {
233                max-width: 80% !important;
234                word-wrap: break-word !important;
235            }
236        `;
237
238        TM_addStyle(mobileStyles);
239        console.log('Snapchat Mobile View: Mobile styles applied');
240    }
241
242    // Add mobile device simulation
243    function simulateMobileDevice() {
244        // Add touch event support
245        window.ontouchstart = function() {};
246        
247        // Simulate mobile screen dimensions
248        Object.defineProperty(window.screen, 'width', {
249            get: function() { return 430; },
250            configurable: true
251        });
252        
253        Object.defineProperty(window.screen, 'height', {
254            get: function() { return 932; },
255            configurable: true
256        });
257
258        console.log('Snapchat Mobile View: Mobile device simulation enabled');
259    }
260
261    // Add mobile-specific features
262    function addMobileFeatures() {
263        // Enable pull-to-refresh simulation
264        let startY = 0;
265        let isPulling = false;
266
267        document.addEventListener('touchstart', function(e) {
268            startY = e.touches[0].pageY;
269            isPulling = window.scrollY === 0;
270        }, { passive: true });
271
272        document.addEventListener('touchmove', function(e) {
273            if (isPulling && e.touches[0].pageY > startY + 100) {
274                console.log('Snapchat Mobile View: Pull to refresh triggered');
275                // Snapchat will handle the actual refresh
276            }
277        }, { passive: true });
278
279        console.log('Snapchat Mobile View: Mobile features added');
280    }
281
282    // Monitor for dynamic content and reapply styles
283    function observePageChanges() {
284        const observer = new MutationObserver(function() {
285            // Ensure mobile layout persists
286            const root = document.getElementById('root');
287            if (root && !root.style.maxWidth) {
288                applyMobileLayout();
289            }
290        });
291
292        observer.observe(document.body, {
293            childList: true,
294            subtree: true
295        });
296
297        console.log('Snapchat Mobile View: Page observer initialized');
298    }
299
300    // Intercept network requests to force mobile API
301    function interceptRequests() {
302        const originalFetch = window.fetch;
303        window.fetch = function(...args) {
304            const [url, options = {}] = args;
305            
306            // Force mobile headers on all Snapchat API requests
307            if (typeof url === 'string' && url.includes('snapchat.com')) {
308                options.headers = options.headers || {};
309                options.headers['User-Agent'] = MOBILE_USER_AGENTS.android;
310                options.headers['X-Snapchat-Client-Type'] = 'mobile';
311                
312                console.log('Snapchat Mobile View: Intercepted request to', url);
313            }
314            
315            return originalFetch.apply(this, [url, options]);
316        };
317
318        // Intercept XMLHttpRequest
319        const originalOpen = XMLHttpRequest.prototype.open;
320        XMLHttpRequest.prototype.open = function(method, url, ...rest) {
321            this._url = url;
322            return originalOpen.apply(this, [method, url, ...rest]);
323        };
324
325        const originalSend = XMLHttpRequest.prototype.send;
326        XMLHttpRequest.prototype.send = function(...args) {
327            if (this._url && this._url.includes('snapchat.com')) {
328                this.setRequestHeader('User-Agent', MOBILE_USER_AGENTS.android);
329                this.setRequestHeader('X-Snapchat-Client-Type', 'mobile');
330                console.log('Snapchat Mobile View: Intercepted XHR to', this._url);
331            }
332            return originalSend.apply(this, args);
333        };
334
335        console.log('Snapchat Mobile View: Request interceptor enabled');
336    }
337
338    // Snap Download Feature
339    function addSnapDownloadFeature() {
340        console.log('Snapchat Mobile View: Adding snap download feature...');
341
342        // Create download button style
343        const downloadButtonStyle = `
344            .snap-download-btn {
345                position: fixed;
346                bottom: 80px;
347                right: 20px;
348                width: 56px;
349                height: 56px;
350                background: linear-gradient(135deg, #FFFC00 0%, #FFA500 100%);
351                border-radius: 50%;
352                border: none;
353                cursor: pointer;
354                z-index: 999998;
355                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
356                display: flex;
357                align-items: center;
358                justify-content: center;
359                font-size: 24px;
360                transition: transform 0.2s, box-shadow 0.2s;
361            }
362
363            .snap-download-btn:hover {
364                transform: scale(1.1);
365                box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
366            }
367
368            .snap-download-btn:active {
369                transform: scale(0.95);
370            }
371
372            .snap-download-menu {
373                position: fixed;
374                bottom: 145px;
375                right: 20px;
376                background: rgba(0, 0, 0, 0.95);
377                border-radius: 16px;
378                padding: 16px;
379                z-index: 999997;
380                min-width: 200px;
381                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
382                display: none;
383            }
384
385            .snap-download-menu.active {
386                display: block;
387                animation: slideUp 0.3s ease;
388            }
389
390            @keyframes slideUp {
391                from {
392                    opacity: 0;
393                    transform: translateY(10px);
394                }
395                to {
396                    opacity: 1;
397                    transform: translateY(0);
398                }
399            }
400
401            .snap-download-item {
402                color: #fff;
403                padding: 12px;
404                margin: 4px 0;
405                border-radius: 8px;
406                cursor: pointer;
407                transition: background 0.2s;
408                font-size: 14px;
409                text-align: center;
410            }
411
412            .snap-download-item:hover {
413                background: rgba(255, 255, 255, 0.1);
414            }
415        `;
416
417        TM_addStyle(downloadButtonStyle);
418
419        // Create download button
420        const downloadBtn = document.createElement('button');
421        downloadBtn.className = 'snap-download-btn';
422        downloadBtn.innerHTML = '⬇️';
423        downloadBtn.title = 'Download Snaps';
424
425        // Create download menu
426        const downloadMenu = document.createElement('div');
427        downloadMenu.className = 'snap-download-menu';
428        downloadMenu.innerHTML = `
429            <div class="snap-download-item" data-action="download-current">Download Current Snap</div>
430            <div class="snap-download-item" data-action="download-all">Download All Visible Snaps</div>
431            <div class="snap-download-item" data-action="auto-save">Auto-Save: <span id="auto-save-status">OFF</span></div>
432        `;
433
434        document.body.appendChild(downloadBtn);
435        document.body.appendChild(downloadMenu);
436
437        // Toggle menu
438        downloadBtn.addEventListener('click', function() {
439            downloadMenu.classList.toggle('active');
440        });
441
442        // Download functionality
443        async function downloadSnap(imageUrl, filename) {
444            try {
445                const response = await GM.xmlhttpRequest({
446                    method: 'GET',
447                    url: imageUrl,
448                    responseType: 'blob'
449                });
450
451                const blob = response.response;
452                const url = URL.createObjectURL(blob);
453                const a = document.createElement('a');
454                a.href = url;
455                a.download = filename || `snap_${Date.now()}.jpg`;
456                document.body.appendChild(a);
457                a.click();
458                document.body.removeChild(a);
459                URL.revokeObjectURL(url);
460
461                console.log('Snapchat Mobile View: Downloaded snap:', filename);
462                showNotification('✅ Snap downloaded successfully!');
463            } catch (error) {
464                console.error('Snapchat Mobile View: Download failed:', error);
465                showNotification('❌ Download failed. Try again.');
466            }
467        }
468
469        // Handle menu actions
470        downloadMenu.addEventListener('click', async function(e) {
471            const action = e.target.dataset.action;
472            
473            if (action === 'download-current') {
474                const currentSnap = document.querySelector('img._HpJ5, video, img[src*="snap"]');
475                if (currentSnap) {
476                    const imageUrl = currentSnap.src || currentSnap.currentSrc;
477                    await downloadSnap(imageUrl, `snap_${Date.now()}.jpg`);
478                } else {
479                    showNotification('⚠️ No snap found to download');
480                }
481                downloadMenu.classList.remove('active');
482            } else if (action === 'download-all') {
483                const allSnaps = document.querySelectorAll('img._HpJ5, img[src*="snap"], video');
484                if (allSnaps.length > 0) {
485                    showNotification(`📥 Downloading ${allSnaps.length} snaps...`);
486                    for (let i = 0; i < allSnaps.length; i++) {
487                        const imageUrl = allSnaps[i].src || allSnaps[i].currentSrc;
488                        await downloadSnap(imageUrl, `snap_${Date.now()}_${i}.jpg`);
489                        await new Promise(resolve => setTimeout(resolve, 500));
490                    }
491                    showNotification('✅ All snaps downloaded!');
492                } else {
493                    showNotification('⚠️ No snaps found to download');
494                }
495                downloadMenu.classList.remove('active');
496            } else if (action === 'auto-save') {
497                const currentStatus = await GM.getValue('auto_save_snaps', false);
498                const newStatus = !currentStatus;
499                await GM.setValue('auto_save_snaps', newStatus);
500                document.getElementById('auto-save-status').textContent = newStatus ? 'ON' : 'OFF';
501                showNotification(newStatus ? '✅ Auto-save enabled' : '⚠️ Auto-save disabled');
502                
503                if (newStatus) {
504                    startAutoSave();
505                }
506            }
507        });
508
509        // Auto-save functionality
510        async function startAutoSave() {
511            const autoSaveEnabled = await GM.getValue('auto_save_snaps', false);
512            if (!autoSaveEnabled) return;
513
514            const observer = new MutationObserver(async function() {
515                const newSnaps = document.querySelectorAll('img._HpJ5:not([data-saved]), img[src*="snap"]:not([data-saved])');
516                for (const snap of newSnaps) {
517                    snap.setAttribute('data-saved', 'true');
518                    const imageUrl = snap.src || snap.currentSrc;
519                    if (imageUrl && !imageUrl.includes('data:')) {
520                        await downloadSnap(imageUrl, `auto_snap_${Date.now()}.jpg`);
521                    }
522                }
523            });
524
525            observer.observe(document.body, {
526                childList: true,
527                subtree: true
528            });
529        }
530
531        // Initialize auto-save if enabled
532        GM.getValue('auto_save_snaps', false).then(enabled => {
533            if (enabled) {
534                document.getElementById('auto-save-status').textContent = 'ON';
535                startAutoSave();
536            }
537        });
538
539        console.log('Snapchat Mobile View: Snap download feature added');
540    }
541
542    // Auto Snap Score Booster
543    function addAutoScoreBooster() {
544        console.log('Snapchat Mobile View: Adding auto score booster...');
545
546        // Create score booster button style
547        const boosterButtonStyle = `
548            .score-booster-btn {
549                position: fixed;
550                bottom: 150px;
551                right: 20px;
552                width: 56px;
553                height: 56px;
554                background: linear-gradient(135deg, #FF0080 0%, #7928CA 100%);
555                border-radius: 50%;
556                border: none;
557                cursor: pointer;
558                z-index: 999998;
559                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
560                display: flex;
561                align-items: center;
562                justify-content: center;
563                font-size: 24px;
564                transition: transform 0.2s, box-shadow 0.2s;
565            }
566
567            .score-booster-btn:hover {
568                transform: scale(1.1);
569                box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
570            }
571
572            .score-booster-btn:active {
573                transform: scale(0.95);
574            }
575
576            .score-booster-menu {
577                position: fixed;
578                bottom: 215px;
579                right: 20px;
580                background: rgba(0, 0, 0, 0.95);
581                border-radius: 16px;
582                padding: 16px;
583                z-index: 999997;
584                min-width: 220px;
585                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
586                display: none;
587            }
588
589            .score-booster-menu.active {
590                display: block;
591                animation: slideUp 0.3s ease;
592            }
593
594            .score-booster-item {
595                color: #fff;
596                padding: 12px;
597                margin: 4px 0;
598                border-radius: 8px;
599                cursor: pointer;
600                transition: background 0.2s;
601                font-size: 14px;
602                text-align: center;
603            }
604
605            .score-booster-item:hover {
606                background: rgba(255, 255, 255, 0.1);
607            }
608
609            .score-status {
610                color: #FFFC00;
611                font-size: 12px;
612                text-align: center;
613                margin-top: 8px;
614                padding: 8px;
615                background: rgba(255, 252, 0, 0.1);
616                border-radius: 8px;
617            }
618        `;
619
620        TM_addStyle(boosterButtonStyle);
621
622        // Create booster button
623        const boosterBtn = document.createElement('button');
624        boosterBtn.className = 'score-booster-btn';
625        boosterBtn.innerHTML = '🚀';
626        boosterBtn.title = 'Auto Score Booster';
627
628        // Create booster menu
629        const boosterMenu = document.createElement('div');
630        boosterMenu.className = 'score-booster-menu';
631        boosterMenu.innerHTML = `
632            <div class="score-booster-item" data-action="start-boost">Start Auto Boost</div>
633            <div class="score-booster-item" data-action="stop-boost">Stop Auto Boost</div>
634            <div class="score-booster-item" data-action="send-streak">Send Streak Snaps</div>
635            <div class="score-status" id="boost-status">Status: Inactive</div>
636        `;
637
638        document.body.appendChild(boosterBtn);
639        document.body.appendChild(boosterMenu);
640
641        // Toggle menu
642        boosterBtn.addEventListener('click', function() {
643            boosterMenu.classList.toggle('active');
644        });
645
646        let boostInterval = null;
647
648        // Auto boost functionality
649        async function startAutoBoost() {
650            const boostStatus = document.getElementById('boost-status');
651            boostStatus.textContent = 'Status: Active 🟢';
652            showNotification('🚀 Auto score booster started!');
653
654            await GM.setValue('auto_boost_active', true);
655
656            boostInterval = setInterval(async function() {
657                try {
658                    // Find camera button
659                    const cameraBtn = document.querySelector('button.qJKfS, button[title*="Camera"]');
660                    if (cameraBtn) {
661                        cameraBtn.click();
662                        console.log('Snapchat Mobile View: Camera clicked');
663
664                        // Wait for camera to load
665                        await new Promise(resolve => setTimeout(resolve, 2000));
666
667                        // Find send button
668                        const sendBtn = document.querySelector('button[title*="Send"], button.send-btn, button[aria-label*="Send"]');
669                        if (sendBtn) {
670                            sendBtn.click();
671                            console.log('Snapchat Mobile View: Snap sent');
672
673                            // Wait before next snap
674                            await new Promise(resolve => setTimeout(resolve, 5000));
675                        }
676                    }
677                } catch (error) {
678                    console.error('Snapchat Mobile View: Auto boost error:', error);
679                }
680            }, 30000); // Send snap every 30 seconds
681
682            console.log('Snapchat Mobile View: Auto boost started');
683        }
684
685        function stopAutoBoost() {
686            if (boostInterval) {
687                clearInterval(boostInterval);
688                boostInterval = null;
689            }
690
691            const boostStatus = document.getElementById('boost-status');
692            boostStatus.textContent = 'Status: Inactive 🔴';
693            showNotification('⚠️ Auto score booster stopped');
694
695            GM.setValue('auto_boost_active', false);
696            console.log('Snapchat Mobile View: Auto boost stopped');
697        }
698
699        async function sendStreakSnaps() {
700            showNotification('📸 Sending streak snaps...');
701
702            // Find all friends with streak
703            const streakFriends = document.querySelectorAll('[title*="streak"], [aria-label*="streak"]');
704            
705            for (let i = 0; i < streakFriends.length; i++) {
706                try {
707                    streakFriends[i].click();
708                    await new Promise(resolve => setTimeout(resolve, 1000));
709
710                    const cameraBtn = document.querySelector('button.qJKfS, button[title*="Camera"]');
711                    if (cameraBtn) {
712                        cameraBtn.click();
713                        await new Promise(resolve => setTimeout(resolve, 2000));
714
715                        const sendBtn = document.querySelector('button[title*="Send"]');
716                        if (sendBtn) {
717                            sendBtn.click();
718                            await new Promise(resolve => setTimeout(resolve, 2000));
719                        }
720                    }
721                } catch (error) {
722                    console.error('Snapchat Mobile View: Streak snap error:', error);
723                }
724            }
725
726            showNotification('✅ Streak snaps sent!');
727        }
728
729        // Handle menu actions
730        boosterMenu.addEventListener('click', function(e) {
731            const action = e.target.dataset.action;
732            
733            if (action === 'start-boost') {
734                startAutoBoost();
735                boosterMenu.classList.remove('active');
736            } else if (action === 'stop-boost') {
737                stopAutoBoost();
738                boosterMenu.classList.remove('active');
739            } else if (action === 'send-streak') {
740                sendStreakSnaps();
741                boosterMenu.classList.remove('active');
742            }
743        });
744
745        // Check if auto boost was active
746        GM.getValue('auto_boost_active', false).then(active => {
747            if (active) {
748                startAutoBoost();
749            }
750        });
751
752        console.log('Snapchat Mobile View: Auto score booster added');
753    }
754
755    // Show notification helper
756    function showNotification(message) {
757        const notification = document.createElement('div');
758        notification.textContent = message;
759        notification.style.cssText = `
760            position: fixed;
761            top: 20px;
762            left: 50%;
763            transform: translateX(-50%);
764            background: rgba(0, 0, 0, 0.9);
765            color: #fff;
766            padding: 12px 24px;
767            border-radius: 24px;
768            font-size: 14px;
769            z-index: 9999999;
770            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
771            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
772            animation: slideDown 0.3s ease;
773        `;
774
775        const style = document.createElement('style');
776        style.textContent = `
777            @keyframes slideDown {
778                from {
779                    opacity: 0;
780                    transform: translateX(-50%) translateY(-20px);
781                }
782                to {
783                    opacity: 1;
784                    transform: translateX(-50%) translateY(0);
785                }
786            }
787        `;
788        document.head.appendChild(style);
789
790        document.body.appendChild(notification);
791
792        setTimeout(() => {
793            notification.style.transition = 'opacity 0.3s';
794            notification.style.opacity = '0';
795            setTimeout(() => {
796                notification.remove();
797                style.remove();
798            }, 300);
799        }, 3000);
800    }
801
802    // Add mobile indicator
803    function addMobileIndicator() {
804        const indicator = document.createElement('div');
805        indicator.id = 'mobile-view-indicator';
806        indicator.innerHTML = '📱 Mobile View Active';
807        indicator.style.cssText = `
808            position: fixed;
809            bottom: 10px;
810            right: 10px;
811            background: rgba(0, 0, 0, 0.8);
812            color: #fff;
813            padding: 8px 12px;
814            border-radius: 20px;
815            font-size: 12px;
816            z-index: 999999;
817            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
818            pointer-events: none;
819            opacity: 0.7;
820        `;
821
822        // Auto-hide after 3 seconds
823        setTimeout(() => {
824            if (indicator.parentNode) {
825                indicator.style.transition = 'opacity 0.5s';
826                indicator.style.opacity = '0';
827                setTimeout(() => indicator.remove(), 500);
828            }
829        }, 3000);
830
831        document.body.appendChild(indicator);
832    }
833
834    // Force reload with mobile user agent
835    async function forceReloadAsMobile() {
836        const hasReloaded = await GM.getValue('snapchat_mobile_reloaded', false);
837        
838        if (!hasReloaded) {
839            console.log('Snapchat Mobile View: First load detected, reloading with mobile user agent...');
840            await GM.setValue('snapchat_mobile_reloaded', true);
841            
842            // Set a timeout to reset the flag
843            setTimeout(async () => {
844                await GM.setValue('snapchat_mobile_reloaded', false);
845            }, 5000);
846            
847            location.reload();
848            return true;
849        }
850        
851        return false;
852    }
853
854    // Main initialization function
855    async function init() {
856        console.log('Snapchat Mobile View Extension: Starting initialization...');
857
858        // Set mobile user agent first
859        setMobileUserAgent();
860        simulateMobileDevice();
861        interceptRequests();
862
863        // Check if we need to reload
864        const needsReload = await forceReloadAsMobile();
865        if (needsReload) {
866            return; // Stop here, page will reload
867        }
868
869        // Wait for page to be ready
870        if (document.readyState === 'loading') {
871            document.addEventListener('DOMContentLoaded', function() {
872                applyMobileLayout();
873                addMobileFeatures();
874                observePageChanges();
875                addMobileIndicator();
876                
877                // Add new features after a delay to ensure page is loaded
878                setTimeout(() => {
879                    addSnapDownloadFeature();
880                    addAutoScoreBooster();
881                }, 3000);
882            });
883        } else {
884            applyMobileLayout();
885            addMobileFeatures();
886            observePageChanges();
887            addMobileIndicator();
888            
889            // Add new features after a delay to ensure page is loaded
890            setTimeout(() => {
891                addSnapDownloadFeature();
892                addAutoScoreBooster();
893            }, 3000);
894        }
895
896        console.log('Snapchat Mobile View Extension: Initialization complete!');
897    }
898
899    // Start the extension
900    init();
901
902})();
Snapchat Mobile View for Desktop | Robomonkey