mobile: hide iOS keyboard accessory bar + composer-anchored attach menu (batch134)
- Accessory bar: call Keyboard.setAccessoryBarVisible({isVisible:false}) on iOS in the native
IIFE, hiding the grey chevrons+Done strip above the keyboard. The plugin is already bundled
in the current TestFlight build, so this takes effect on a web deploy — no rebuild.
- Attach: tapping the paperclip now opens a composer-anchored Photos/Camera/Document menu
(like the emoji/mention popups) instead of firing the generic mid-screen OS chooser as the
first thing. Each option opens a type-scoped picker (image/*, capture). Web fix, all clients.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@ On the **Register an App ID** page, only three fields matter — leave everythin
|
||||
1. [codemagic.io](https://codemagic.io) → sign in with the git provider → add this repository.
|
||||
2. **Teams → Integrations → App Store Connect → Connect**, upload the `.p8`, paste the **Issuer ID** and
|
||||
**Key ID**. **Name it exactly `BizGaze App Store Connect`** (the `codemagic.yaml` references that name).
|
||||
3. Codemagic detects `codemagic.yaml`. That's all the signing setup — automatic signing creates the
|
||||
3. Codemagic detects `codemagic.yaml`. That's all the signing setup — automatic signing creates the
|
||||
distribution certificate + provisioning profile from this key on the first build.
|
||||
|
||||
## Step 4 — Run the build
|
||||
|
||||
+21
-4
@@ -589,6 +589,12 @@
|
||||
/* (message action button styling is under .msg-actions) */
|
||||
.emoji-pop{position:absolute;bottom:64px;left:12px;width:330px;height:300px;background:#fff;border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 28px rgba(0,0,0,.18);z-index:50;display:flex;flex-direction:column;overflow:hidden;}
|
||||
.mention-pop{position:absolute;left:12px;right:12px;bottom:64px;max-height:240px;overflow:auto;background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 28px rgba(0,0,0,.18);z-index:60;padding:.3rem;}
|
||||
/* Attach menu: anchored to the composer (like the emoji/mention popups) so options appear right above the
|
||||
paperclip instead of the OS chooser jumping to mid-screen. */
|
||||
.attach-pop{position:absolute;left:12px;bottom:64px;z-index:60;background:var(--card);border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 28px rgba(0,0,0,.18);padding:.3rem;display:flex;flex-direction:column;min-width:190px;}
|
||||
.attach-pop .attach-opt{display:flex;align-items:center;gap:.6rem;padding:.65rem .7rem;border:none;background:transparent;color:var(--ink);cursor:pointer;border-radius:8px;font-size:.95rem;text-align:left;width:100%;}
|
||||
.attach-pop .attach-opt:hover{background:var(--blue-soft);color:var(--blue);}
|
||||
.attach-pop .attach-opt svg{flex:0 0 auto;}
|
||||
.mention-pop .mrow{display:flex;align-items:center;gap:.55rem;padding:.4rem .55rem;border-radius:8px;cursor:pointer;}
|
||||
.mention-pop .mrow.sel{background:var(--blue-soft);}
|
||||
.mention-pop .mn{font-weight:600;color:var(--ink);}
|
||||
@@ -1152,7 +1158,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<script src="/icons.js?v=6"></script>
|
||||
<script>window.__BUILD='2026-07-19-batch133';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
||||
<script>window.__BUILD='2026-07-19-batch134';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
|
||||
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
|
||||
//
|
||||
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
|
||||
@@ -1193,6 +1199,7 @@ window.__perfProbe=function(label){ try{
|
||||
function setKb(px){ px=Math.max(0,Math.round(px)); r.style.setProperty('--kb',px+'px'); document.body.classList.toggle('kb-open',px>4); var b=document.getElementById('msgs'); if(b && (b.scrollHeight-b.scrollTop-b.clientHeight)<400) b.scrollTop=b.scrollHeight; } /* #1: only keep pinned to newest when ALREADY near the bottom — replying to an OLD message must not yank to the latest */
|
||||
window.__bzResetKb=function(){ setKb(0); }; // called on composer blur as a safety net (#1: keyboard closed but lift stayed, leaving empty space after reply-cancel)
|
||||
var KB=C.Plugins&&C.Plugins.Keyboard;
|
||||
if(p==='ios' && KB && KB.setAccessoryBarVisible){ try{ KB.setAccessoryBarVisible({ isVisible:false }); }catch(_){} } // hide the grey iOS input accessory bar (up/down chevrons + Done) above the keyboard — iOS-only, no-op elsewhere
|
||||
if(KB && KB.addListener){
|
||||
KB.addListener('keyboardWillShow', function(info){ var raw=(info&&info.keyboardHeight)||0; var kb = raw>window.innerHeight ? raw/(window.devicePixelRatio||1) : raw; setKb(kb); });
|
||||
KB.addListener('keyboardWillHide', function(){ setKb(0); });
|
||||
@@ -1974,9 +1981,16 @@ function convoShellHTML(it){
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '<input type="file" id="fileInput" multiple style="display:none">'
|
||||
+ '<input type="file" id="fileInputImg" accept="image/*" multiple style="display:none">'
|
||||
+ '<input type="file" id="fileInputCam" accept="image/*" capture="environment" style="display:none">'
|
||||
+ '</form>'
|
||||
+ '<div class="emoji-pop" id="emojiPop" style="display:none"></div>'
|
||||
+ '<div class="mention-pop" id="mentionPop" style="display:none"></div>'
|
||||
+ '<div class="attach-pop" id="attachPop" style="display:none">'
|
||||
+ '<button type="button" class="attach-opt" data-src="photo">'+ic('image',18)+'<span>Photos</span></button>'
|
||||
+ '<button type="button" class="attach-opt" data-src="camera">'+ic('camera',18)+'<span>Camera</span></button>'
|
||||
+ '<button type="button" class="attach-opt" data-src="file">'+ic('paperclip',18)+'<span>Document</span></button>'
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
}
|
||||
// Small round avatar (photo if the member has one, else colored initials) for a group message sender.
|
||||
@@ -2205,7 +2219,7 @@ function renderAttachBar(){
|
||||
pendingAttachs.splice(i,1); renderAttachBar();
|
||||
});
|
||||
}
|
||||
function hideAttach(){ pendingAttachs=[]; const bar=document.getElementById('attachBar'); if(bar){ bar.style.display='none'; bar.innerHTML=''; } const fi=document.getElementById('fileInput'); if(fi) fi.value=''; }
|
||||
function hideAttach(){ pendingAttachs=[]; const bar=document.getElementById('attachBar'); if(bar){ bar.style.display='none'; bar.innerHTML=''; } ['fileInput','fileInputImg','fileInputCam'].forEach(id=>{ const fi=document.getElementById(id); if(fi) fi.value=''; }); const ap=document.getElementById('attachPop'); if(ap) ap.style.display='none'; }
|
||||
// Paste an image from the clipboard (e.g. a screenshot) straight into the composer.
|
||||
function onPaste(e){
|
||||
const cd=e.clipboardData; if(!cd) return;
|
||||
@@ -2228,6 +2242,7 @@ document.addEventListener('click',(e)=>{
|
||||
const pop=document.getElementById('emojiPop'), btn=document.getElementById('emojiBtn');
|
||||
if(pop && !pop.contains(e.target) && !(btn&&btn.contains(e.target)) && !e.target.closest('.react-btn')) closeEmoji();
|
||||
});
|
||||
document.addEventListener('click',(e)=>{ const ap=document.getElementById('attachPop'); if(!ap||ap.style.display==='none') return; const abtn=document.getElementById('attachBtn'); if(!ap.contains(e.target) && !(abtn&&abtn.contains(e.target))) ap.style.display='none'; }); // close the attach menu on an outside tap
|
||||
let emojiOpen=false;
|
||||
function openEmoji(mode, anchorEl){ emojiMode=mode||'compose'; if(emojiMode&&emojiMode.react&&emojiCat==='gif') emojiCat=0; /* GIFs can't be a reaction */ const pop=document.getElementById('emojiPop'); if(!pop) return; emojiOpen=true; renderEmojiPop(pop); pop.style.display='flex'; positionEmojiPop(pop, anchorEl); }
|
||||
// Anchor the picker at its trigger: above the composer emoji icon, or at the message you reacted to.
|
||||
@@ -2881,8 +2896,10 @@ async function openConvo(kind,id){
|
||||
const back=document.getElementById('convoBack'); if(back) back.onclick=showWelcome;
|
||||
const form=document.getElementById('composer'); if(form) form.addEventListener('submit',(e)=>{ e.preventDefault(); sendMessage(); });
|
||||
clearReply(); cancelEdit(); hideAttach();
|
||||
const ab2=document.getElementById('attachBtn'); if(ab2) ab2.onclick=()=>{ const fi=document.getElementById('fileInput'); if(fi) fi.click(); };
|
||||
const fi=document.getElementById('fileInput'); if(fi) fi.onchange=()=>{ Array.from(fi.files||[]).forEach(uploadFile); fi.value=''; }; // #5: queue every selected file
|
||||
const ab2=document.getElementById('attachBtn'); const apop=document.getElementById('attachPop');
|
||||
if(ab2&&apop) ab2.onclick=(e)=>{ e.stopPropagation(); apop.style.display = apop.style.display==='none' ? 'flex' : 'none'; }; // show a composer-anchored menu instead of firing the generic mid-screen OS chooser straight away
|
||||
if(apop) apop.querySelectorAll('.attach-opt').forEach(b=>b.onclick=(e)=>{ e.stopPropagation(); apop.style.display='none'; const src=b.dataset.src; const inp = src==='photo'?document.getElementById('fileInputImg') : src==='camera'?document.getElementById('fileInputCam') : document.getElementById('fileInput'); if(inp) inp.click(); }); // Photos → gallery, Camera → capture, Document → any file
|
||||
['fileInput','fileInputImg','fileInputCam'].forEach(fid=>{ const fi=document.getElementById(fid); if(fi) fi.onchange=()=>{ Array.from(fi.files||[]).forEach(uploadFile); fi.value=''; }; }); // #5: queue every selected file from whichever scoped input fired
|
||||
const eb=document.getElementById('emojiBtn'); if(eb) eb.onclick=(e)=>{ e.stopPropagation(); emojiOpen?closeEmoji():openEmoji('compose', eb); };
|
||||
const fb=document.getElementById('fmtBtn'); if(fb) fb.onclick=()=>{ const bar=document.getElementById('fmtBar'); if(bar) bar.style.display=bar.style.display==='none'?'flex':'none'; };
|
||||
const fbar=document.getElementById('fmtBar'); if(fbar) fbar.querySelectorAll('button[data-fmt]').forEach(b=>b.onclick=()=>applyFmt(b.dataset.fmt));
|
||||
|
||||
Reference in New Issue
Block a user