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

24 days 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